Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2cb19fde | ||
|
|
c6c411a6db | ||
|
|
01ba910685 | ||
|
|
43f2fd4da3 | ||
|
|
08b86183c2 | ||
|
|
1e036e981e | ||
|
|
0b16daf14a | ||
|
|
5bb487795c | ||
|
|
3a3904fea6 | ||
|
|
52d77f6834 | ||
|
|
e4aa3c6d99 | ||
|
|
e2f5729e8f | ||
|
|
c1b0b35352 | ||
|
|
6c04eb24f4 | ||
|
|
acaaaa67e0 | ||
|
|
9d4582a47f | ||
|
|
6a1308c251 | ||
|
|
31daf7f764 | ||
|
|
c51ae06b4d | ||
|
|
fd13a26677 | ||
|
|
cf44b1ed04 | ||
|
|
d259242849 | ||
|
|
da4d016ee1 | ||
|
|
c0611814aa | ||
|
|
bcdb30cc55 | ||
|
|
e251c4bd62 | ||
|
|
e9ce9a539e | ||
|
|
d77c6425bc | ||
|
|
3481253c54 | ||
|
|
5783940e4c | ||
|
|
fb3a8214df | ||
|
|
e4536afda6 | ||
|
|
c236d3099d |
@@ -0,0 +1,150 @@
|
||||
name: governance
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
governance:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOPROXY: https://goproxy.cn,direct
|
||||
steps:
|
||||
- name: Checkout
|
||||
env:
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SUBMODULE_TOKEN: ${{ secrets.SUBMODULE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# 只物化两个治理 gitlink(Guidelines 与 deps/development-manager),非递归;
|
||||
# 其它 gitlink 不物化、不取回。凭据只以 URL 限定的 extraheader 存在于本进程树内。
|
||||
python3 - <<'PY_CHECKOUT'
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
# 服务端基址取运行器注入的 GITHUB_SERVER_URL(平台控制、候选 PR 不可影响)。
|
||||
SERVER = os.environ.get("GITHUB_SERVER_URL", "").rstrip("/")
|
||||
parsed_server = urlsplit(SERVER)
|
||||
if parsed_server.scheme not in {"http", "https"} or not parsed_server.netloc:
|
||||
sys.exit("Checkout server must be the platform-injected http(s) URL")
|
||||
SSH = "ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/"
|
||||
# 审查过的白名单,独立于候选 .gitmodules:仅列出两个治理 gitlink。
|
||||
REPOSITORIES = {
|
||||
"deps/development-guidelines": "Development-Guidelines",
|
||||
"deps/development-manager": "Development-Manager",
|
||||
}
|
||||
TARGET = "weichen/Agent-System-Documents"
|
||||
root_token = os.environ.pop("TOKEN", "")
|
||||
component_token = os.environ.pop("SUBMODULE_TOKEN", "")
|
||||
if not root_token or not component_token:
|
||||
sys.exit("Checkout requires TOKEN and SUBMODULE_TOKEN")
|
||||
if os.environ.get("GITHUB_REPOSITORY") != TARGET:
|
||||
sys.exit("Checkout repository is outside the reviewed allowlist")
|
||||
sha = os.environ.get("GITHUB_SHA", "")
|
||||
if not re.fullmatch(r"[0-9a-fA-F]{40}", sha):
|
||||
sys.exit("Checkout requires a fixed commit SHA")
|
||||
|
||||
# 丢弃继承的 Git 追踪/配置/SSH 钩子;凭据只存在于单个 Git 进程树的精确 URL 头中。
|
||||
clean_env = {k: v for k, v in os.environ.items() if not k.startswith("GIT_")}
|
||||
clean_env.update(GIT_CONFIG_NOSYSTEM="1", GIT_CONFIG_SYSTEM="/dev/null",
|
||||
GIT_CONFIG_GLOBAL="/dev/null", GIT_TERMINAL_PROMPT="0")
|
||||
config = {
|
||||
"core.hooksPath": "/dev/null", "core.fsmonitor": "false",
|
||||
"init.templateDir": "", "credential.helper": "",
|
||||
"fetch.recurseSubmodules": "false", "submodule.recurse": "false",
|
||||
"http.followRedirects": "false", "protocol.allow": "never",
|
||||
"protocol." + urlsplit(SERVER).scheme + ".allow": "always",
|
||||
}
|
||||
|
||||
|
||||
def git(repo, *args, extra=None):
|
||||
values = config | (extra or {})
|
||||
env = clean_env | {"GIT_CONFIG_COUNT": str(len(values))}
|
||||
for index, (key, value) in enumerate(values.items()):
|
||||
env[f"GIT_CONFIG_KEY_{index}"] = key
|
||||
env[f"GIT_CONFIG_VALUE_{index}"] = value
|
||||
return subprocess.run(["git", "-C", str(repo), *args], env=env,
|
||||
check=True, stdout=subprocess.PIPE).stdout
|
||||
|
||||
|
||||
def materialize_governance(root):
|
||||
# -r 列出任意深度的 gitlink,但不进入 submodule 内部;其它 gitlink 只记录、从不取回。
|
||||
links = {}
|
||||
for entry in git(root, "ls-tree", "-rz", "HEAD").split(b"\0"):
|
||||
if not entry:
|
||||
continue
|
||||
metadata, path = entry.split(b"\t", 1)
|
||||
mode, kind, commit = metadata.decode().split()
|
||||
if mode == "160000":
|
||||
links[path.decode()] = commit
|
||||
for name in REPOSITORIES:
|
||||
if name not in links:
|
||||
raise ValueError("Governance gitlink is missing from the fixed commit")
|
||||
raw = git(root, "config", "--no-includes", "--blob", "HEAD:.gitmodules", "--null", "--list")
|
||||
entries = {}
|
||||
for item in raw.split(b"\0"):
|
||||
if not item:
|
||||
continue
|
||||
key, value = item.decode().split("\n", 1)
|
||||
match = re.fullmatch(r"submodule\.(.+)\.(path|url)", key)
|
||||
if not match:
|
||||
continue
|
||||
name, field = match.groups()
|
||||
if name not in REPOSITORIES:
|
||||
continue
|
||||
if field in entries.setdefault(name, {}):
|
||||
raise ValueError("Duplicate candidate .gitmodules setting")
|
||||
entries[name][field] = value
|
||||
# 先整体校验两个治理条目,再进行任何带凭据的取回。
|
||||
for name, repository in REPOSITORIES.items():
|
||||
fields = entries.get(name, {})
|
||||
if fields.get("path") != name:
|
||||
raise ValueError("Governance submodule path is outside the reviewed allowlist")
|
||||
trusted_url = f"{SERVER}/weichen/{repository}.git"
|
||||
if fields.get("url") not in {SSH + repository + ".git", trusted_url}:
|
||||
raise ValueError("Governance submodule URL is outside the reviewed allowlist")
|
||||
for name, repository in REPOSITORIES.items():
|
||||
url = f"{SERVER}/weichen/{repository}.git"
|
||||
# 仅持久化审查过的公开 URL,使 Git 在进程级配置消失后仍识别该 submodule。
|
||||
git(root, "config", f"submodule.{name}.url", url)
|
||||
git(root, "submodule", "update", "--init", "--checkout", "--", name,
|
||||
extra={f"submodule.{name}.url": url,
|
||||
f"submodule.{name}.update": "checkout",
|
||||
f"http.{url}.extraheader": "AUTHORIZATION: token " + component_token})
|
||||
child = root / name
|
||||
if git(child, "rev-parse", "HEAD").decode().strip() != links[name]:
|
||||
raise ValueError("Materialized governance gitlink does not match the fixed SHA")
|
||||
|
||||
|
||||
try:
|
||||
root = Path.cwd()
|
||||
# 必须是干净的临时工作区;不复用既有 Git 状态。
|
||||
if (root / ".git").exists():
|
||||
raise ValueError("Checkout requires a fresh workspace")
|
||||
git(root, "init", "-q")
|
||||
url = f"{SERVER}/{TARGET}.git"
|
||||
git(root, "remote", "add", "origin", url)
|
||||
git(root, "fetch", "--depth=1", "origin", sha,
|
||||
extra={f"http.{url}.extraheader": "AUTHORIZATION: token " + root_token})
|
||||
git(root, "checkout", "-q", "--detach", "FETCH_HEAD")
|
||||
materialize_governance(root)
|
||||
except (ValueError, OSError, subprocess.CalledProcessError) as error:
|
||||
sys.exit(f"Checkout refused: {error}")
|
||||
print("Checkout complete; credentials were not persisted in Git configuration")
|
||||
PY_CHECKOUT
|
||||
unset TOKEN SUBMODULE_TOKEN
|
||||
- name: Install Go 1.27.1
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -fsSL -o "$RUNNER_TEMP/go.tgz" https://mirrors.aliyun.com/golang/go1.27.1.linux-amd64.tar.gz
|
||||
tar -C /usr/local -xzf "$RUNNER_TEMP/go.tgz"
|
||||
ln -sfn /usr/local/go/bin/go /usr/local/bin/go
|
||||
ln -sfn /usr/local/go/bin/gofmt /usr/local/bin/gofmt
|
||||
- name: Validate agent execution configuration
|
||||
run: scripts/agent-exec validate --repo .
|
||||
- name: Governance check
|
||||
run: scripts/dm governance check --repo .
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "deps/development-guidelines"]
|
||||
path = deps/development-guidelines
|
||||
url = ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Development-Guidelines.git
|
||||
[submodule "deps/development-manager"]
|
||||
path = deps/development-manager
|
||||
url = ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Development-Manager.git
|
||||
@@ -0,0 +1,57 @@
|
||||
# Agent-System-Documents 采纳 development-governance-v2 治理套件 任务卡
|
||||
|
||||
任务主记录:[Agent-System-Documents PR #110](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/110)(本文件随 PR 描述维护)。
|
||||
状态:已完成(闭合随本 PR 合入;合入以平台记录为准);owner:weichen。
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:weichen/Agent-System-Documents;root_target:main;immediate_target:main
|
||||
- branch_kind:initiative;initiative_id:governance-adoption-Agent-System-Documents
|
||||
- R0(initial_main_sha):da4d016ee1dee6a418411ed0f8b45911fe9aab02;工作区:/home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption(worktree);分支:feature/governance-adoption
|
||||
- 父任务信息(仅子任务填写):无
|
||||
- accepting_requirements:false(单仓采纳 initiative,范围固定为下列十步,不接收新需求)
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;候选=无;理由=development-governance-v2 Phase 6 每个正式仓库独立 initiative;triage 收据=无(本仓采纳前尚无 triage 入口)
|
||||
- 授权与交付终点:owner 已授权 development-governance-v2 全部实施(Agent-System-Dev 主记录 docs/superpowers/plans/2026-10-16-development-governance-v2-master.md);终点为本 PR 合入 main 且采纳状态 active。分支保护与 tag 由用户执行。
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md;外部 Agent 一单元一新进程/会话/工作区)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标(Phase 6 十步):固定 Guidelines SHA(deps/development-guidelines @ 2688fd2be6a52c0bf110e7e6366b6f291ab5e4eb);增加 AGENT-EXECUTION.md;增加共享执行器薄入口(deps/development-manager @ bd3a7de57a3a9e1a4495b94c5fa5c08d71e7be8c + scripts/dm、scripts/agent-exec、scripts/task);四条路由(implementation/repair/test/review);任务卡模板与分支元数据;CI 中启用 schema 校验(.gitea/workflows/governance.yml:agent-exec validate + governance check);一次正例运行;负例(缺配置、坏收据、越界路径、未知副作用);独立 Review + CI;采纳状态 active。
|
||||
|
||||
非目标:不改业务代码与既有测试;不改分支保护(由用户执行);不发布 tag。
|
||||
|
||||
引导说明:AGENT-EXECUTION.md 由 host 以固定版本治理套件(dm-server governance init @ bd3a7de57a3a9e1a4495b94c5fa5c08d71e7be8c)生成并作为引导配置提交(执行器运行前必须存在有效配置;沿用 DM 采纳先例);其余文件由实施单元生成。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
| 单元 ID | 预期范围 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| governance-adoption-Agent-System-Documents-r1 | 13 个允许路径(治理文件 + 两个 gitlink) | | implementation | cursor→antigravity→pi | run 3a6bdcf3(dm-server pinned,attempts=1) | cursor / not-observable | failed-unknown | 无(apply 快进) | 否 | /tmp/gov-receipt-Agent-System-Documents-r1.json |
|
||||
| 对账(r1) | 规范 worktree 仍为 d259242 且干净;无 transport ref。worker 自报外部副作用:并行 worker 共用固定文件 /tmp/gov-scratch-path.txt 记录 scratch 路径,该文件被覆盖后误删非本单元创建的仓外临时目录 /tmp/tmp.2FlZqPmOJM(不可恢复;各单元私有工作区不在该路径,已完成单元收据均为 success) | — | — | — | — | — | failed-unknown 对账完成;单元包追加禁止固定共享 /tmp 路径约束后全新运行 r2 | — | — | — |
|
||||
| governance-adoption-Agent-System-Documents-r2 | 13 个允许路径(治理文件 + 两个 gitlink) | cf44b1ed | implementation | cursor→antigravity→pi | run 8348cbe4(dm-server pinned,attempts=1) | cursor / not-observable | success | fd13a266777b(apply 快进) | 否 | /tmp/gov-receipt-Agent-System-Documents-r2.json |
|
||||
| governance-adoption-Agent-System-Documents-repair-r1 | PROJECT.md、policy/guidelines-adoption.md | repair | cursor→antigravity→pi | run cddbee35(attempts=1) | cursor / not-observable | success | 31daf7f76493(apply 快进) | 否 | /tmp/gov-receipt-Agent-System-Documents-repair-r1.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at | 结果 |
|
||||
|---|---|---|---|---|
|
||||
| 启动 | da4d016ee1dee6a418411ed0f8b45911fe9aab02 | da4d016ee1dee6a418411ed0f8b45911fe9aab02 | 2026-10-17T07:16:27Z | 一致 |
|
||||
| 请求审核 | da4d016ee1dee6a418411ed0f8b45911fe9aab02 | 6a1308c2510e | 2026-10-17T08:47:50Z | 已含 origin/main |
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:治理套件已采纳——Guidelines 与 Development-Manager gitlink 固定、AGENT-EXECUTION.md 四条路由、共享执行器薄入口、pre-commit admit、governance CI;采纳状态 active 随本 PR 合入生效。
|
||||
- 验证:见下表(命令输出由 /tmp/gov-neg/ev.sh 写入 .task/evidence/governance-adoption/,SELF 除非另注)。
|
||||
- 复现:正负例脚本冻结副本 Agent-System-Dev `docs/superpowers/plans/evidence/governance-adopt-cases.py`(sha256 3aa1bf6ea1cd48f3f59c0a307620e4112389a93fe21a113006f54e62eb8f0d06),用法 `python3 governance-adopt-cases.py <仓库根>`;它经本仓 `scripts/agent-exec` 运行假 CLI,不访问网络。
|
||||
- 影响与剩余项:不改业务代码;本地启用钩子需由人执行 `git config core.hooksPath scripts/hooks`;将 governance 检查设为分支保护必需项属用户双钥匙事项(命令见 Agent-System-Dev 治理主记录)。
|
||||
|
||||
| 时间(UTC) | 命令 | 被测 HEAD | exit | 来源 | 摘要/位置 |
|
||||
|---|---|---|---|---|---|
|
||||
| 20261017T074046Z | `scripts/dm governance check --repo .` | fd13a266777b | 0 | SELF | ok fixtures;[输出](../evidence/governance-adoption/00-20261017T074046Z.log) sha256:0ff0ec3150c5162e |
|
||||
| 20261017T074046Z | `scripts/agent-exec validate --repo .` | fd13a266777b | 0 | SELF | validate: ok;[输出](../evidence/governance-adoption/01-20261017T074046Z.log) sha256:7ba43b8b1d0ee152 |
|
||||
| 20261017T074046Z | `python3 /tmp/gov-neg/adopt_cases.py .` | fd13a266777b | 0 | SELF | [verdict] ALL PASS cases=5;[输出](../evidence/governance-adoption/02-20261017T074046Z.log) sha256:b63c5ee476cd94f5 |
|
||||
| 20261017T084129Z | `scripts/dm governance check --repo .` | 31daf7f76493 | 0 | SELF | ok fixtures;[输出](../evidence/governance-adoption/03-20261017T084129Z.log) sha256:809536ab8a8d1caa |
|
||||
| 20261017T084129Z | `scripts/agent-exec validate --repo .` | 31daf7f76493 | 0 | SELF | validate: ok;[输出](../evidence/governance-adoption/04-20261017T084129Z.log) sha256:3d3cf541ce4c6560 |
|
||||
| 2026-10-17T08:47:50Z | CI `governance / governance (pull_request)` | 6a1308c2510e | 0 | GATE | success;[运行](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/actions/runs/3593/jobs/5686) |
|
||||
| — | 候选 HEAD CI | 9d4582a47f4f | 0 | GATE | success governance / governance (pull_request)=success(/weichen/Agent-System-Documents/actions/runs/3611/jobs/5709) |
|
||||
| — | 独立 Review | 9d4582a47f4f | 0 | REPLAY | cursor-agent-stable --mode ask 全新只读会话(fresh_session=true,session id not-observable)闭合重审 VERDICT: APPROVE(对象 9d4582a,与 HEAD 仅 .task/ 不同) |
|
||||
@@ -0,0 +1,19 @@
|
||||
command: scripts/dm governance check --repo .
|
||||
cwd: /home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption
|
||||
head: fd13a266777b4c794acb879190c49ee980db38af
|
||||
exit: 0
|
||||
utc: 20261017T074046Z
|
||||
---
|
||||
ok gitlink deps/development-guidelines
|
||||
ok gitlink deps/development-manager
|
||||
ok agent-execution
|
||||
ok file AGENTS.md
|
||||
ok file PROJECT.md
|
||||
ok file policy/guidelines-adoption.md
|
||||
ok file .task/templates/task-card.md
|
||||
ok executable scripts/agent-exec
|
||||
ok executable scripts/hooks/pre-commit
|
||||
ok executable scripts/dm
|
||||
ok executable scripts/task
|
||||
ok adoption status
|
||||
ok fixtures
|
||||
@@ -0,0 +1,7 @@
|
||||
command: scripts/agent-exec validate --repo .
|
||||
cwd: /home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption
|
||||
head: fd13a266777b4c794acb879190c49ee980db38af
|
||||
exit: 0
|
||||
utc: 20261017T074046Z
|
||||
---
|
||||
validate: ok
|
||||
@@ -0,0 +1,13 @@
|
||||
command: python3 /tmp/gov-neg/adopt_cases.py .
|
||||
cwd: /home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption
|
||||
head: fd13a266777b4c794acb879190c49ee980db38af
|
||||
exit: 0
|
||||
utc: 20261017T074046Z
|
||||
---
|
||||
[object] Agent-System-Documents-wt-feature-governance-adoption HEAD=fd13a266777b4c794acb879190c49ee980db38af
|
||||
PASS positive-run-apply: run exit=0 terminal=success apply exit=0 head=876e934a0187
|
||||
PASS missing-config: run exit=2 terminal=invalid-config validate exit=2
|
||||
PASS bad-receipt: apply exit=4 head_unchanged=True msg=apply: no local integrity record: changed paths do not match the transport diff
|
||||
PASS out-of-scope-path: run exit=5 terminal=scope-expansion transport=False
|
||||
PASS unknown-side-effect: run exit=4 terminal=failed-unknown transport=False
|
||||
[verdict] ALL PASS cases=5
|
||||
@@ -0,0 +1,19 @@
|
||||
command: scripts/dm governance check --repo .
|
||||
cwd: /home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption
|
||||
head: 31daf7f7649334ddcc385ce4ba46aaca88f2f3b5
|
||||
exit: 0
|
||||
utc: 20261017T084129Z
|
||||
---
|
||||
ok gitlink deps/development-guidelines
|
||||
ok gitlink deps/development-manager
|
||||
ok agent-execution
|
||||
ok file AGENTS.md
|
||||
ok file PROJECT.md
|
||||
ok file policy/guidelines-adoption.md
|
||||
ok file .task/templates/task-card.md
|
||||
ok executable scripts/agent-exec
|
||||
ok executable scripts/hooks/pre-commit
|
||||
ok executable scripts/dm
|
||||
ok executable scripts/task
|
||||
ok adoption status
|
||||
ok fixtures
|
||||
@@ -0,0 +1,7 @@
|
||||
command: scripts/agent-exec validate --repo .
|
||||
cwd: /home/weichen/projects/Agent-System-Documents-wt-feature-governance-adoption
|
||||
head: 31daf7f7649334ddcc385ce4ba46aaca88f2f3b5
|
||||
exit: 0
|
||||
utc: 20261017T084129Z
|
||||
---
|
||||
validate: ok
|
||||
@@ -0,0 +1,31 @@
|
||||
# feature/iam-u3-account-contract 任务卡
|
||||
|
||||
任务主记录:本文件随 PR 描述维护。
|
||||
状态:已完成(合入前闭合);owner:weichen。PR:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/115
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Documents.git;root_target:main;immediate_target:main
|
||||
- branch_kind:standalone;accepting_requirements:false;跨仓总任务:Agent-System-Dev `feature/iam-u3-account-directory-recovery`([Dev PR #626](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/626) 已合入 `7716b28`)
|
||||
- R0(initial_main_sha):1e036e9;工作区:/home/weichen/projects/Agent-System-Documents-wt-iam-u3-contract-merge;分支:feature/iam-u3-account-contract
|
||||
- 授权与交付终点:用户 2026-10-18 授权按计划完成并清理 IAM 各线 worktree,U3 采用 merge_then_accept。回收 Dev U3 worktree 时发现 U3 已批准合同(Documents `c236d30` r1、`fb3a821` A1–A4 修订、`5783940` 委托投影真实操作者修订)只存在于该 worktree 的子模块元数据中,从未推送,也未进入 Documents main;本任务先把原分支推送到远端保全,再经 PR 合入 main。
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)。原分支缺 `AGENT-EXECUTION.md`,因此从 main 新建分支,由 worker 应用 `git merge-tree` 三方合并补丁并解决 `INDEX.md` 冲突;随后以 `merge -s ours` 并入原分支头 `3481253`(树不变),使已批准 SHA 可从 main 到达。
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:三份已批准合同正文按原文进入 `docs/proposals/`,`docs/proposals/INDEX.md` 收录 U3 条目并写明当前事实状态。
|
||||
- 非目标:修改已批准正文;推进 Dev 的 Documents gitlink;部署或业务验收。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
| 单元 ID | 预期范围 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| iam-u3-documents-contract-onto-main | docs/proposals/INDEX.md 与三份 2026-10-14-iam-u3-*.md | implementation | cursor, antigravity, pi | 1 | cursor-agent / auto(session not-observable,新进程) | success | apply 快进 1e036e9..08b8618 | 否 | /tmp/cleanup-20261018/render/u3-doc-contract-receipt.json |
|
||||
| iam-u3-documents-contract-index-date-repair | docs/proposals/INDEX.md | repair | cursor, antigravity, pi | 1 | cursor-agent / auto(session not-observable,新进程) | success | apply 快进 08b8618..43f2fd4 | 否 | /tmp/cleanup-20261018/render/u3-doc-contract-repair-receipt.json |
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:三份合同正文与 INDEX 条目;修复单元把 Backend PR #198 的合入日期更正为 2026-10-14(Gitea API `merged_at` 核对)。
|
||||
- 验证(SELF,host):三份正文的 blob 与 `origin/feature/iam-u3-account-contract` 逐一相同;`git diff --check` 通过;`merge -s ours` 前后树相同;`c236d30`、`fb3a821`、`5783940` 均为分支头祖先;Backend `v0.5.0` 经 `git ls-remote` 核对指向 `2716b9e`。
|
||||
- Review:host 主模型对照原分支与 Gitea PR 元数据逐项核对 INDEX 事实,无阻塞项。
|
||||
- 影响与剩余项:仅文档。Dev 的 Documents gitlink 未推进(非目标)。
|
||||
@@ -0,0 +1,30 @@
|
||||
# feature/regulations-kb-ext20-proposal 任务卡
|
||||
|
||||
任务主记录:本文件随 PR 描述维护。
|
||||
状态:已完成(合入前闭合);owner:weichen。PR:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/113
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Documents.git;root_target:main;immediate_target:main
|
||||
- branch_kind:standalone;跨仓总任务:Agent-System-Dev `feature/regulations-kb`([Dev PR #614](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/614))
|
||||
- R0(initial_main_sha):e2f5729;工作区:/home/weichen/projects/Agent-System-Documents-wt-feature-regulations-kb-u9;分支:feature/regulations-kb-ext20-proposal
|
||||
- 授权与交付终点:用户 2026-10-18 /goal 指示按法规知识库计划实现全部内容;本任务登记 U9 的 EXT-20 扩展点提案(与 Backend `feature/regulations-kb-ext20` 实现一致),经 PR 合入 main。
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)。此前分支 `feature/regulations-kb-u9-contract` 上的提案由 host 直接撰写且未经准入钩子,作废不合入,收口时删除该远端分支。
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:`docs/proposals/2026-10-18-ext-20-regulations-provider.md` 与 `docs/proposals/INDEX.md` 收录条目;内容与 Backend 实现的公开符号逐一一致。
|
||||
- 非目标:实现代码(Backend/KB/RDM 各自任务)。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
| 单元 ID | 预期范围 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| documents-ext20-proposal | docs/proposals/2026-10-18-ext-20-regulations-provider.md、docs/proposals/INDEX.md | implementation | cursor, antigravity, pi | 1 | cursor-agent-stable / auto(session not-observable,新进程) | success | apply 快进 e4aa3c6..52d77f6 | 否 | /tmp/regkb-u9doc-receipt.json |
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:EXT-20 提案(场景、现有扩展点不可组合理由、全部公开符号与 DTO 字段、基座强制行为、兼容、验收映射、分工)与 INDEX 收录条目。
|
||||
- 验证(SELF,host 于 52d77f6):`git diff --check` 通过;`scripts/dm governance check --repo .` ok。
|
||||
- Review:host 主模型对照 Backend `pkg/extension/regulations.go`、`internal/app/regulations.go`(PR #212 合入 56a7f9f)逐项核对,无阻塞项。
|
||||
- 影响与剩余项:仅文档。
|
||||
@@ -0,0 +1,66 @@
|
||||
# <任务标题> 任务卡
|
||||
|
||||
任务主记录:<Issue/PR 链接,或注明"本文件随 PR 描述维护">。
|
||||
状态:<进行中 / 待审核 / 已合入 / 已放弃>;owner:<填写>。
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:<仓 URL 或逻辑 ID>;root_target:main;immediate_target:<main 或 feature/initiative>
|
||||
- branch_kind:<initiative | child | standalone>;initiative_id:<稳定 ID,普通任务为空>
|
||||
- R0(initial_main_sha):<完整 SHA>;工作区:<路径>(worktree / clone);分支:<feature|fix|hotfix/...>
|
||||
- 父任务信息(仅子任务填写):parent_branch=<父分支>;parent_head_sha=<SHA>;parent_pr=<父 PR>
|
||||
- accepting_requirements:<true | false>(仅 initiative 可为 true;冻结后为 false)
|
||||
- 需求分流:verdict=<JOIN | NEW>;selected_initiative=<分支或无>;候选=<分支@HEAD 列表或无>;理由=<不适配/适配依据>;triage 收据=<源码外路径与摘要>
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md;外部 Agent 一单元一新进程/会话/工作区)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
按根目录 AGENT-EXECUTION.md 路由;外部 Agent 一单元一新进程/会话/隔离工作区。实施、修复与测试必须通过登记的候选执行;未配置或候选安全耗尽停在 unavailable/blocked,禁止 host 隐式代写。
|
||||
|
||||
| 单元 ID | 预期范围 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 复用材料(task-start 自动检索结果摘录)
|
||||
|
||||
- 相关近期提交:<git log -- <paths> 命中;无则写无>。
|
||||
- 相关经验条目:<检索命中;无则写无>。
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at | 结果 |
|
||||
|---|---|---|---|---|
|
||||
| 启动 | <R0> | <HEAD> | <日期时间> | <一致/已同步> |
|
||||
| 验收前 | <填写> | <填写> | <填写> | <填写> |
|
||||
|
||||
字段没有发生的写"无",不省略。本卡与 PR 描述同源:开 PR 时将"目标与非目标、结果与验证、影响与剩余项"复制进 PR 描述,不另写第二份。
|
||||
|
||||
## 交付调度(有多个就绪任务时)
|
||||
|
||||
- 调度记录 / entry_id / 当前 attempt_id:<平台或调度服务链接及标识;未入队写无>。
|
||||
- 入队源 HEAD / owner / 依赖:<完整 SHA、身份、依赖或无>。
|
||||
- 固定候选引用:<调度尝试中的 base/head、组件与配置;未固定写无>。
|
||||
- 窗口、最终 CI、Review 与真实合入结果:<引用调度记录及原始证据,不复制全队列>。
|
||||
|
||||
定义见 [合入调度与 CI 分配](../standards/merge-queue.md)。本段在候选固定前整理;固定后先使用源码之外的记录保存运行结果,再按项目闭合流程回填。
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:<交付了什么;未发生写无>。
|
||||
- 验证:<结论性摘要:PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据见下表,来源标注与写入规则见 [按影响选择验证与证据](../standards/verification.md)>。
|
||||
- 影响与剩余项:<影响面、遗留风险、后续任务>。
|
||||
|
||||
| 时间(UTC) | 命令 | 被测 HEAD | exit | 来源 | 摘要/位置 |
|
||||
|---|---|---|---|---|---|
|
||||
| <时间> | <命令;由项目 evidence 工具写入,无工具时附命令+对象 SHA+输出 digest 并标 SELF> | <HEAD> | <exit> | GATE/REPLAY/SELF | <摘要/位置> |
|
||||
|
||||
## 回收(task-finish)
|
||||
|
||||
- 合入核对:<分支已包含于目标分支的核对输出>。
|
||||
- 未跟踪/ignored 文件处置:<归档/可再生/存疑 逐项;无则写无>。
|
||||
- 回收动作:<worktree remove、分支删除、prune;放弃路径注明原因>。
|
||||
@@ -0,0 +1,67 @@
|
||||
# Project Agent Execution Configuration
|
||||
# 项目工程 Agent 执行路由配置
|
||||
|
||||
适用范围:定义本项目中由 AI / Agent 主导的代码实施、修复、测试与审阅所调用的 Agent CLI 路由。
|
||||
解析器识别正文中唯一一个 ```agent-execution 代码块。正文外的说明文字可根据项目需要调整。
|
||||
|
||||
通用硬规则见 Development-Guidelines 的 [Agent 执行与协作契约](deps/development-guidelines/standards/agent-execution.md)。
|
||||
本配置为项目本地路由权威,声明具体的候选 profile、尝试次数上限、超时、集成入口与豁免路径;
|
||||
代码块须通过 `deps/development-guidelines/templates/schemas/agent-execution.schema.json` 校验后才能登记为 active。
|
||||
`fallback_to_host` 只能为 `never`;旧值 `all-unavailable` 仅供迁移工具读取,不能通过准入。
|
||||
下例的 CLI 名称、命令与路径均为占位,按项目实际执行器替换。
|
||||
|
||||
```agent-execution
|
||||
schema: agent-execution/v1
|
||||
|
||||
execution:
|
||||
fresh_process_per_unit: true
|
||||
permission: run-everything
|
||||
fallback_to_host: never
|
||||
max_parallel: 3
|
||||
timeout_seconds: 7200
|
||||
idle_timeout_seconds: 600
|
||||
summary_bytes: 16384
|
||||
raw_output_bytes: 10485760
|
||||
max_attempts_per_profile: 3
|
||||
emergency_profile: codex
|
||||
|
||||
roles:
|
||||
implementation: [cursor, antigravity, pi]
|
||||
repair: [cursor, antigravity, pi]
|
||||
test: [pi, antigravity, cursor]
|
||||
review: [pi, antigravity, cursor, codex]
|
||||
|
||||
profiles:
|
||||
cursor:
|
||||
adapter: cursor
|
||||
executable: "cursor-agent-stable"
|
||||
model: auto
|
||||
enabled: true
|
||||
|
||||
antigravity:
|
||||
adapter: antigravity
|
||||
executable: agy
|
||||
model: auto
|
||||
enabled: true
|
||||
|
||||
pi:
|
||||
adapter: pi
|
||||
executable: pi
|
||||
model: auto
|
||||
enabled: true
|
||||
|
||||
codex:
|
||||
adapter: codex
|
||||
executable: codex
|
||||
model: auto
|
||||
enabled: true
|
||||
|
||||
integration:
|
||||
exempt_paths: [".task/**", "docs/superpowers/specs/**", "docs/superpowers/plans/**"]
|
||||
transport:
|
||||
command: "scripts/agent-exec apply"
|
||||
admission:
|
||||
command: "scripts/agent-exec admit"
|
||||
cleanup:
|
||||
command: "scripts/agent-exec cleanup"
|
||||
```
|
||||
@@ -34,3 +34,23 @@
|
||||
- **SSH key**:`~/.ssh/home_gitea`(ED25519,公钥指纹
|
||||
`SHA256:GYXaOXIMeloQ1fKSfdJ/ndgPQzs/Ix7Hk62ekfkfQ20`,Gitea 侧密钥名 GMKtec);
|
||||
`~/.ssh/config` 已有 `git-ssh.u1680832.nyat.app` 主机条目,克隆/推送直接可用。
|
||||
|
||||
## Agent 执行与新项目准入(强制)
|
||||
|
||||
### 新项目准入(强制)
|
||||
|
||||
1. 缺 Guidelines 固定版本(`deps/development-guidelines` gitlink 未就绪或 HEAD 不符)时,Agent 只能只读分析,不得实施。
|
||||
2. 缺有效 `AGENT-EXECUTION.md`(`scripts/dm governance check` 未通过)时禁止 AI 实施。
|
||||
3. 缺共享执行器(`scripts/agent-exec` 或 `deps/development-manager` 不可用)时,状态为 `blocked/unavailable`,不得改由 host 替代。
|
||||
4. 不允许 host 代写实施单元;实施、修复与测试必须经 `AGENT-EXECUTION.md` 登记的候选执行。
|
||||
5. 人类直接开发按其自身授权与项目规则进行,不受本条款约束。
|
||||
6. 分支保护与规范 tag 由用户负责设置与维护,Agent 不得代为设置。
|
||||
|
||||
入口:
|
||||
|
||||
- 需求分流:`scripts/task triage discover|decide|verify`
|
||||
- 任务启动:`scripts/task start …`
|
||||
- PR 检查:`scripts/task pr-check …`
|
||||
- Agent 执行:`scripts/agent-exec run|apply|admit|validate`
|
||||
|
||||
AI 实施单元仅通过 `scripts/agent-exec run` 按 AGENT-EXECUTION.md 运行,并经 `scripts/agent-exec apply` 集成。
|
||||
|
||||
@@ -17,3 +17,8 @@
|
||||
|
||||
本仓没有可编译程序或独立业务测试套件。跨仓固定组合在 Dev 仓运行 `make test`;
|
||||
过程记录中的命令和环境须按当次任务核实,不能机械重放旧运维命令。
|
||||
|
||||
治理检查(CI `.gitea/workflows/governance.yml` 同步执行):
|
||||
|
||||
- `scripts/agent-exec validate --repo .`:校验 AGENT-EXECUTION.md 的唯一 `agent-execution` 配置块,须输出 `validate: ok`。
|
||||
- `scripts/dm governance check --repo .`:只读核验治理文件与 gitlink,须只输出 ok 行。
|
||||
|
||||
+1
Submodule deps/development-guidelines added at 2688fd2be6
+1
Submodule deps/development-manager added at bd3a7de57a
@@ -0,0 +1,211 @@
|
||||
# IAM U3:账号资料、有效态、受控目录与本人恢复
|
||||
|
||||
版本:r1 草案;日期:2026-10-14;owner:weichen。状态:待阶段 4–5 人工评审,未实施、未验收、未合入、未部署。
|
||||
|
||||
主任务:Dev `feature/iam-u3-account-directory-recovery`。承接用户对下一步 U3 的“授权”、[统一方案 r1](2026-10-13-identity-role-permission-plan.md) U3 及 [IAM-D/IAM-C 合同](2026-10-13-iam-i1-permission-contract.md) §5–6。统一方案原文 SHA256 为 `39b8e9a6e078dc509bd80118c1807ce6ed23e4466e5902b51f4bfa255d55c0a1`,本补充不修改其正文,不扩展到 U4–U9。
|
||||
|
||||
本文落实已经批准的人员资料、管理员业务隔离和本人恢复原则,补齐实际公共接口与切换方法。网页建号不再接受初始密码/管理员位、旧凭据不能自动成为本人证明,是本次需要明确审阅的兼容取舍。用户的启动授权不代替新对象的阶段评审。Review、CI 与合入按 Dev `policy/delivery.md`;生产迁移、部署、服务/真实账户操作、异常人工恢复和清理另行授权。
|
||||
|
||||
## 1. 核查对象与现状
|
||||
|
||||
以下仅为固定源码观察(SELF),不是运行结果。业务测试、真实 PostgreSQL 重放、浏览器、部署及迁移均 `NOT_RUN`。
|
||||
|
||||
| 对象 | 固定版本/位置 | 观察及影响 |
|
||||
|---|---|---|
|
||||
| Dev 启动基线 | `add097d06137c4912a4ed9279e98ca353e4e4cf1` | 已取得启动收据并建立独立任务工作区;原 I1 已交付,不重做 |
|
||||
| Backend | `5d5fc9891c0eb8994545ccb8b740535d9b99fb0e`,`internal/store/auth.go` | `users.id/owner` 稳定,已有姓名、原子建号及 auth_version;缺资料版本与停用状态 |
|
||||
| 当前扩展面 | 同 Backend,`pkg/extension/extension.go`、`internal/app/hostcaps.go` | 实际为 `2.12.0-experimental`,EXT-19 已有 Name 和姓名搜索;目录直接取所有账号,Valid 恒 true,无提供者字段授权 |
|
||||
| 认证与恢复 | 同 Backend,`internal/server/accounts.go`、`internal/store/login_keys.go` | 管理员仍直接取目标明文 Key/设置密码;SessionInfo 不核对当前账号版本;部分最终写缺同账号锁/CAS |
|
||||
| RDM 消费源码 | `6fcb256205c2384abed35d3b6ab96ca7216e9297` | 成员目录有 member.manage 检查;缺能力时有效性检查放行、调用者丢弃错误;三权只校验项目成员关系 |
|
||||
| Frontend | Dev 基线所固定版本,`src/components/adminSections.tsx`、`src/api.ts` | 管理页提供签发/复制目标恢复 Key;必须随新合同收口,不能只改 Backend |
|
||||
| 本文 Documents 起点 | 权威 `origin/main`:`7bd4e17827d4c7c88647bf3c9199c495b60cb107` | 独立 `feature/iam-u3-account-contract`;不在 Dev 的固定子模块里改源码 |
|
||||
|
||||
Backend `account_lifecycle_test.go`、`login_keys_test.go` 和现有 HTTP auth helper 大量硬编码 OpenSQLite。仅设置 PG DSN 不会让这些场景在 PostgreSQL 执行;历史报告中的 PG 覆盖不能作为本轮证据。
|
||||
|
||||
在途 `feature/svc-account-durability-plan` 当前授权集中于文档/svc 帮助及其测试,不替代 U3 业务实施。实施前再核验 Backend/RDM 权威 HEAD、在途迁移及写者范围;迁移编号和扩展面次版本由当时基线分配,不预占 v31 或 2.13。
|
||||
|
||||
## 2. 需求、范围与验收对象
|
||||
|
||||
| 需求 | 本轮具体结果 | 可判定验收条件 |
|
||||
|---|---|---|
|
||||
| R3/R5 | 可选资料及独立资料写面 | 姓名、电话、邮箱、工号、部门、备注全部可空;填写/清空保持原 userId、owner、角色、凭据和业务数据;本人不能借资料 patch 改权限 |
|
||||
| R4/R5 | 精确/模糊、字段与状态过滤、分页及最小候选投影 | 同名不误选;未授权字段不返回、不匹配、不影响数量/分页;稳定 ID 决定选择对象;职位过滤明确留 U4 |
|
||||
| R11/R12 账号部分 | 有效态、认证与执行边界检查 | 停用拒绝新请求/登录/兑换,旧会话与 API key 不再认证,候选人不能提交,持续流停止新输出;启用不恢复已撤会话和 Key |
|
||||
| R7/R12 恢复部分 | 管理员提案、独立本人确认、受限兑换 | 管理员拿不到目标设密能力;初始密码/旧 Key 派生链不成为证明;无独立证明线上拒绝;完成后本人重新密码登录 |
|
||||
|
||||
U3 不建设通用职位权威、多职绑定、角色 cap 配置、完整管理矩阵、项目职位重选或统一跨库 authorityEpoch。现有 RDM 单角色、项目成员及文档职责保持各自权威。本人资料和恢复只在必要的既有界面中闭合;完整系统用户/职位配置体验仍是 U7。
|
||||
|
||||
## 3. 单一账号权威与数据
|
||||
|
||||
Backend 复用 `users.id` 与唯一 `owner`;owner 仍为登录名/兼容定位,不允许资料修改改变身份。RDM 不复制账号表,不把显示名或工号当身份主键。
|
||||
|
||||
| 数据 | 规则 |
|
||||
|---|---|
|
||||
| Profile | `name/phone/email/staffNo/department/notes` 默认空;`profileVersion` 独立递增,显式空值表示清空;未知字段整体拒绝 |
|
||||
| AccountState | `enabled`、`stateVersion`;valid 表示账号存在且 enabled。未设密码/强制改密是独立认证限制,不混作停用 |
|
||||
| Credential lineage | 持久来源及是否具备本人证明资格;`legacy_unknown` 默认无资格,普通管理员路径永远不能提升资格 |
|
||||
| Sessions | 保存受信认证方式、凭据来源、auth_version;受限会话关联确切 activation/recovery grant;未知来源不作证明 |
|
||||
| Directory revision | 独立、持久、事务内更新;候选目录版本只受其可见辨识资料/有效态变化影响,私密联系方式编辑不能成为候选查询的侧信道 |
|
||||
| Recovery | 独立 proposal/proof-session 引用/grant 记录;仅存随机凭据摘要,保留目标、用途、到期、状态、版本、操作者与因果关联 |
|
||||
|
||||
建议长度上限:姓名 128、电话 64、邮箱 254、工号 64、部门 128、备注 2000 个 Unicode 字符;邮箱空值合法,非空做基础格式校验。电话只 trim,不猜国家码/号码归一化;文本不强制唯一。拒绝控制字符和超限输入,不静默截断。搜索限制 128 字符,参数化查询,并转义 LIKE 通配符。
|
||||
|
||||
资料 patch 只允许六个字段,要求 expectedProfileVersion;读取/写入按稳定 userId。请求中的 owner 若用于兼容,必须与服务端 ID 映射一致。`isAdmin/enabled/roles/password/proof` 等字段不能混入 profile 或普通建号请求。
|
||||
|
||||
网页账号管理只创建无凭据的普通账号,可选资料全空成功;初始密码/管理员位参数明确拒绝,不能忽略后继续建号。新建记录持久标识 pending initialization,迁移中的既有未知账号不能推断为该状态。首管及新账号首凭据建立由可信本地 owner/bootstrap 边界完成,其来源持久为可信 owner 初始化;网页管理员不能通过旧 activate 接口补领凭据。
|
||||
|
||||
补充窄 CLI `user initialize-account <userId>`:由受信机器/DB owner 为尚无密码、无历史完成初始化、无其他既有凭据的新待初始化账号建立首 activation grant,要求 expectedStateVersion/初始化 generation,保持原 userId、owner、管理员位及业务关系。网页创建后的待初始化账号和本地新账号都走同一服务。Key 过期、交付失败或已兑换但尚未完成首次设密时,可信 owner 可以显式续办:在同账号事务中撤旧 activation grant/派生受限会话,推进 generation 后重新交付;不得重复建号,不改变已完成初始化账号的密码。完成首次设密的事务把状态改为 initialized,后续所有 initialize/reissue 都拒绝。
|
||||
|
||||
首管 bootstrap 的建号/首 activation grant 保持同事务;其过期续办也仅限上述已明确来源的 pending 状态,不借“用户库非空”重建首管。generic activate、reset 与该 initializer 明确分开,普通系统管理员、HTTP/Host/角色 grant 均不能调用 initializer。CLI 来源记录实际本地操作者及目标,不接受 --actor 自报成为授权;秘密仅交受信 owner 的保护交付通道。代码与隔离测试纳入 U3,真实机器/DB owner 身份核对、首次交付及续办运行仍须另行批准的操作计划,不用可选资料验证收件人。
|
||||
|
||||
## 4. 受保护系统动作与资料权限
|
||||
|
||||
U4 前保留一份 Backend 受保护账号管理适配器,仅识别 `system.users.view/create/edit/disable` 与 `credential.recover`,以当前有效的既有系统管理员资格为过渡来源。该适配器不是通用 Role/SystemGrant 新权威,不给项目 grant 赋予系统资料权限,也不把插件 `Host.IsAdmin()` 布尔当新目录授权回执。
|
||||
|
||||
本人可读/改六个非授权资料字段,须正常、有效完整密码会话;受限设密会话只走设密门。系统资料读取和搜索需 system.users.view,编辑需 edit,停用需 disable,恢复提案需 credential.recover。调用方、对象、资格及 expectedVersion 在写事务中重验;资格读取失败拒绝。
|
||||
|
||||
启用/停用与资料分别提交、分别版本化。停用事务锁定受保护管理者集合,保证至少一个有效且具备可用正常认证能力的系统管理者;PG 使用共同的锁顺序/管理者保护锁,SQLite 使用写事务,禁止“先 count 后独立 update”。并发停用最后两名管理者最多成功一个。
|
||||
|
||||
停用不物理删除账号或密码摘要,不改历史业务归属。事务内推进认证版本、撤销 API key、会话、激活/恢复 grant 与提案,并审计;启用后必须重新认证,已撤对象永不自动恢复。
|
||||
|
||||
## 5. IAM-D 提供者、授权与候选范围
|
||||
|
||||
新增可选 `AccountDirectoryProvider`,不向既有 Host 增加必实现方法;提供查询与精确候选查验两个入口。下列是冻结的语义形态,Go 的具体命名及签名可在同语义下按仓内规范落实:
|
||||
|
||||
| 合同 | 输入/输出 |
|
||||
|---|---|
|
||||
| Query | schemaVersion、purpose、领域及 project/document locator、exact/fuzzy 模式、query、searchFields、状态过滤、cursor、limit;不接受 Actor、isAdmin 或 allowedFields 输入 |
|
||||
| Page | userId、owner、valid、获准 name/staffNo;opaque directoryVersion、nextCursor。候选目录不提供全系统总数或 bulk export |
|
||||
| Exact lookup | 在同一 purpose/locator 下指定稳定 userId(兼容 owner 必须显式标识并解析);只返回确定身份、valid 与当前版本,不扫描整个目录 |
|
||||
| 专用注册口 | 可选目录授权 Registrar,由受信进程内 RDM 注册;重复/冲突注册拒绝。Backend 构造 actor/plugin/purpose/locator,回调解析真实项目/文档并返回当前资格、可见字段、可匹配字段及候选范围 |
|
||||
|
||||
Backend 首先验证受信 dispatch actor 的有效态,再咨询专用领域回调。purpose 与 locator 只是待验证请求,不能授予资格。项目选人查当前 member.manage;职责候选查目标文档/项目当前 duty.assign,并从服务端得到当前项目成员集合。候选范围在匹配、排序、分页前施加,不能取全系统页再由 RDM 删掉非成员。
|
||||
|
||||
系统管理查询由 Backend 适配器决定。领域回调不能返回联系方式权限;系统资料授权不能绕过 RDM 项目资格。未知 purpose、字段、模式、状态、职位过滤,文档/项目不一致,actor 为空,提供者/回调缺失或报错,一律拒绝;不用 ResourceAuthorizer V1、旧宽松 EXT-19 或手填 owner 兜底。
|
||||
|
||||
| 使用面 | 默认可见/可匹配字段 | 范围 |
|
||||
|---|---|---|
|
||||
| 系统资料管理 | userId、owner、valid、六个获准资料字段 | system.users.view;资料查询独立于候选 DTO |
|
||||
| 本人资料 | 本人六个字段 | 身份从认证上下文取得,不接受目标 owner 参数 |
|
||||
| 项目成员候选 | userId、owner、name、valid | 当前 member.manage;enabled 账号;无私密字段 |
|
||||
| 文档职责候选 | userId、owner、name、valid | 当前 duty.assign;当前项目成员且账号 enabled;现有职责规则再检查 |
|
||||
|
||||
staffNo 仅在回调有明确受信字段授权时披露/匹配;本期项目默认不授予,不能因用户填了工号便自动开放。电话、邮箱、部门、备注仅在独立资料面出现。searchFields 必须是当前允许匹配/返回字段的子集;被禁止字段不能通过默认全文搜索暗中参加匹配。
|
||||
|
||||
精确 ID/owner 命中也要先授权。模糊候选必须让用户明确选一个稳定 ID,名称与 ID 不混用。候选查询无词时不枚举全系统,返回提示输入账号/姓名;模糊词至少两个字符,最大页 50,系统管理最大页 100。keyset 按稳定 owner、userId 排序;游标签名绑定 actor、purpose、真实领域范围、字段、模式、过滤、查询及授权/可见目录版本。串用或过期拒绝,要求重新查询,不悄悄换页。
|
||||
|
||||
EXT-19 保留现有 Owner/Name/Valid 隐私范围,Valid 改为真实状态。旧签名缺领域 locator,只允许明确系统目录管理上下文,或已有可信 dispatch 领域上下文经同一授权服务判定;无此上下文拒绝。U3 的 RDM 消费者要求新能力,不回落旧宽松行为。此为明确的授权收紧,登记兼容变化,禁止描述成旧行为完全不变。
|
||||
|
||||
RDM 成员/三权写入共享服务先检当前领域管理资格,再做精确账号查验;有效态错误必须传播。检查覆盖成员添加/任命、章节、整文、批量三权,选人查询不是提交授权。三权仍要求项目成员、相异人员、轮次/冻结条件;不因提供者确认账号有效而省略领域规则。
|
||||
|
||||
## 6. IAM-C:本人证明与恢复
|
||||
|
||||
### 6.1 最小可交付证明
|
||||
|
||||
首期只实现“提案之前已经存在的、具备可信凭据来源的完整密码会话”这一分支;不新增短信、邮件、SSO、恢复码或 WebAuthn。姓名/电话/邮箱/工号不参与证明建立、搜索或匹配。
|
||||
|
||||
具备资格须同时满足:目标本人、auth-on、确切 cookie/password 会话、full、非强制改密、未过期/撤销、当前 auth_version、enabled、可信 owner 初始化或可信本人恢复的凭据来源。API key、auth-off、restricted、混合/冲突 Bearer+cookie、客户端自报 actor/scope/proof、管理员密码/Key 派生链和 legacy_unknown 均拒绝。
|
||||
|
||||
提案事务在同账号锁内记录当时已存在的合格会话摘要引用,确认只能使用记录中的仍有效会话;不靠客户端时间,也不因提案后新登录得到资格。新登录/凭据变更遵守同一锁顺序,避免时间相等或并发插入绕过。
|
||||
|
||||
管理员给初始密码 → 自行强制改密 → 再密码登录的 full 会话仍继承不可信来源。主动改密不能提升证明资格,资料修改不能提升,管理员不能改变来源。历史密码保留正常登录,但来源默认 legacy_unknown;不得把“用户再次登录”解释为证明历史管理员从未掌握密码。若未来要信任某些历史账号,需另有用户批准的可信 owner 证明建立方案。
|
||||
|
||||
无合格旧会话时线上明确拒绝,不能签 Key 给管理员“代交”。丢失全部会话/凭据的异常本人恢复仍由另行批准的物理 owner 操作处理,U3 不提供新的旁路。
|
||||
|
||||
### 6.2 提案、确认、兑换与完成
|
||||
|
||||
| 路径/动作 | 合同 |
|
||||
|---|---|
|
||||
| POST `/api/admin/users/{userId}/recovery-proposals` | credential.recover、版本及幂等校验;仅提案 ID/目标/状态/期限/版本。不改目标密码、不删正常会话、不改变 must_change,不返回证明或 Key |
|
||||
| GET/DELETE 同目标提案 | 管理者仅状态/撤销;路径目标与记录绑定;没有恢复秘密或会话摘要输出 |
|
||||
| GET `/api/me/recovery-proposals`(含按 id 查状态) | 目标只能是当前本人;返回 pending/confirmed/completed/revoked/expired、版本/generation 及非秘密来源说明,不接受 owner 注入,不返回 Key/证明摘要 |
|
||||
| POST `/api/auth/recovery-proposals/{id}/confirm` | 本人 cookie 证明、pending、expectedVersion/auth_version 在最终事务重验;一次 confirmed 并发 grant;仅该本人 HTTP 响应获得明文 |
|
||||
| POST `/api/auth/recovery-proposals/{id}/rotate-grant` | 仅原合格本人证明,confirmed、expectedVersion/generation 与当前账号版本;原能力及派生会话同事务撤销后仅本人获得新明文;不重新确认、不给管理员补领 |
|
||||
| POST `/api/auth/recovery-grants/redeem` | body 中凭据;摘要定位目标,无 URL/Bearer 形式;一次消费与建立 set_password_only 会话同事务 |
|
||||
| POST `/api/auth/password`(grant 设密) | 复用既有入口;此分支须有确切可信 activation/proof-linked grant;最终事务再核状态/来源/版本/期限/撤销;成功更新密码、撤相关能力与全部旧会话、审计,返回 reloginRequired,清 cookie |
|
||||
| 同入口(有密码的强制改密) | 仅服务端标记为 password-authenticated 的确切受限 cookie 会话;必须校验 current 密码、enabled、auth_version、must_change。在最终事务更新、撤旧会话并重新登录;继承原来源,不获得本人恢复证明资格;legacy Key 派生会话不得进入此分支 |
|
||||
| 普通主动改密 | 保留 current 校验;继承凭据来源;若保留当前会话,事务内同步其 auth_version,其余会话撤销 |
|
||||
|
||||
提案默认 24 小时;grant 5 分钟;受限会话 10 分钟。grant 256-bit CSPRNG、摘要存储,绑定 target/purpose/proposal generation/issued auth_version。响应 no-store,不写日志/审计/localStorage/剪贴板自动复制。确认或兑换不生成业务会话;设密后必须正式密码登录。
|
||||
|
||||
确认成功但响应丢失,不重放明文。页面进入“结果待核验”,读取本人提案当前非秘密状态;pending 才可再次显式确认,confirmed 显示“原能力无法读回”,仅仍具备原合格证明的本人可点“重新生成设密能力”,走 rotate-grant 后继续兑换。过期、撤销、完成或证明失效则说明当前状态并退出;不能自动循环签发或让管理员补领。幂等重复返回非秘密状态。若兑换响应丢失而 cookie 已成为合法受限会话,先经 `/api/me` 确认当前模式,再继续设密;若结果仍未知,查状态后由本人显式续办。
|
||||
|
||||
登录/`/api/me` 输出服务端确定的非秘密 `passwordChangeMode`:grant_setup 或 current_required;UI 据此在可信 activation/recovery 设密时省略 current、强制/普通改密时要求 current。客户端自报模式无效,未知模式拒绝。明确失败整体回滚;结果未知先查状态,不能盲目自动重试秘密签发。
|
||||
|
||||
PG 同账号凭据事务统一锁顺序 user → proposal → grant → session;最终 UPDATE 使用版本谓词,不能仅先 SELECT。SQLite 复用 immediate 写事务。提案/确认/撤销/补发/兑换/设密/主动改密/停用/正常会话建立共享版本栅栏;审计与写入同事务,故障无部分成功。
|
||||
|
||||
### 6.3 公共扩展与旧入口
|
||||
|
||||
可选 `RecoveryProposalProvider` 只公开提案、状态和撤销的 metadata DTO;actor 从 Host 绑定。插件不确认本人、不接收恢复 Key,不按 actor 字符串推导 cookie 证明。本人恢复 grant 的秘密交付只在 Backend 本人 HTTP 会话入口;若未来工具需要本人确认,须另有不可伪造证明 handle 合同。本期无必要消费者则不增加无人消费的 Host 写方法,只冻结 DTO 与 HTTP 合同。
|
||||
|
||||
旧 POST `/api/admin/users/{owner}/password`、POST `.../login-keys` 明确拒绝并给出新提案路径,不能返回成功兼容。建议稳定 410/`RECOVERY_CONTRACT_REPLACED`;目标不存在也不进行凭据写。旧 Key 的读取仅限无秘密 metadata;撤销必须校验 path owner 与 key 真属主。
|
||||
|
||||
generic CLI `user reset-password`、`issue-login-key --purpose recover/activate` 对既有账号拒绝;在打开/迁移 Store、撤 apiKey/session、生成密码之前校验,避免先产生副作用再拒绝。新账号可信 owner 初始化和无用户首管 bootstrap 按§3保留窄路径;本地创建同事务建号/首 activation grant,已登记新 pending 账号由 initializer 续办,不把它们扩展成已有凭据账号恢复。svc 包装器/帮助同步,不新增 --force 旁路;真实运行仍另行授权。
|
||||
|
||||
## 7. 账号停用、Host 与后台边界
|
||||
|
||||
SessionInfo 与 AuthOwner 必须读取当前 enabled;会话同时比较当前 auth_version。密码验证与最终创建会话在同账号栅栏内复验。权威状态读取错误拒绝,修正当前 password gate 读错误放行。
|
||||
|
||||
Host 各账号能力及保留的 dispatch Host 在调用时读当前状态,不能把构造时 actor 当永久 allow。run start/resume、工具执行、HITL 确认后、Backend 有后果写入和新输出前核验账号状态;停用通过既有取消机制停止本实例活动任务,其他实例在持久状态重验时停止。SSE 每个新帧/重放前复验,安静流继续周期复验,不能只等待 15 秒定时检查后继续输出旧数据。
|
||||
|
||||
U3 只承诺账号有效态和 Backend 自有凭据/写事务边界。RDM 在实际写入前再次做当前账号查验;Backend 与 RDM 独立数据库间,最后一次查验与 RDM commit 仍非同一事务,不能据此宣称零窗口的跨库提交 fencing。完整角色/成员/三权撤销、跨实例运行 fencing 和旧模型上下文整体重建是 U8。若验收要求 RDM 并发停用后绝不提交任何在途写入,则需将该 fencing 作为新增契约重审,不能扩大 U3 的通过声明。
|
||||
|
||||
## 8. 迁移、切换与回退
|
||||
|
||||
增量迁移保留账号 ID、owner、密码摘要、业务/角色/成员/历史职责。name 原值保留;新资料空、不从聊天或联系方式推断。enabled 对既有账号按已批准的既有有效态初始化,profile/state 版本设固定初值。旧来源均标 legacy_unknown;不给历史记录补造本人证明。
|
||||
|
||||
恢复切换必须同时处理未兑换 legacy Key、已兑换 legacy restricted session,以及既有 full session 的证明资格。无 proof-linked 来源的旧恢复/激活能力不能在新线上免 current 设密;不可仅禁止新签发。有既有密码但 must_change 的账号仍可通过密码认证及 current-required 分支完成强制改密,来源继续 legacy_unknown/原不可信来源。旧 full 会话业务策略与证明资格分开,正常业务认证继续遵守当前有效态与版本,恢复资格默认拒绝;保留历史密码不意味着能证明历史管理员从未掌握它。
|
||||
|
||||
新 schema 可读不代表旧认证协议可安全写。切换要求所有 HTTP、CLI、后台写者固定新版本;旧 writer 不得与新合同并行。迁移兼容登记/启动守卫须阻止旧程序静默重开恢复及忽略 disabled;若当前版本机制不足,列为实施停止条件并提出具体栅栏修订。回退只到保留新安全协议的修复版本,不能恢复旧越权接口/有效凭据。
|
||||
|
||||
本轮仅在新建隔离 SQLite/PG 副本演练迁移、重启和失败回滚;现有服务、真实账号及生产数据库均不触碰。生产切换另交备份/身份/writer 清单与恢复演练方案,且必须先具备获批的两份 owner 操作安排:新 pending 账号首次初始化/过期续办的身份及秘密交付手册,以及丢失全部凭据的历史账号异常恢复/证明建立手册。由 weichen 指定实际可信机器/DB owner 操作者、固定目标和接口并批准运行;两者缺一不得切换,不用临时数据库手术代替。本文不批准对 legacy 账号提升证明来源,也不提供异常重置旁路。
|
||||
|
||||
## 9. 实施顺序、负责人及文件范围
|
||||
|
||||
协调 owner 为根智能体;组件在各自权威 origin/main 建登记工作区,固定来源及组合,不编辑 pins。各项由新上下文实施者承担,根智能体验收实际 diff、记录证据及提交;共享 schema/公开合同先串行冻结,文件和语义不独立的工作不并行。
|
||||
|
||||
| 工作项 | 依赖与范围 | 完成条件 |
|
||||
|---|---|---|
|
||||
| U3-1 资料/状态/凭据来源模型 | Backend store、migrate、compat、account events;核对最新迁移编号 | 双档空值/CAS/重启;固定数据模型和安全兼容门槛,原身份不变 |
|
||||
| U3-2 当前认证及停用 | U3-1;auth、accounts、sessions、Host、runmgr、SSE | 事务锁/CAS及最终认证检查;最后管理者;停用/启用不复活已撤能力;关键拒绝路径先写红测试 |
|
||||
| U3-3 受控目录提供者 | U3-1/2;accounts service、hostcaps、extension、registry、surface/version | 授权/字段/范围先于查询,exact lookup、签名分页、EXT-19收口;真实 provider 测试 |
|
||||
| U3-4 恢复与旧入口收口 | U3-1/2;store recovery、server HTTP、CLI/svc 帮助;新 pending initializer/续办与首管窄路径 | 管理员绕行全拒;可信本人正例完整链;待初始化稳定ID续办;历史强制改密current分支;旧能力拒绝和失败原子性;双连接竞争 |
|
||||
| U3-5 RDM 最小消费 | U3-3;RDM public pins、注册回调、accountdir、members、chapter/doc/batch duty shared service | 缺能力拒;候选范围在提供者内;提交重验;保留 I1项目/三权规则;新提供者真实组合测试 |
|
||||
| U3-6 既有界面及契约记录 | U3-3/4/5;Frontend api、UsersPanel、本人既有设置入口/恢复确认、RDM selector;Documents/Dev svc 合同 | 删除密钥复制/旧重置承诺,建号无密码/管理员位;本人资料与确认闭合;提示待 owner 初始化/无证明;未知结果查状态及本人显式更换;按服务端模式要求current;错误与过期响应不覆盖新状态 |
|
||||
| U3-7 固定组合验证和阶段出口 | 全部;Backend/RDM/Frontend/Documents PR 与 Dev pins、任务卡 | SQLite/PG、组合/build、浏览器及独立 Review;用户阶段6/7评审,当前HEAD CI 后智能体合入;运行/部署状态另列 |
|
||||
|
||||
U3-1 是共享前置;U3-2 冻结账号栅栏后目录/恢复才有稳定基础。目录与恢复如仍碰 auth/schema/extension 由根智能体串行整合;仅独立测试或文件在已提交合同后并行。Frontend UI 修改前重新按 DESIGN.md 核对,消费既有令牌、motion、Collapsible,不新增完整管理中心。
|
||||
|
||||
阶段 1–3 承接已批准统一方案的 U3 需求和本轮启动授权,不重走未受影响的用户意图。本文涉及公开契约、数据/证明及兼容切换细化,需阶段 4–5 审阅本版本后进入实施;阶段 6/7 保留人工出口,阶段 8/9 按仓规由智能体完成。不得用测试通过代签人工评审。
|
||||
|
||||
## 10. 验证计划与证据
|
||||
|
||||
所有下列测试当前 `NOT_RUN`。优先更新已有错误合同测试,观察期望红失败,再实现;不复制仅镜像实现的测试。测试对象为隔离临时数据,真实模型/线上账号不在本轮验证范围。
|
||||
|
||||
| 验证组 | 关键正常/拒绝/竞争场景 | 对象与方法 |
|
||||
|---|---|---|
|
||||
| 资料与身份 | 全空建号、填写清空、同名不同ID、格式/长度、未知/权限字段注入;同 expectedVersion 两写只有一胜者 | 真实 Store+HTTP,SQLite/PG 共用合同用例 |
|
||||
| 目录与隐私 | self/system/member/duty权限矩阵;仅禁用电话命中的哨兵不改变结果/数量/游标;空词、限额、过期/跨范围cursor、未知过滤拒绝 | 真实 Backend provider;字段授权回调失败;序列化输出和错误无秘密 |
|
||||
| 当前账号 | 密码/full/restricted/APIkey/旧Host/HITL/run/SSE;停用与建会话/兑换/最终写交错;启用不复活已撤对象;最后两管理者并发 | 两个独立 Store/连接;可控 barrier;双档,不靠 SQLite 单连接证明 PG |
|
||||
| 恢复正例 | 网页建号→可信owner按原ID初始化,activation过期/交付失败/已兑换未设密可续办,首次完成后initializer拒绝;可信 owner 初始化→正式密码会话→管理员 metadata 提案→本人确认→一次受限兑换→设密→正式重新登录,身份和业务数据不变 | 真实HTTP、同源cookie;两HTTP实例共享隔离PG;无 mock 身份认证 |
|
||||
| 恢复负例 | 初始密码/旧Key/reset 派生full、unknown lineage、APIkey/auth-off/提案后会话、错误owner、Bearer混合、管理员代确认均拒;资料不改proof | 实际旧/新HTTP链+CLI拒绝,确认前后数据库无不应发生的变更 |
|
||||
| 原子与恢复 | confirm/revoke、redeem/reissue、completion/disable/activechange、登录insert/改密强制交错;100并发至多一有效能力;audit/commit失败回滚;响应丢失无管理员补领 | SQLite同文件双Store/PG同schema双连接;显式测试日志证实 PG 场景确实执行 |
|
||||
| RDM 消费 | 项目管理先授权后lookup;不存在/disabled/provider缺失报错;章节/整文/批量三权拒失效候选;职责仍要求成员;正例I1隔离/cap保持 | 固定新Backend/RDM的实际注册提供者+命令/HTTP,不只 mock Host |
|
||||
| UI 与兼容 | 管理员不见明文;全空资料建号;本人确认/无证明拒绝;确认/兑换响应丢失查状态、显式更换、失效退出;grant_setup/current_required正确显示;同名明确选择;旧接口迁移提示;桌面/移动无错位,design guard | 真实浏览器;surface snapshot/version/导出边界;不开放额外用户流程 |
|
||||
| 迁移/组合 | 原ID保留;legacy Key restricted不能继续设密;既有密码强制态要求current且继承无proof来源;旧full无proof;disabled重启不复活;旧writer/不安全回退拒绝;组件固定pins+build | 隔离双档;Dev make test/build、test-pg及相关治理检查;当前HEADCI |
|
||||
|
||||
本地与作者工具结果为 SELF;独立审阅者重放须记录身份、命令、精确对象才是 REPLAY;当前 HEAD CI 有运行链接/受信执行身份才是 GATE。命令、对象SHA、完整脱敏输出由 `make evidence` 写入,任务卡/PR引用,不手抄测试通过。设计源码观察不冒充运行 FAIL 或 PASS。
|
||||
|
||||
## 11. 备选、代价及停止条件
|
||||
|
||||
主要备选是直接补资料、沿用旧目录和一次性 Key 交付。成本更低,但字段搜索仍可探测、管理员仍可接管目标账号,不满足已批准 IAM-D/IAM-C。另一个备选是本期同时建设外部身份/邮件/恢复码及 U4 授权权威,能改善无会话恢复,但扩大基础设施及迁移边界,当前不采用。
|
||||
|
||||
推荐本方案:一个账号权威、受控提供者、来源可追溯的本人会话、默认拒绝旧恢复旁路。代价是新账号需可信 owner 初始化,历史未知来源/丢失全部会话者不能线上恢复;最小搜索兼容也收紧,完整管理体验仍需 U7。方案批准不等于批准任何真实账号初始化或迁移。
|
||||
|
||||
遇到以下情况暂停受影响实施并修订:无法可靠判断 credential lineage 或默认拒绝旧能力;旧 writer/回退守卫不能落实;提供者必须依赖请求自报资格;新契约要求通用角色迁移、不可伪造插件本人proof、跨库 fencing 或外部身份证明;双档结果不一致;新的公共合同/权限设计需要改变本文取舍。已授权且不受影响工作可继续,不静默扩大范围或降低验收。
|
||||
|
||||
## 12. 交付出口
|
||||
|
||||
评审出口是本 r1 的明确兼容取舍及 U3 实施/测试范围。实施后分别给出需求到证据映射、剩余 U4–U9 差距、组件PR/固定组合、阶段6/7候选和独立Review记录。合入按有效 Review+当前HEADCI执行;生产上线、历史owner证明建立与工作区回收保持待授权;上线前须批准并演练§8的首次初始化/续办及异常恢复两份owner操作安排。
|
||||
|
||||
原 r1 仍是总方向,本补充是 U3 的具体设计对象。完成后主记录只更新真实推进状态,下一步优先核对 U4 的职位权威与现有在途工作,再提出具体任务建议;不得将本轮启动授权扩展到 U4。
|
||||
@@ -0,0 +1,40 @@
|
||||
# IAM U3 委任项目改名真实操作者围栏修订与实施计划 r1
|
||||
|
||||
状态:待阶段 4–5 人工重审;日期:2026-10-14。本文是已批准 U3 r1 及其 [A1–A4 增量修订](2026-10-14-iam-u3-stage6-review-amendment.md) 的**单点补充**。仅涉及 I1 已交付的委任项目改名投影在 U3 账号停用围栏下的真实操作者传递;不改变 RDM 项目职责规则,不启动 U4–U9,不授权 PR、合入、生产迁移、部署或真实账号操作。
|
||||
|
||||
## 1. 问题、对象与边界
|
||||
|
||||
独立只读复核在 Backend `4053c4d038b1d13a356e0196db56ab11c0e4f8e1` 后的 U3 未提交修复候选中发现:RDM `internal/rdmapp/system_projects.go` 已对请求主体 Bob 检查当前 `project.settings`,但委任 Bob 改创建者 Alice 的项目时,`internal/rdmapp/host_projection.go` 调用 `extension.NewScopedHost(Alice, "rdm")` 重建创建者 Host;Backend `internal/app/project_lifecycle.go` 的 `RenameOwnedProject` 将 Host owner Alice 同时作为最终 Store 事务 actor。若 Bob 通过 RDM 的最后授权检查后被停用,Backend 的 `projects` 表仍可在停用提交后由 Alice 身份完成改名。RDM 与 Backend 之间的授权快照窗口依旧不可原子关闭;**此次可关闭的是 Backend 同一账号数据库内 Bob 有效态与项目投影写入的窗口**,不能把后者归入已批准的跨库限制。
|
||||
|
||||
需求不变:项目创建者决定项目权限;获其委任且当前具有 `project.settings` 的成员可改名;系统管理员本身不因此获得项目内容或改名权。委任操作者停用后,已在途的物理投影不得在 Backend 最终事务中成功;创建者停用亦不应由旧 Host 句柄继续投影。项目 ID、创建者 owner 和委任 actor 是三个不同字段,不能互相代用。I1 的持久失败/恢复记录仍是跨库失败出口,不能假定两库提交原子性。
|
||||
|
||||
## 2. 推荐契约与备选
|
||||
|
||||
推荐新增**由当前受信请求 Host 派生**的可选项目投影能力,例如 `extension.DelegatedProjectProjectionHost.ProjectProjectionForCreator(creator, projectID) (ProjectHost, error)`。RDM 的 HTTP 生命周期回调和 `rdm.project.rename` 命令处理器都把当前认证主体绑定到各自传入的 `requestHost`;两条入口都必须从**该次收到的 Host**派生,不能另造操作者身份。派生句柄继承操作者 Bob、仅把资源 owner 收窄为 Alice。方法不接收调用方自填的 `operator`,也不复用按任意 owner 重建的全局 `NewScopedHost` 作为委任写入口。普通 `NewScopedHost` 继续服务既有读取/恢复能力,但不得替代新委任写入口。能力只向已注册的 RDM 项目生命周期/命令提供者开放;缺少能力、绑定不一致、项目不存在或插件来源不符时拒绝,不回退为创建者身份执行。
|
||||
|
||||
Backend 派生时核对原 Host 的操作者、插件来源、目标项目 ID 与持久创建者;RDM 仍按现有顺序检查 Bob 的当前 `project.settings`、项目 HostBinding 和改名意图。`RenameOwnedProject` 将**真实 actor Bob**、**创建者 owner Alice**、目标 ID 与名称分开送入 Store。最终 Store 事务按账号 owner 稳定排序锁定 Bob 与 Alice 并读取两者有效态,再取项目名称锁、读取持久项目 owner、校验名称并更新同一 `projects` 行;创建者本人改名时只锁一次。停用先提交则拒绝改名,改名先提交则停用等待;账号锁不授予 Bob 项目权限,RDM `project.settings` 检查继续独立存在。`auth-off` 缺失本地默认 owner 的例外只对该 owner 生效,明确停用记录拒绝。
|
||||
|
||||
未采用的方案:在全局 `NewScopedHost` 增加可自填 actor 参数,无法从受信请求句柄证明该 actor 是本次操作者;只在 RDM 最后授权后再查一次账号,仍有检查到 Backend SQL 之间的同库晚到窗口;把委任投影从 A1 承诺中豁免,则保留已知的 Backend 同库晚到写,与获批的 A1 契约不符。
|
||||
|
||||
## 3. 数据流、失败恢复与兼容
|
||||
|
||||
1. HTTP 认证主体 Bob → Backend 生命周期回调或命令分发的 requestHost(Bob)→ RDM 当前 `project.settings` 判定与 HostBinding/意图校验。
|
||||
2. RDM 从**同一个** requestHost 派生 Alice 项目的投影能力;能力内部固定 `actor=Bob, owner=Alice, projectID=既定项目`,RDM 不提交自选 actor。
|
||||
3. Backend Store 在最终事务中锁定并复核两个账号、项目 owner 与名称后提交物理改名;RDM 随后按 I1 原流程完成自己的数据库投影和意图记录。
|
||||
4. 派生能力在改名意图建立前不可用时直接拒绝,RDM 数据保持原状;意图建立后的账号停用、权限撤回、超时或 Backend 写入失败按 I1 规则标记失败并保留恢复信息。重试需新的受信请求 Host 和原意图 actor,重新检查当前权限/账号;不可把旧创建者 ScopedHost 当作跳过 Bob 的恢复捷径。
|
||||
|
||||
这个修订增加一个可选公开 Go 能力,不改 HTTP 请求/响应、迁移表或现有 `ProjectHost` 方法集合;RDM 的测试替身需要显式实现派生能力。若正式 Backend 尚未发布该能力,RDM 委任改名须返回能力不可用,不能默默回退。Frontend 无需新控件;已有项目改名错误提示可显示拒绝。两个数据库之间以及 RDM 权限撤回后的瞬时窗口仍不能声称零窗口,按 I1 持久意图和人工核对规则处理。
|
||||
|
||||
## 4. 实施计划与可判定验证
|
||||
|
||||
1. 固定 Backend、RDM 和 Documents 候选 SHA;在 Backend `pkg/extension` 定义可选受信派生接口,在 `internal/app` 实现请求绑定、来源/项目核对。逐一追踪 HTTP 生命周期与 `rdm.project.rename` 命令两条入口;旧全局 ScopedHost 的委任写用法在两条改名路径全部替换,其它读取调用保持原状。
|
||||
2. Backend Store 增加两账号稳定排序的最终事务围栏,仅在项目投影入口使用;检查 Bob 与 Alice 有效态、持久项目 owner、全局名称唯一性和 rows affected。保持单账号项目创建路径与 U7 bootstrap 锁序;不得在 RDM 回调或模型/网络调用期间持有 Backend DB 事务。
|
||||
3. RDM 改名与失败重试从当前回调 Host 派生投影句柄,继续执行现有 `project.settings` 和 HostBinding/意图检查。缺少新能力时明确失败;已建立的意图按原规则保留,未建立意图的请求不凭空新增。补创建者本人、委任成员、停用创建者、错误绑定和权限撤回场景。
|
||||
4. 用可控屏障在 Backend 最终投影 SQL 前暂停,分别在隔离 SQLite 双 Store、PG 双连接/两进程停用 Bob,再继续,断言 Backend 项目名不变、无创建者身份回退;反向顺序验证先改名后停用。并发 Alice/Bob 锁顺序测试不得死锁。RDM 组合测试同时覆盖无会话 `rdm.project.rename` 命令和 HTTP PATCH:委任成员仅有 `project.settings`、无 `doc.read` 时均可改名;两入口的停用围栏、授权撤回与失败意图恢复均成立,不宣称跨库原子性。
|
||||
5. 重跑 Backend 全包/构建、隔离 PG、RDM 源码外 Go overlay、相关 Frontend 构建与浏览器路径;固定 SHA、命令和完整脱敏输出进入 Dev 任务卡 `SELF` 收据。随后重新提交阶段 6 人工评审;正式 pin、阶段 7、独立 Review/GATE、PR 与合入按既有前置分别处理。
|
||||
|
||||
停止条件:不能从受信请求 Host 得到真实操作者、测试发现 RDM 的回调来源并非可信身份、两账号锁序与 U7 冲突、或必须更改现有 `ProjectHost` 公开方法/持久数据设计时,先修订本文并重审,不以创建者代替 actor 继续交付。
|
||||
|
||||
## 5. 本次评审请求
|
||||
|
||||
请仅裁定本文件的阶段 4–5 契约与计划。批准后才实施上述新增接口与委任投影修复,并重跑受影响验证,再提交阶段 6 人工评审。本次不请求阶段 6 结论、阶段 7 验收、PR/合入、受信 tag、生产迁移或部署。
|
||||
@@ -0,0 +1,58 @@
|
||||
# IAM U3 阶段 6 审阅后契约修订与补测计划 r1
|
||||
|
||||
状态:待阶段 4–5 人工重审;日期:2026-10-14。本文是已批准 [U3 r1](2026-10-14-iam-u3-account-contract-and-plan.md) 的增量修订,不替换其已批准正文。对象是 U3 实施候选在合并 Backend U7 账号事务后暴露的四项缺口;U4–U9、生产迁移、部署和真实账户操作不在本次授权申请内。
|
||||
|
||||
## 1. 当前证据与为什么要重审
|
||||
|
||||
U3 r1 的实施候选为 Backend `4053c4d038b1d13a356e0196db56ab11c0e4f8e1`、RDM 源码 `ad815febce522d029a74cd90217409efc9a81c35`、Frontend `a961b19ebb793d6c02a72002050d269700b1a5bd`。Backend 在此后有**未提交的审阅修复候选**;它们不是已批准或已验收版本。Dev 任务卡 `feature/iam-u3-account-directory-recovery` 保存上述固定版本的 SELF 收据,RDM 任务卡保存源码外 Go overlay 的 SELF 收据。正式 pin、独立重放、GATE、生产与部署均 `NOT_RUN`。
|
||||
|
||||
独立只读代码审阅发现以下可复现缺口;现有绿色单测不推翻这些代码路径事实:
|
||||
|
||||
| ID | 触发与证据位置 | 对 U3 r1 的影响 |
|
||||
|---|---|---|
|
||||
| A1 在途写入 | `internal/server/server.go` 的 `createProject` 在读取请求体后调用 `Store.CreateProject`;`internal/store/projects.go` 插入时不读取当前账号。请求通过认证后暂停,另一连接停用账号,再继续请求,项目仍可在停用提交之后写入。其它非 GET 业务入口须同样盘点,不能只修这一条。 | 违反 r1 §7 Backend 有后果写入前检查有效态;同库可闭合的窗口尚未闭合。 |
|
||||
| A2 API key 隐式建号 | `internal/app/app.go` 的 `CreateAPIKey` 调用 `Store.CreateAPIKey`;后者 `EnsureUser` 可创建启用、来源为 legacy 的账号,再签发完整 API key。 | 绕过 r1 §3 待初始化账号及首凭据边界;纯集成账号的历史读取兼容与新增建号须分开。 |
|
||||
| A3 锁顺序 | 合并 U7 后普通建号走 account 锁再取 bootstrap 锁;首管及本地建号曾反向取得两锁,同 owner 并发有 PG 死锁风险。 | 影响首管与并发建号的活性及 U7 的准入保证。 |
|
||||
| A4 游标与可见版本 | `internal/app/account_directory.go` 和 `internal/server/account_search.go` 以进程内随机密钥签游标,负载均衡切到另一实例时下一页 409。受限候选的版本又组合了全局 public revision,域外账号改名也会使该域游标过期。 | 违反 r1 §5 多实例分页及“目录版本只受可见辨识资料/有效态变化影响”的范围;后一问题还可成为域外活动侧信道。 |
|
||||
|
||||
本次提出的处理改变持久数据设计(共享游标签名密钥)和账号入口兼容行为(阻断 API key 隐式建号),并需要把原计划的“写前复验”细化为同库提交围栏。按 Dev `policy/delivery.md` 的“技术路线、模块职责、公开契约、数据或权限设计……修订方案或计划,经人工重审后继续”,阶段 6 评审暂缓,先审本文阶段 4–5 修订。
|
||||
|
||||
## 2. 修订后的契约
|
||||
|
||||
### A1:同库账号写入围栏
|
||||
|
||||
Backend 自有数据库内,每个以账号身份执行的有后果业务写入,必须在**最终写事务**内按统一顺序取得该 actor 的账号锁,读取当前 `enabled` 并在同事务提交业务变更。若目标资源另有 owner,仍由领域授权决定可写范围;账号锁不能授予项目或文档权限。停用事务取得相同账号锁并推进有效态,因此并发结果只能是业务写入先提交、停用后提交,或停用先提交、业务写入拒绝;不得出现停用提交后旧请求再写入成功。
|
||||
|
||||
实现采用显式 Store 事务入口和逐路由终点映射,不在请求全程持有额外数据库连接,也不以“handler 刚检查过”代替事务内复验。先盘点 Backend 当前 `66` 条非 GET API 路由,将凭据/资料写入(已有或补充最终事务复验)、Backend 自有业务写入、外部副作用/运行控制、只读或无业务后果动作分别记录。项目、会话消息、工件、设置、任务、工具/命令等适用的 Backend 自有写入改由同一账号锁和事务检查;文件或外部工具在真正执行前重验并使用既有取消机制,不能宣称跨外部系统原子性。`auth-off` 的本地默认 owner 继续按 r1 的嵌入式隐式身份规则处理;若存在明确停用记录则拒绝。独立 RDM 数据库的最终提交窗口沿用 r1 §7 已声明限制,不在 U3 声称零窗口跨库 fencing。
|
||||
|
||||
变更须给出“路由 → 最终 sink → actor/owner 来源 → 围栏方式 → 重放用例”清单;有未覆盖的有后果入口即阻止阶段 6 完成。对同一数据库中的认证版本、会话/API key 在途撤销窗口另作影响分析;若需要扩大本次安全承诺,先再修订本文,不用账号 enabled 检查冒充凭据撤销栅栏。
|
||||
|
||||
### A2:API key 不再建号
|
||||
|
||||
本地 `apikey create` 只对**已存在、当前启用、已完成初始化**的账号签发;迁移而来的 `legacy` 账号保留既有无密码集成 Key 兼容,但其 Key 不得作为 U3 本人恢复证明。待初始化账号、停用账号及不存在的 owner 拒绝,不执行 `EnsureUser`、不新增账号或 Key;现有 Key 不因本修订自动撤销。CLI 由当前机器/DB owner 边界执行,不能通过普通管理员 HTTP 或角色 grant 自助补领。未来新增纯集成账号须单列身份/授权方案,不能借 `apikey create` 的历史便利隐式建号。
|
||||
|
||||
Store 的旧 `CreateAPIKey` 目前被许多内部测试当建号 fixture 使用。生产调用入口先收紧;实施时应把内部 fixture 与正式签发 API 分离,并扫描所有非测试调用者,避免新调用重新打开隐式建号旁路。CLI 对不存在 owner 的返回由“创建并签发”改为明确拒绝,这是有意兼容收紧。
|
||||
|
||||
### A3:统一锁序
|
||||
|
||||
PG 账号事务统一按账号 owner 升序取得账号 advisory lock,再取得首次建号/末位管理员保护所需 bootstrap lock,然后锁用户行及后续 proposal、grant、session。SQLite 保持同等业务检查,依靠写事务串行。普通建号与首管竞争仍须保留 U7 的“空库同时入场仅一方成功”语义;不能通过改锁顺序让两方都成功。多 owner 操作按稳定排序,事务内重查 ID、版本和管理员资格。不得用死锁重试掩盖顺序倒置。
|
||||
|
||||
### A4:共享、分域且可见的游标
|
||||
|
||||
使用 256-bit CSPRNG 密钥,经共享账号数据库的 compare-and-set 在首次需要时原子持久化;保存密钥版本,绝不输出到 API、日志、任务卡或浏览器。管理搜索与领域目录以不同 HMAC 域签名,游标绑定密钥版本及 r1 原定的 actor、purpose、locator、范围、匹配字段、查询、过滤、可见目录版本。多实例从同一数据库读取同一密钥;密钥读取失败拒绝分页,旧进程本地随机游标切换时返回明确冲突并要求重新查询,不静默换页。
|
||||
|
||||
`AllCandidates` 的公共辨识变化可以使用全局公开 revision;有限候选范围的版本应由**当前可见候选集合**内的稳定 ID、有效态与获准辨识字段及领域 `ScopeVersion` 形成,不加入域外账号或未授权字段。快照读取与游标版本计算需在一致的数据库视图内完成,或在返回前复核版本并拒绝过期结果,避免版本与页面来自不同时间点。资料私密字段编辑不改变不获准字段的目录版本;当前允许披露的 `staffNo` 独立纳入版本。候选名单变化须失效旧游标。
|
||||
|
||||
若权威数据库已经实际应用 U3 v32,不能就地修改其迁移正文,必须追加新的迁移并给出旧游标废止步骤;只有证明 v32 尚未进入任何持久实例,才可在未发布候选内修订 v32。U7 `account_audit` 占 v31,U3 账号基础/恢复仍按已合并候选的 v32/v33 排序。
|
||||
|
||||
## 3. 实施与验证顺序
|
||||
|
||||
1. 固定当前主干与候选 SHA,盘点 66 条非 GET 路由、Host/runmgr 出口及账号写入者;给每条有后果 sink 指定最终有效态围栏和测试。完成 A2 生产入口及内部 fixture 分离,补“不存在/pending/disabled 均不建号或签 Key”双档测试。
|
||||
2. 按 A3 统一 PG 锁序。用两个独立连接/进程和可控屏障覆盖首管对普通建号、同 owner 建号、管理员停用对初始化/改密;验证无死锁、唯一成功与事务审计。
|
||||
3. 落 A4 密钥持久化、域分隔和范围内版本;补 SQLite 两 Store、PG 两实例跨进程翻页、字段/范围变动、域外改名、密钥不可见和故障拒绝测试;确认迁移版本前置事实。
|
||||
4. 按 A1 路由清单逐类实现最终写围栏。用暂停请求体和暂停最终 SQL 写入的确定性测试,在 SQLite、PG 双连接与 PG 两进程验证“停用先提交则无晚到写”;覆盖项目、消息、工件、设置等代表性入口及实际 RDM 限制说明。外部副作用单独验证最后检查与取消。
|
||||
5. 同步最新 main,重跑 Backend 全包、PG Store/Server、RDM 源码外组合与 Frontend 正常测试;对受影响真实浏览器路径重放。三仓正式 pin、GATE、独立 Review、阶段 7 验收仍遵守原计划的后续人工节点和受信发布前置。
|
||||
|
||||
## 4. 评审决定
|
||||
|
||||
请只裁定是否批准本修订的阶段 4–5,使 U3 阶段内可继续上述修复和隔离验证,然后**重新提交阶段 6 人工评审**。不请求现阶段批准阶段 6、阶段 7、PR 合入、受信版本 tag、真实机器/数据库账号操作、生产迁移或部署。若 A1 的全路由写围栏成本或 A2 的 CLI 兼容收紧不可接受,应在此处明确缩小验收承诺或另立增量;不得保留原 U3 §7 的完成声明同时留下已知晚到写。
|
||||
@@ -0,0 +1,393 @@
|
||||
# Focused Role and Permission System Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Deliver the requested single-effective-role position model and enforce system, role, and per-project permissions from one server-side action catalog.
|
||||
|
||||
**Architecture:** Backend owns roles, the single active user binding, the permission catalog, system grants, and role defaults/caps. RDM consumes that public authority while retaining project membership, project overrides, creator ownership, and existing document duties. Frontend uses Backend role APIs for the single-role and matrix views and uses RDM only for project-scoped configuration.
|
||||
|
||||
**Tech Stack:** Go 1.27, SQLite, PostgreSQL, HTTP JSON APIs, compiled extension Host contracts, React 19, TypeScript, Vitest, Playwright.
|
||||
|
||||
**Spec:** `docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- One user has at most one effective role; one role may be assigned to many users.
|
||||
- Role and user stable IDs never change when display data changes.
|
||||
- System administrators do not gain implicit access to project business content.
|
||||
- A project creator can manage only the project they created.
|
||||
- Unknown actions, missing providers, stale versions, disabled accounts, and disabled roles fail closed.
|
||||
- Role defaults never exceed role caps; project allows never exceed role or platform caps.
|
||||
- UI configuration uses a draft, preview, and atomic batch commit; hidden rows do not lose draft edits.
|
||||
- Account recovery, multi-role bindings, Agent permissions, workflow redesign, production migration, and deployment remain outside this plan.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- Reassigning a user while two administrators submit concurrently must leave exactly one active binding and return one conflict.
|
||||
- Disabling a role between preview and commit must reject the commit without partial writes.
|
||||
- A project creator switching from project A to B must not retain A-derived management authority in B.
|
||||
- A filtered matrix batch must contain explicit action IDs and must not apply to hidden or newly registered actions.
|
||||
- RDM provider absence or version mismatch must deny authorization and must not fall back to the legacy local role table.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Backend action catalog and role schema
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/catalog.go`
|
||||
- Create: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Modify: `Agent-System-Backend/internal/store/migrate.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_migration_test.go`
|
||||
- Test: `Agent-System-Backend/internal/authz/catalog_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `authz.Descriptor`, `authz.DescriptorByID(string) (Descriptor, bool)`, `store.RoleRecord`, `store.RoleBinding`, and schema v35 tables.
|
||||
- Consumes: existing `users(id, owner, enabled, state_version)` and migration transaction machinery.
|
||||
|
||||
- [ ] **Step 1: Add failing catalog tests**
|
||||
|
||||
```go
|
||||
func TestCatalogRejectsUnknownAndProtectsManagementActions(t *testing.T) {
|
||||
if _, ok := DescriptorByID("invented.action"); ok { t.Fatal("unknown action registered") }
|
||||
got, ok := DescriptorByID("system.roles.manage")
|
||||
if !ok || got.Scope != ScopeSystem || !got.Protected { t.Fatalf("descriptor=%+v", got) }
|
||||
create, ok := DescriptorByID("project.create")
|
||||
if !ok || create.Scope != ScopeSystem || create.Protected { t.Fatalf("project.create=%+v", create) }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused test and confirm the missing package failure**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz -run TestCatalogRejectsUnknownAndProtectsManagementActions -count=1`
|
||||
Expected: FAIL because `internal/authz` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the fixed action catalog**
|
||||
|
||||
```go
|
||||
type Scope string
|
||||
const ( ScopeSystem Scope = "system"; ScopeProject Scope = "project" )
|
||||
type Descriptor struct { ActionID, Label, Description, Group string; Scope Scope; Protected bool }
|
||||
func DescriptorByID(id string) (Descriptor, bool)
|
||||
func Catalog() []Descriptor
|
||||
```
|
||||
|
||||
Register only the system and project actions listed in the spec. Return copies so callers cannot mutate the registry.
|
||||
|
||||
- [ ] **Step 4: Add schema v35 and migration tests for both dialects**
|
||||
|
||||
Create `roles`, `role_permissions`, `user_role_bindings`, `system_grants`, `authority_state`, and `policy_receipts`. Enforce one active binding per user with a unique `user_id` key; preserve inactive history through versioned replacement rows in the same transaction. Use the existing boolean representation per dialect and foreign keys to `users(id)`.
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestRolePolicyMigration|TestMigrate' -count=1`
|
||||
Expected: PASS on SQLite; the existing PG migration suite later verifies PostgreSQL.
|
||||
|
||||
- [ ] **Step 5: Commit the schema and catalog**
|
||||
|
||||
```bash
|
||||
git add internal/authz/catalog.go internal/authz/catalog_test.go internal/store/role_policy.go internal/store/role_policy_migration_test.go internal/store/migrate.go
|
||||
git commit -m "Add the role permission catalog and schema"
|
||||
```
|
||||
|
||||
### Task 2: Atomic role, binding, and permission store
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_test.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_pg_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `authz.DescriptorByID`, current account state, store transaction helpers.
|
||||
- Produces:
|
||||
- `CreateRole(actor string, in RoleCreate) (RoleRecord, error)`
|
||||
- `PatchRole(actor, roleID string, expected int64, patch RolePatch) (RoleRecord, error)`
|
||||
- `ReplaceRolePermissions(actor, roleID string, expected int64, changes []RolePermissionChange, key string) (PolicyReceipt, error)`
|
||||
- `AssignUserRole(actor, userID, roleID string, expectedState int64, key string) (RoleBinding, error)`
|
||||
- `RoleForUser(userID string) (RoleBindingView, error)`
|
||||
- `ReplaceSystemGrants(actor, userID string, expectedEpoch int64, actions []string, key string) (PolicyReceipt, error)`
|
||||
|
||||
- [ ] **Step 1: Write failing transaction and concurrency tests**
|
||||
|
||||
```go
|
||||
func TestAssignUserRoleKeepsOneEffectiveBinding(t *testing.T) {
|
||||
first := mustAssign(t, st, admin, userID, engineerID, 1, "assign-1")
|
||||
second := mustAssign(t, st, admin, userID, reviewerID, first.UserStateVersion, "assign-2")
|
||||
if second.RoleID != reviewerID { t.Fatalf("binding=%+v", second) }
|
||||
if got := countActiveBindings(t, st, userID); got != 1 { t.Fatalf("active=%d", got) }
|
||||
}
|
||||
```
|
||||
|
||||
Also test two concurrent replacements with the same expected state, duplicate idempotency keys, disabled users, disabled roles, unknown actions, `allowed=true/capAllowed=false`, stale role versions, a multi-change batch containing one invalid item, and rejection when a system-grant batch removes the final effective `system.permissions.manage` holder.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm missing methods fail**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestAssignUserRole|TestReplaceRolePermissions' -count=1`
|
||||
Expected: FAIL with undefined store methods.
|
||||
|
||||
- [ ] **Step 3: Implement transaction-scoped mutations**
|
||||
|
||||
Lock the actor, target account, role, and `authority_state` in the existing account-before-policy lock order. Validate the complete batch before any write. Increment `role_version` and `authority_epoch` once per successful transaction and persist one `policy_receipts` row per idempotency key.
|
||||
|
||||
- [ ] **Step 4: Verify SQLite and PostgreSQL behavior**
|
||||
|
||||
Run SQLite: `GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Run PostgreSQL through the isolated PG harness after it creates the mode-0600 DSN file: `AGENT_SYSTEM_TEST_PG_DSN_FILE=/tmp/iam-u4-role-policy-pg-dsn GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Expected: both pass with identical conflict and rollback assertions.
|
||||
|
||||
- [ ] **Step 5: Commit the store service**
|
||||
|
||||
```bash
|
||||
git add internal/store/role_policy.go internal/store/role_policy_test.go internal/store/role_policy_pg_test.go
|
||||
git commit -m "Add atomic role and binding policies"
|
||||
```
|
||||
|
||||
### Task 3: Backend authorization service and HTTP API
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/service.go`
|
||||
- Create: `Agent-System-Backend/internal/server/roles.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/server.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/accounts.go`
|
||||
- Test: `Agent-System-Backend/internal/server/roles_test.go`
|
||||
- Test: `Agent-System-Backend/internal/server/role_authorization_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 2 store methods and existing authenticated actor resolution.
|
||||
- Produces: the HTTP endpoints in the spec and `Service.AuthorizeSystem(actor, actionID string) Decision`.
|
||||
|
||||
- [ ] **Step 1: Write failing authorization and HTTP tests**
|
||||
|
||||
```go
|
||||
func TestRoleManagerCannotGrantProtectedPermission(t *testing.T) {
|
||||
cookie := loginAs(t, h, "role-manager")
|
||||
rec := do(h, "PUT", "/api/admin/roles/"+roleID+"/permissions",
|
||||
`{"expectedRoleVersion":1,"idempotencyKey":"p1","changes":[{"actionId":"system.permissions.manage","allowed":true,"capAllowed":true}]}`, cookie)
|
||||
if rec.Code != http.StatusForbidden { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) }
|
||||
}
|
||||
```
|
||||
|
||||
Cover role description create/edit, exact and fuzzy role search, status/action filtering, pagination, one role assigned to two users, user reassignment, stale versions, fixed-ID batches, disabled role assignment, system-grant listing/replacement, final permission-manager protection, and account profile fields remaining outside authorization.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm routes are absent**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/server -run 'Role|PermissionCatalog|UserRole' -count=1`
|
||||
Expected: FAIL with 404 or missing handlers.
|
||||
|
||||
- [ ] **Step 3: Implement service authorization and handlers**
|
||||
|
||||
Protected actions require explicit `system_grants`; `project.create` may also use the current enabled role permission. Provide `GET/PUT /api/admin/users/{userId}/system-grants` with expected epoch and idempotency key. Replace `requireAdmin` only for the new role endpoints in this task; existing unrelated admin endpoints remain unchanged until their explicit action is wired in Task 4.
|
||||
|
||||
- [ ] **Step 4: Wire account management actions without changing recovery**
|
||||
|
||||
Map account list/create/profile/state handlers to `system.users.view/create/edit/disable`. Do not touch recovery routes or credential behavior. Preserve first-admin bootstrap as the protected root recovery path.
|
||||
|
||||
- [ ] **Step 5: Run server and full Backend tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz ./internal/server ./internal/store -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit the Backend API**
|
||||
|
||||
```bash
|
||||
git add internal/authz/service.go internal/server/roles.go internal/server/server.go internal/server/accounts.go internal/server/roles_test.go internal/server/role_authorization_test.go
|
||||
git commit -m "Expose role and system permission APIs"
|
||||
```
|
||||
|
||||
### Task 4: Public role provider for RDM
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/pkg/extension/extension.go`
|
||||
- Modify: `Agent-System-Backend/internal/app/hostcaps.go`
|
||||
- Test: `Agent-System-Backend/internal/app/role_provider_test.go`
|
||||
- Create: `Agent-System-RDM/internal/perms/role_provider.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/role_provider_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces public `extension.RolePolicyProvider` methods `RolePolicyForActor(actor string)`, `RolePolicyForUser(userID string)`, and `CheckRoleAction(userID, actionID string)` returning versioned, non-secret snapshots.
|
||||
- Consumes the actual request actor from the trusted Host; caller-supplied actor fields never select authority.
|
||||
|
||||
- [ ] **Step 1: Add provider contract tests on both sides**
|
||||
|
||||
Backend tests verify stable user IDs, enabled state, role version, action default/cap, authority epoch, and rejection when the Host has no authenticated actor. RDM tests verify provider missing, wrong version, disabled role, and unknown action all deny without consulting the legacy tables.
|
||||
|
||||
- [ ] **Step 2: Run the contract tests red**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./internal/app -run RoleProvider -count=1`
|
||||
Run RDM: `GOWORK=off go test ./internal/rdmapp -run RoleProvider -count=1`
|
||||
Expected: FAIL with missing public provider.
|
||||
|
||||
- [ ] **Step 3: Implement the public interface and RDM adapter**
|
||||
|
||||
The provider returns only role and permission metadata needed for authorization. It must not return user contact fields. RDM's evaluator must use the provider whenever a role binding exists; absence is a denial after migration readiness is enabled.
|
||||
|
||||
- [ ] **Step 4: Run both component suites**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./... -count=1`
|
||||
Run RDM with the reviewed Backend module overlay during development: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS; overlay evidence remains SELF until a trusted Backend tag is published.
|
||||
|
||||
- [ ] **Step 5: Commit each repository separately**
|
||||
|
||||
Backend commit: `Expose the role policy provider`.
|
||||
RDM commit: `Consume the Backend role authority`.
|
||||
|
||||
### Task 5: RDM migration adapter and creator-only project management
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-RDM/internal/perms/role_migration.go`
|
||||
- Modify: `Agent-System-RDM/internal/perms/roles.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Modify: `Agent-System-RDM/plugins/rdm/plugin.go`
|
||||
- Test: `Agent-System-RDM/internal/perms/role_migration_test.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/project_creator_permissions_test.go`
|
||||
- Test: `Agent-System-RDM/plugins/rdm/roles_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 4 provider and Backend import endpoint/command.
|
||||
- Produces a versioned export manifest for existing RDM roles and a compatibility forwarding layer for `rdm.role.*` writes.
|
||||
|
||||
- [ ] **Step 1: Write migration and cross-project denial tests**
|
||||
|
||||
```go
|
||||
func TestCreatorCannotManageAnotherCreatorsProject(t *testing.T) {
|
||||
projectA := createProjectAs(t, "alice")
|
||||
projectB := createProjectAs(t, "bob")
|
||||
requireAllowed(t, setProjectPermission("alice", projectA, "carol", ActDocRead, "allow"))
|
||||
requireDenied(t, setProjectPermission("alice", projectB, "carol", ActDocRead, "allow"))
|
||||
}
|
||||
```
|
||||
|
||||
Also assert role IDs, descriptions, bindings, defaults, caps, disabled state, and counts survive export/import; once provider readiness is set, legacy local writes and fallback reads are rejected.
|
||||
|
||||
- [ ] **Step 2: Implement export and forwarding without production migration**
|
||||
|
||||
Add dry-run export and compare functions. Forward `rdm.role.create/action/assign/disable/list` to Backend through the trusted Host. Keep old tables read-only for verification; do not delete them.
|
||||
|
||||
- [ ] **Step 3: Tighten project management to immutable creator ownership**
|
||||
|
||||
For member, permission, and duty configuration, load the actual project and compare its immutable `created_by` to the authenticated actor before applying role and platform caps. Do not add delegated project-manager behavior.
|
||||
|
||||
- [ ] **Step 4: Run RDM tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/perms ./internal/rdmapp ./plugins/rdm -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Commit the RDM consumer**
|
||||
|
||||
```bash
|
||||
git add internal/perms/role_migration.go internal/perms/roles.go internal/rdmapp/perms_api.go plugins/rdm/plugin.go internal/perms/role_migration_test.go internal/rdmapp/project_creator_permissions_test.go plugins/rdm/roles_test.go
|
||||
git commit -m "Move RDM role writes to the Backend authority"
|
||||
```
|
||||
|
||||
### Task 6: Frontend role API and shared draft model
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Frontend/src/api.ts`
|
||||
- Create: `Agent-System-Frontend/src/rolePermissions.ts`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Backend HTTP endpoints from Task 3.
|
||||
- Produces `RoleRecord`, `PermissionDescriptor`, `RolePermissionDraft`, `mergeRoleDraft`, `visiblePatch`, and typed API methods.
|
||||
|
||||
- [ ] **Step 1: Write failing draft-model tests**
|
||||
|
||||
Test that filtered rows retain edits, `visiblePatch` includes only explicit action IDs, single-role and matrix edits produce identical changes, stale server refresh does not replace a newer draft generation, and `allowed=true/capAllowed=false` is rejected before submit.
|
||||
|
||||
- [ ] **Step 2: Implement the typed API and pure draft helpers**
|
||||
|
||||
```ts
|
||||
export type RolePermissionValue = { allowed: boolean; capAllowed: boolean }
|
||||
export type RolePermissionDraft = Record<string, RolePermissionValue>
|
||||
export function visiblePatch(draft: RolePermissionDraft, visibleActionIds: string[]): RolePermissionChange[]
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run tests and build**
|
||||
|
||||
Run: `npx vitest run test/role-permissions.test.ts`
|
||||
Run: `npm run build`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Commit the client contract**
|
||||
|
||||
```bash
|
||||
git add src/api.ts src/rolePermissions.ts test/role-permissions.test.ts
|
||||
git commit -m "Add the role permission client model"
|
||||
```
|
||||
|
||||
### Task 7: Single-role and matrix configuration UI
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Frontend/src/components/RolePermissionsPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AdminZone.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AccountUsersPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/styles.css`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions-ui.test.tsx`
|
||||
- Test: `Agent-System-Frontend/test/design-guard.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 6 API and draft helpers plus existing `@ui` components and tokens.
|
||||
- Produces the “角色与权限” system-management section and one-role assignment in user details.
|
||||
|
||||
- [ ] **Step 1: Re-read Dev `DESIGN.md` and add failing interaction tests**
|
||||
|
||||
Cover exact/fuzzy role search, state filtering, description edit, binding count, single-role edit, matrix edit, grouped paging, hidden-row draft retention, diff preview, conflict preservation, one user role replacement, system-grant editing with final-manager protection, and a late response discarded after switching roles.
|
||||
|
||||
- [ ] **Step 2: Implement the panel using existing design tokens**
|
||||
|
||||
Use `Collapsible` from `@ui`; consume `@ui/tokens.css` and `@ui/motion.css`; do not add raw colors, private keyframes, `<details>`, or inline font sizes. Keep one request sequence owner for list/detail/preview/commit results.
|
||||
|
||||
- [ ] **Step 3: Replace the RDM role admin entry**
|
||||
|
||||
Change the section label from “RDM 角色权限” to “角色与权限” and bind it to Backend APIs. Keep RDM project permission and document-duty components in project settings.
|
||||
|
||||
- [ ] **Step 4: Add the user role selector**
|
||||
|
||||
Display the current role and explicit system grants in account details. Allow role replacement only when the server advertises `system.roles.assign`; submit stable `userId`, `roleId`, expected state version, and idempotency key. Allow system-grant editing only with `system.permissions.manage`, using the server epoch and fixed action IDs.
|
||||
|
||||
- [ ] **Step 5: Run Frontend and design checks**
|
||||
|
||||
Run: `npm test`
|
||||
Run: `npm run build`
|
||||
Run paired UI: `npm test` in `Agent-System-UI`.
|
||||
Expected: all pass with no design-guard regression.
|
||||
|
||||
- [ ] **Step 6: Commit the UI**
|
||||
|
||||
```bash
|
||||
git add src/components/RolePermissionsPanel.tsx src/components/AdminZone.tsx src/components/AccountUsersPanel.tsx src/styles.css test/role-permissions-ui.test.tsx
|
||||
git commit -m "Add role and permission configuration views"
|
||||
```
|
||||
|
||||
### Task 8: Fixed-combination verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Dev/modules/backend`
|
||||
- Modify: `Agent-System-Dev/modules/rdm`
|
||||
- Modify: `Agent-System-Dev/modules/frontend`
|
||||
- Modify: `Agent-System-Dev/modules/ui`
|
||||
- Test evidence: `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes reviewed component commits and trusted published tags where required.
|
||||
- Produces one fixed Dev combination and requirement-to-evidence map.
|
||||
|
||||
- [ ] **Step 1: Run component verification on exact commits**
|
||||
|
||||
Backend: full SQLite, isolated PostgreSQL, migration rollback/retry, build.
|
||||
RDM: full tests/build against the reviewed Backend public version.
|
||||
Frontend/UI: full tests/build and design guards.
|
||||
|
||||
- [ ] **Step 2: Run a real browser scenario on an isolated database**
|
||||
|
||||
Create two ordinary users, one “软件工程师” role with a description, assign both users, replace one user's role, configure `project.create`, create projects A and B under different creators, and verify A's creator cannot manage B. Verify an unassigned system administrator cannot read either project's business content. Exercise exact/fuzzy search, filters, single-role edit, matrix edit, preview, conflict, and draft retention.
|
||||
|
||||
- [ ] **Step 3: Run the Dev combination**
|
||||
|
||||
Run: `make test`
|
||||
Expected: PASS on the exact component SHAs recorded by `make evidence`.
|
||||
|
||||
- [ ] **Step 4: Record remaining boundaries**
|
||||
|
||||
Mark production migration, deployment, account recovery, multi-role, Agent permissions, and workflow changes `NOT_RUN / out of scope`. Do not claim them from component tests.
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: 聚焦版角色与分层权限系统规格
|
||||
date: 2026-10-15
|
||||
type: feature
|
||||
status: approved
|
||||
approved_at: 2026-10-15
|
||||
supersedes_scope: 2026-10-13-identity-role-permission-plan.md U4-U7 中超出本文的部分
|
||||
---
|
||||
|
||||
# 聚焦版角色与分层权限系统规格
|
||||
|
||||
## 目标
|
||||
|
||||
实现用户要求的角色与权限系统,不引入账号恢复、多职位、Agent 授权或新的工作流能力。
|
||||
|
||||
首期角色等同企业职位。角色与用户分离:同一角色可分配给多个用户,每个用户同时只有一个生效角色。角色包含稳定 `roleId`、名称、可选描述、启用状态和版本。用户继续使用稳定 `userId`,姓名、电话、邮箱、工号、部门和备注均为可选资料,不参与授权主键。
|
||||
|
||||
权限分为三个配置面,但使用同一服务端动作目录和判定结果:
|
||||
|
||||
1. 系统权限控制系统设置、账号管理、角色管理、权限上限和项目创建。
|
||||
2. 角色权限描述一个职位默认可以执行的动作及其上限,例如创建项目、查看获准目录信息。
|
||||
3. 项目权限只在一个项目内生效,由该项目创建者维护;A 项目的管理权不能用于 B 项目。
|
||||
|
||||
系统管理员负责账号、角色和平台权限上限。系统管理员没有他人项目业务内容的隐式访问权,读取项目正文、原件、草稿、业务审计或导出仍需该项目的明确授权。
|
||||
|
||||
## 范围
|
||||
|
||||
### 角色与用户
|
||||
|
||||
- `Role`:`roleId`、名称、描述、状态、`roleVersion`、创建者、修改者和时间。
|
||||
- `UserRoleBinding`:`bindingId`、`userId`、`roleId`、状态、版本、任命者和任命时间。
|
||||
- 一个用户最多一条有效 binding;同一角色允许任意多个有效用户 binding。
|
||||
- 改名、编辑描述和停用角色不改变用户、项目或历史记录的稳定 ID。
|
||||
- 停用角色时保留 binding 和审计记录,但其角色授权立即失效;不回落到更宽的默认权限。
|
||||
- 任命停用角色、未知角色或停用账号均拒绝。
|
||||
|
||||
### 权限目录与三层授权
|
||||
|
||||
动作目录由受信注册表定义 `actionId`、中文名、说明、scope、分组、风险级别和是否受保护。请求不能通过动作字符串或请求体自行声明 scope。
|
||||
|
||||
首期系统动作至少包括:
|
||||
|
||||
| 分组 | 动作 |
|
||||
|---|---|
|
||||
| 系统设置 | `system.settings.read`、`system.settings.write` |
|
||||
| 用户管理 | `system.users.view`、`system.users.create`、`system.users.edit`、`system.users.disable` |
|
||||
| 角色管理 | `system.roles.view`、`system.roles.manage`、`system.roles.assign` |
|
||||
| 权限管理 | `system.permissions.view`、`system.permissions.manage` |
|
||||
| 项目入口 | `project.create` |
|
||||
|
||||
受保护的角色、权限和管理员管理动作只能来自明确系统授权,普通角色配置不能授予。`project.create` 可以来自明确系统授权或当前有效角色的允许项,但仍受平台上限约束。
|
||||
|
||||
项目动作至少包括 `project.settings`、`project.members.manage`、`project.permissions.manage`、`document.duties.assign`、`document.read`、`document.author`、`document.review`、`document.approve`。项目动作必须同时满足:账号有效、项目关系有效、当前角色有效、角色动作允许且未超过角色上限、平台未禁止、项目未明确拒绝、资源关系和文档状态允许。
|
||||
|
||||
项目创建者只获得自己项目的管理默认,且仍受平台和角色上限约束。创建者关系不授予其他项目权限,也不自动授予项目业务内容读取或代签能力。
|
||||
|
||||
### 角色权限
|
||||
|
||||
每个角色的动作配置包含 `allowed` 与 `capAllowed`:
|
||||
|
||||
- `allowed=true` 表示该角色默认允许动作。
|
||||
- `capAllowed=false` 表示该角色不能通过项目覆盖获得该动作。
|
||||
- `allowed=true && capAllowed=false` 是非法配置,服务端拒绝。
|
||||
- 未知动作和未配置动作默认拒绝。
|
||||
- 角色默认和上限不得写入超出操作者可委派范围的受保护动作。
|
||||
|
||||
首期一个用户只有一个有效角色,因此一次判定只使用该 binding,不进行跨角色动作或范围拼接。
|
||||
|
||||
### 项目权限和文档三权
|
||||
|
||||
RDM 保留项目成员、项目创建者、项目内覆盖和现有文档三权的领域权威。Backend 负责角色、binding、系统动作和平台/角色上限。
|
||||
|
||||
项目覆盖使用 `inherit | allow | deny`:`allow` 不能突破平台或角色上限,`deny` 明确阻断。项目创建者只能修改自己创建项目的成员、权限和文档三权。
|
||||
|
||||
文档编制、审核、批准继续使用现有 RDM 三权记录;分配候选必须是该项目有效成员,账号和角色有效,并具有对应动作上限。角色只提供资格,不能代替具体文档职责。
|
||||
|
||||
## 服务端接口
|
||||
|
||||
Backend 成为角色和系统权限的唯一写入权威,提供版本化接口:
|
||||
|
||||
- `GET /api/admin/permissions/catalog`
|
||||
- `GET /api/admin/roles`,支持名称精确/模糊搜索、状态和动作过滤、分页
|
||||
- `POST /api/admin/roles`
|
||||
- `GET /api/admin/roles/{roleId}`
|
||||
- `PATCH /api/admin/roles/{roleId}`,使用 `expectedRoleVersion`
|
||||
- `PUT /api/admin/roles/{roleId}/permissions`,批量提交固定 actionId 列表和 `expectedRoleVersion`
|
||||
- `PUT /api/admin/users/{userId}/role`,替换该用户唯一有效角色,使用用户状态版本和幂等键
|
||||
- `GET /api/admin/users/{userId}/role`
|
||||
- `GET /api/admin/users/{userId}/system-grants`
|
||||
- `PUT /api/admin/users/{userId}/system-grants`,批量提交明确系统动作并使用 `expectedAuthorityEpoch` 和幂等键
|
||||
- `GET /api/authz/explain`,仅解释调用者自己或其有权管理的对象
|
||||
|
||||
批量权限修改在单库事务内全部成功或全部失败;任一未知动作、越权、越上限或版本冲突不得部分写入。响应返回新版本、实际差异和审计回执标识。
|
||||
|
||||
RDM 通过公开、固定版本的 Backend 角色读取/判定契约消费角色数据。RDM 原 `rdm.role.*` 写命令在迁移期转发 Backend;切换完成后不得继续写 RDM 旧角色表。读取失败、提供者缺失或版本不兼容时拒绝权限动作。
|
||||
|
||||
## 配置界面
|
||||
|
||||
系统管理区增加“角色与权限”入口:
|
||||
|
||||
- 角色列表显示名称、描述、状态和绑定人数,支持精确搜索、模糊搜索和状态过滤。
|
||||
- 单角色视图编辑名称、描述、状态及权限。
|
||||
- 角色矩阵以角色为行、权限为列,按权限分组和分页展示。
|
||||
- 单角色视图与矩阵使用同一草稿模型和同一批量接口。
|
||||
- 过滤隐藏的单元格不丢失草稿;全选只作用于当前明确显示的固定 actionId 集合。
|
||||
- 提交前显示差异;冲突保留草稿并要求刷新,不逐格即时写库。
|
||||
|
||||
用户详情显示当前角色和明确系统授权来源,并允许有 `system.roles.assign` 权限的操作者从有效角色中选择。有 `system.permissions.manage` 权限的操作者可以编辑目标用户的系统授权,但不能撤销最后一个有效权限管理者。用户可选资料继续用于展示、搜索和辨识,不参与权限计算。
|
||||
|
||||
项目设置继续使用现有成员和权限入口,显示角色默认、角色上限、项目覆盖及最终允许/拒绝原因。项目创建者无法切换到其他项目后沿用原项目管理权。
|
||||
|
||||
## 迁移与兼容
|
||||
|
||||
1. 导出 RDM 当前角色、动作配置和账号单角色绑定,包含来源版本与摘要。
|
||||
2. 以原 `roleId` 导入 Backend;为每个账号生成一个稳定 binding。
|
||||
3. 对角色数量、绑定数量、动作默认/上限和引用逐项比对。
|
||||
4. 在迁移窗口内禁止双主写;RDM 写入口改为转发 Backend。
|
||||
5. 切换消费者后保留旧表只读,确认无回退依赖后再另行批准清理。
|
||||
|
||||
迁移失败不得启用部分角色数据;恢复时保留已撤销、停用和拒绝事实,不能复活旧权限。
|
||||
|
||||
## 验收条件
|
||||
|
||||
- 创建“软件工程师”角色并分配给两个用户,两人保留各自稳定 userId。
|
||||
- 同一用户第二次任命替换原生效角色,不产生两个同时有效的 binding。
|
||||
- 角色描述可编辑,用户可选资料全空仍合法。
|
||||
- 未知动作、默认超过上限、停用角色任命、无授权角色管理和最后系统权限管理者撤销均拒绝。
|
||||
- 普通角色可被授予 `project.create`,但不能因此修改系统设置、用户、角色或权限上限。
|
||||
- A 项目创建者管理 A 的成员、项目权限和文档三权成功;操作 B 全部拒绝。
|
||||
- 系统管理员未获项目授权时不能读取 A 或 B 的业务内容。
|
||||
- 单角色配置和角色矩阵产生等价的固定 actionId 批量 patch;过滤和分页不丢草稿。
|
||||
- SQLite 与 PostgreSQL 的版本冲突、事务回滚和迁移结果一致。
|
||||
|
||||
## 明确排除
|
||||
|
||||
本增量不实现一人多职、账号恢复、SSO/组织树、Agent 权限、工作流重建、新的文档审签流程、生产迁移或部署。现有文档三权只接入新的角色资格和项目权限判定。
|
||||
@@ -0,0 +1,368 @@
|
||||
---
|
||||
title: IAM U4 Stage 7-10 Delivery - Plan
|
||||
type: feat
|
||||
date: 2026-10-15
|
||||
artifact_contract: ce-unified-plan/v1
|
||||
product_contract_source: specialized-project-spec
|
||||
execution: code
|
||||
origin: docs/proposals/2026-10-15-iam-focused-role-permission-spec.md
|
||||
---
|
||||
|
||||
# IAM U4 Stage 7-10 Delivery - Plan
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
- **Objective:** The approved IAM U4 role and permission behavior is accepted in a real browser, merged through each protected repository at reviewed commits, and closed with traceable evidence and clean workspaces.
|
||||
- **Means:** Use the repository's existing Playwright stack against an isolated RDM product composition, then deliver Backend, Frontend, Documents, RDM, and Dev in dependency order with exact-head review and CI. (KTD1–KTD5)
|
||||
- **Authority:** The focused specification approved at Documents `e9ce9a5` owns behavior; this plan owns delivery mechanics; `Agent-System-Dev/policy/delivery.md` and `policy/merge-queue.md` own stage and merge gates; the task card owns current evidence.
|
||||
- **Execution profile:** The agent prepares and performs testing, review, PRs, conflict resolution, CI checks, merges, and cleanup. The user retains stage 7 and stage 10 decisions and personally publishes any trusted tag.
|
||||
- **Stop conditions:** Stop on a changed requirement, cross-project access success, stale or mismatched candidate SHA, unresolved browser or CI failure, a conflicting merge window, a mismatched existing tag, or any request to touch production data or deployment.
|
||||
|
||||
---
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Summary
|
||||
|
||||
Stage 7 proves the implemented role model and permission boundaries through the real React interface and live Backend/RDM authorization path.
|
||||
Stages 8–10 then carry the same behavior through component PRs, a trusted Backend release tag, RDM pins, the Dev fixed combination, protected-branch CI, and safe task closure.
|
||||
|
||||
### Problem Frame
|
||||
|
||||
The implementation candidate has passed SQLite, isolated PostgreSQL, RDM overlay, Frontend/UI, and Dev combination checks, but those checks do not prove browser behavior or protected-repository delivery.
|
||||
The repositories also move independently: RDM consumes a published Backend extension contract, while Dev pins Backend, Frontend, UI, and Documents but does not pin RDM.
|
||||
Delivery must preserve those dependency and evidence boundaries without turning production migration or deployment into an implied part of acceptance.
|
||||
|
||||
### Requirements
|
||||
|
||||
**Candidate identity and formal acceptance**
|
||||
|
||||
- DR1. Stage 7 starts only after each task branch is synchronized with its current `origin/main` and the tested component commits are recorded together.
|
||||
- DR2. Formal browser acceptance runs on a fresh isolated data root and random loopback ports, never on a registered or production service.
|
||||
- DR3. Browser evidence uses the existing Frontend Playwright stack against the exact Backend, RDM, Frontend, and UI sources under review.
|
||||
- DR4. Every acceptance result records the command, component SHAs, environment shape, exit code, sanitized screenshots or traces where applicable, and evidence source. Browser credentials are synthetic; artifacts use owner-restricted external storage and expire after stage 10.
|
||||
- DR5. Stage 7 requires user review of the requirement-to-evidence map before any component merge begins.
|
||||
|
||||
**Role, user, and permission behavior**
|
||||
|
||||
- DR6. The browser creates and edits a role with a description while its stable `roleId` remains unchanged. Before disabling a role, the UI names it, shows the bound-user count and authorization impact, supports cancellation without a request, and sends one versioned request only after confirmation; success preserves a usable selection, while conflict or failure preserves context for retry.
|
||||
- DR7. Two users can share one role, one user can be moved to another role, and the system never exposes two active roles for one user.
|
||||
- DR8. Name, phone, email, staff number, department, and notes remain optional and searchable without becoming authorization identifiers.
|
||||
- DR9. Exact and fuzzy role search, status and action filters, pagination, single-role editing, matrix default/cap editing, visible-page batch changes, preview, and 409 rebasing work through the rendered UI. Drafts are keyed by `roleId`: permission grouping/paging, role filters, role paging, and single/matrix switching preserve them; switching roles never transfers a draft; leaving the administration area or refreshing requires an explicit save/discard decision; a 409 rebases only the same role's draft.
|
||||
- DR10. Delegated system actions expose only their permitted user, role, or permission controls; a manager without view authority cannot replace data with an empty set. Loading, stale, empty, partial-failure, retry, and success states are distinct; destructive controls remain unavailable without authoritative data. The core flow works by keyboard with named controls, announced status changes, stable focus after save/conflict/error, and a usable narrow viewport including matrix navigation.
|
||||
- DR11. `project.create` works only through an explicit system grant or an enabled role default within its cap.
|
||||
- DR12. Project A's creator can manage A's members, overrides, and document duties, but A's creator relation and an untrusted owner label never authorize project B. A separate valid B-side membership, current role grant, cap, and project override continue to authorize only the B actions they explicitly allow.
|
||||
- DR13. System administrator identity alone cannot read another user's project business content, private conversations, originals, drafts, controlled document bodies, business audit records, or exports, and cannot create a business-content export artifact.
|
||||
- DR14. Document author, reviewer, and approver assignments require current account and role eligibility, and approval remains separate from document publication.
|
||||
- DR15. Missing providers, incompatible schemas, stale authority epochs, disabled accounts, disabled roles, unknown actions, and out-of-cap overrides fail closed. Version-conflict, unknown-action, and out-of-cap batch updates perform zero partial writes, with the same resulting state and migration outcome on SQLite and PostgreSQL.
|
||||
|
||||
**Protected delivery**
|
||||
|
||||
- DR16. Component branches merge in dependency order; a downstream pin never points to an unmerged commit or unpublished required tag.
|
||||
- DR17. Backend's trusted tag is created only by the user after the reviewed Backend PR merge commit is verified on `origin/main`; an existing tag is never rewritten.
|
||||
- DR18. RDM updates its pins only after Backend and Frontend commits are merged, then builds the product from committed Git objects or a strictly clean tracked/untracked/ignored source tree, records digests of the copied content as well as Git SHAs, and replays affected acceptance. Dirty or untracked source must fail before packaging.
|
||||
- DR19. Dev enters the manual merge queue only after all component dependencies are merged and the final gitlinks point to their authoritative commits.
|
||||
- DR20. Dev merge requires exact-head `test`, `test-pg`, and `svc-realcombo` GATE success, current Review, no conflicting main advance, and an explicit queue claim.
|
||||
- DR21. Task closure records the actual merge commits and retains every failed, skipped, and not-run result at its real evidence level. Any source, base, pin, candidate manifest, or test-definition change after stage 7 updates the manifest, replays affected evidence, and requires a renewed stage 7 conclusion before the first component merge.
|
||||
|
||||
### Actors
|
||||
|
||||
- A1. **System administrator:** manages platform accounts, roles, explicit system grants, and platform permission ceilings.
|
||||
- A2. **Delegated manager:** holds a selected subset of system actions and sees only the matching administration controls.
|
||||
- A3. **Project creator:** manages only the project identified by immutable creation metadata.
|
||||
- A4. **Project member:** receives project behavior from the current professional role, project override, resource relation, lifecycle state, and document duty.
|
||||
- A5. **Delivery agent:** executes tests, review, PRs, CI checks, merges, and cleanup within the approved scope.
|
||||
- A6. **User reviewer:** approves stage 7 evidence, personally publishes a trusted tag, and approves stage 10 cleanup.
|
||||
|
||||
### Key Decisions
|
||||
|
||||
- **One effective role remains the product model.** The delivery does not introduce multi-role assignment. Governs DR7.
|
||||
- **System administration does not grant business-content access.** Project and resource authorization remain explicit. Governs DR12–DR15.
|
||||
- **Production migration and deployment remain outside this delivery.** The migration machinery is tested only on isolated databases. Governs DR2, DR21.
|
||||
|
||||
### Key Flows
|
||||
|
||||
- F1. **Formal acceptance**
|
||||
- **Trigger:** The synchronized component candidate is fixed.
|
||||
- **Actors:** A1–A5.
|
||||
- **Steps:** Build the isolated composition; seed accounts and projects; drive role, user, system grant, project, and document flows in Chromium; capture traces and API decisions; replay SQLite, PostgreSQL, and component suites.
|
||||
- **Outcome:** DR6–DR15 have exact-candidate evidence or stage 7 stops.
|
||||
- **Covers:** DR1–DR15.
|
||||
|
||||
- F2. **Serial protected delivery**
|
||||
- **Trigger:** The user approves stage 7.
|
||||
- **Actors:** A5, A6, Gitea.
|
||||
- **Steps:** Merge Backend; publish and verify its tag; merge Frontend and Documents; update and merge RDM pins; fix the Dev gitlinks; claim the Dev queue window; pass exact-head CI; merge and verify; finish registered worktrees; close the task card through its own reviewed PR.
|
||||
- **Outcome:** Every protected branch contains the reviewed behavior and the task record identifies the actual commits.
|
||||
- **Covers:** DR16–DR21.
|
||||
|
||||
### Acceptance Examples
|
||||
|
||||
- AE1. **Shared position:** Given two enabled users and one enabled software-engineer role, assigning both users succeeds; replacing one user's role leaves one active binding for each user and the original role still has one member. Covers DR7.
|
||||
- AE2. **Fine-grained manager:** Given `system.users.view` without role or permission view, the manager opens user details without a failing role request and cannot edit roles or grants. Covers DR10.
|
||||
- AE3. **Draft conflict:** Given a local matrix draft and a concurrent role update, saving returns 409, reloads the latest role version, replays the local draft against it, and never applies role A's draft to role B. Covers DR9.
|
||||
- AE4. **Creator isolation:** Given projects A and B with different creators, A's creator can change A's members, overrides, and author/reviewer/approver duties. With no valid B-side management grant, the same actor is denied for all three mutation families in B and B remains unchanged, even if the actor is a B member or carries an untrusted owner label. A separate fixture proves that a valid B-side grant authorizes only its declared B actions. Covers DR12, DR14.
|
||||
- AE5. **Administrator privacy:** Given an administrator with no project content grant, direct authenticated requests to B's private conversations, originals, drafts, controlled bodies, business-audit list/detail, and export create/status/download routes are denied with no payload or export artifact. A separately authorized project actor supplies positive controls for the same routes. Covers DR13.
|
||||
- AE6. **Fail-closed refresh:** Given an allowed cached role snapshot, tightening the role cap changes the authority epoch; the next project or knowledge read consumes the current Backend snapshot and denies the old permission. Covers DR15.
|
||||
- AE7. **Migration rehearsal:** Given a provenance-recorded frozen v35 fixture and oracle covering active and disabled roles, inactive bindings, revoked grants, explicit denies, owner mappings, and orphan/reference failures, a trusted non-production harness invokes the real Backend/RDM stores to freeze legacy writes, stage stable IDs and full owner-to-role mappings, compare source hashes and expected results, and activate by epoch. Compare mismatch or a concurrent legacy write blocks activation; a lost success response can be retried without duplicate bindings. SQLite and PostgreSQL produce the same final state. Covers DR2, DR15, DR21.
|
||||
|
||||
### Scope Boundaries
|
||||
|
||||
Included:
|
||||
|
||||
- Repeatable UI and live-composition browser acceptance for the approved U4 requirements.
|
||||
- Test-only adjustments needed to make that acceptance deterministic and reviewable.
|
||||
- Component sync, PRs, CI, Backend tag handoff, RDM pin updates, Dev queue delivery, and task cleanup.
|
||||
|
||||
Excluded:
|
||||
|
||||
- Production schema migration, legacy-role cutover, deployment, or 8788 service changes.
|
||||
- Account recovery, password flows beyond isolated fixture setup, multi-role support, Agent permissions, organization trees, SSO, and workflow redesign.
|
||||
- Branch-protection changes, tag rewriting, runner changes, or merge-queue automation.
|
||||
|
||||
---
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### Key Technical Decisions
|
||||
|
||||
- KTD1. **Use repository-native Playwright as the formal browser gate.** `Agent-System-Frontend/playwright.config.ts` and the existing `e2e/` suite already define the project's real-browser contract. `agent-browser` may add an exploratory visible smoke when installed, but its absence does not replace or invalidate the repeatable Playwright run.
|
||||
- KTD2. **Run two browser layers.** Deterministic routed tests cover UI permission combinations, pagination, draft retention, and response ordering; a live isolated RDM product covers authentication, storage, Backend policy, RDM project boundaries, and embedded Frontend behavior.
|
||||
- KTD3. **Freeze evidence by component SHA and environment.** A branch sync, pin change, test-harness change, or target-main advance invalidates only the affected results; no receipt is copied to a different object.
|
||||
- KTD4. **Deliver dependencies serially.** Backend merges and receives a trusted tag before RDM updates its Backend pin. Frontend and Documents merge before RDM fixes its product pins. Dev pins only merged component commits and enters its final queue window last.
|
||||
- KTD5. **Keep high-consequence operations dual-keyed.** The agent prepares and verifies the proposed Backend tag command; the user executes tag publication. Production migration and deployment have no execution unit in this plan.
|
||||
- KTD6. **Treat missing component CI honestly.** Backend, RDM, and Dev consume their repository workflows. Frontend and Documents use their repository verification plus downstream Dev GATE when no repository-local workflow exists; they are not labeled GATE without a real platform run.
|
||||
|
||||
### High-Level Technical Design
|
||||
|
||||
The diagrams are delivery guidance rather than implementation specifications.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
BE[Backend reviewed commit] --> BET[User-published Backend tag]
|
||||
BET --> RP[RDM Backend pin]
|
||||
FE[Frontend reviewed commit] --> RP
|
||||
UI[UI commit already on main] --> RP
|
||||
DOC[Documents plan/spec commit] --> DEV[Dev final gitlinks]
|
||||
BE --> DEV
|
||||
FE --> DEV
|
||||
UI --> DEV
|
||||
RP --> RDM[RDM protected main]
|
||||
DEV --> DCI[Dev test + test-pg + svc-realcombo]
|
||||
DCI --> DM[Dev protected main]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Agent as Delivery agent
|
||||
participant Browser as Playwright Chromium
|
||||
participant Product as Isolated RDM product
|
||||
participant User as User reviewer
|
||||
participant Gitea as Protected repositories
|
||||
Agent->>Product: Build exact component composition
|
||||
Agent->>Browser: Run deterministic and live U4 scenarios
|
||||
Browser->>Product: Role, user, grant, project, and duty requests
|
||||
Product-->>Browser: Current policy decisions and rendered results
|
||||
Agent->>User: Stage 7 requirement-to-evidence map
|
||||
User-->>Agent: Stage 7 conclusion
|
||||
Agent->>Gitea: Review, CI, and serial component PR merges
|
||||
Agent->>User: Exact Backend merge commit and tag command
|
||||
User-->>Gitea: Publish trusted tag
|
||||
Agent->>Gitea: RDM pins, Dev queue CI, merge, and verification
|
||||
```
|
||||
|
||||
### Sequencing
|
||||
|
||||
1. Fix and accept the synchronized stage 7 candidate.
|
||||
2. Complete stage 7 browser and database evidence and obtain the user's conclusion.
|
||||
3. Re-review any files changed by acceptance work.
|
||||
4. Merge Backend, then complete the user-owned tag gate.
|
||||
5. Merge Frontend and Documents.
|
||||
6. Sync RDM, update pins, replay the product and browser gates, then merge RDM.
|
||||
7. Fix Dev gitlinks, claim the manual queue window, run exact-head CI, and merge Dev.
|
||||
8. Verify merged content, preserve receipts, clean registered worktrees, and then close the task card through its own reviewed PR after stage 10 approval.
|
||||
|
||||
### Risks and Dependencies
|
||||
|
||||
- Backend, RDM, Documents, and Dev main may advance before their windows. Each affected branch must sync once at its delivery boundary and rerun affected evidence.
|
||||
- RDM currently trails its authoritative main substantially. Conflict resolution must preserve upstream behavior and may invalidate the pre-sync RDM review.
|
||||
- Browser execution requires the repository Playwright runtime and Chromium. If the runtime cannot launch, stage 7 stops with the launch error; another browser stack is not substituted.
|
||||
- The recommended Backend version is `v0.6.0` because U4 adds public surface 2.15 and schema v36. If that remote tag exists or the release convention has changed, stop for a new user-selected version and never rewrite the ref.
|
||||
- Gitea API or Actions may fail transiently. A retry is allowed only after reading the actual PR/run state; unknown results are reconciled before resubmission.
|
||||
- The current Dev merge queue has no active window after the recorded release of the workbench visual task. This fact must be re-read immediately before U8 claims a new window.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Units
|
||||
|
||||
### U1. Record approval and fix the stage 7 candidate
|
||||
|
||||
- **Goal:** Record the user's stage 6 approval and produce one synchronized candidate manifest for formal acceptance.
|
||||
- **Requirements:** DR1, DR4, DR5.
|
||||
- **Files:** `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`, `Agent-System-Documents/docs/proposals/2026-10-15-iam-u4-stage7-10-delivery-plan.md`, `Agent-System-Documents/docs/proposals/INDEX.md`.
|
||||
- **Approach:** Update the stage table with the approved component versions and date. Run the required task sync in each branch before its evidence is treated as current. Record base, head, component commits, test definitions, and browser-harness commit in one candidate manifest.
|
||||
- **Test scenarios:** A clean same-base sync leaves the candidate unchanged; a moving target updates the manifest and invalidates affected evidence; a dirty or drifting component stops the unit.
|
||||
- **Verification:** Dev precheck/task-resume receipt, `make task-sync` evidence, recursive worktree verification, and Documents `git diff --check`.
|
||||
- **Dependencies:** None.
|
||||
|
||||
### U2. Add the repeatable IAM browser acceptance slice
|
||||
|
||||
- **Goal:** Make DR6–DR15 reproducible in the existing browser-test stack without production services.
|
||||
- **Requirements:** DR2–DR4, DR6–DR15.
|
||||
- **Files:** `Agent-System-Frontend/e2e/iam-role-authority.playwright.ts`, `Agent-System-RDM/scripts/iam-u4-role-acceptance.sh`, and only the smallest supporting test fixtures required by those files.
|
||||
- **Approach:** Add deterministic route-driven cases for fine-grained navigation, filters, matrix edits, role-scoped draft lifetime, 409 rebasing, pagination, late-response rejection, role-disable impact confirmation, and independently loading role/permission data. Cover keyboard operation, focus, accessible names and announcements, and a narrow viewport with horizontal matrix navigation. Add a live harness that materializes exact committed candidate sources into a temporary build root, builds the embedded RDM product, initializes a fresh SQLite database, starts on loopback random ports, and runs the same Playwright file in live mode. Synthetic credentials and activation material remain in the temporary root and are deleted on exit; traces and screenshots are sanitized before owner-restricted retention.
|
||||
- **Execution note:** Add failure-first cases for the previously reviewed permission combinations and race paths before changing the harness or UI assertions.
|
||||
- **Test scenarios:** Roles-only viewer; users-only viewer; permissions viewer without manager; permissions manager without view; shared-role assignment and reassignment; exact/fuzzy/action/status filters; multi-page role selection; matrix default/cap and visible-page batch edits; draft-preserving and explicit-discard transitions; concurrent 409 rebase; stale response after role switch; role-disable cancel/success/conflict/failure; initial loading; filtered and catalog-empty states; independent role-list and permission-catalog failure; failed create/save retry; keyboard-only core flow; narrow viewport; current epoch denial; isolated service startup failure and cleanup.
|
||||
- **Verification:** Focused Playwright run with trace retention, Frontend unit tests/build/design guard, harness shell check, and a clean-process check after success and failure.
|
||||
- **Dependencies:** U1.
|
||||
|
||||
### U3. Execute stage 7 formal acceptance
|
||||
|
||||
- **Goal:** Produce the requirement-to-evidence map the user can approve without reading implementation details.
|
||||
- **Requirements:** DR2–DR15.
|
||||
- **Files:** `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`; screenshots, traces, and receipts remain outside source and are linked by digest or durable run URL.
|
||||
- **Approach:** Run deterministic Playwright and the live isolated product. Replay Backend SQLite and isolated PostgreSQL role-policy tests, RDM full tests against the fixed Backend, Frontend/UI suites, and Dev combination tests. Exercise migration through an isolated cross-component harness that calls the real trusted Backend/RDM store path for freeze, stage, compare, activate, failure, and retry against the frozen v35 fixture and oracle. Record each DR-ID as PASS, FAIL, SKIP, or NOT_RUN against the exact candidate.
|
||||
- **Test scenarios:** AE1–AE7 plus missing provider, incompatible schema, disabled account/role, final permission-manager protection, project-override cap, document-duty cap, administrator audit/export denial, dirty product-source rejection, and SQLite/PostgreSQL batch failures for version conflict, unknown action, and out-of-cap change with zero partial writes.
|
||||
- **Verification:** Evidence receipts and browser artifacts match the candidate manifest; an independent replay covers the minimum critical set; the user records the stage 7 conclusion in the task card.
|
||||
- **Dependencies:** U2.
|
||||
|
||||
### U4. Re-review and prepare component PRs
|
||||
|
||||
- **Goal:** Turn the stage 7 candidate into current, reviewable component branches without changing accepted behavior.
|
||||
- **Requirements:** DR4, DR16, DR21.
|
||||
- **Files:** Component diffs and task records in Backend, Frontend, Documents, and RDM.
|
||||
- **Approach:** Sync each component with its current main at its own delivery boundary. Resolve conflicts without weakening authorization. If source, base, dependency pin, candidate manifest, or test definition changes, update the manifest, rerun the affected U2/U3 evidence, and obtain a renewed stage 7 conclusion before any component merge. Then simplify only changed code, conduct independent code review, push the branch, and create a draft PR with the final behavior and evidence.
|
||||
- **Test scenarios:** Clean sync; source conflict; changed public surface; changed Frontend API type; changed RDM provider contract; target advance after review.
|
||||
- **Verification:** Each PR identifies base/head, component dependencies, review findings and closure, and current applicable CI or explicit absence of repository-local CI.
|
||||
- **Dependencies:** U3 and stage 7 user approval.
|
||||
|
||||
### U5. Merge Backend and publish its trusted tag
|
||||
|
||||
- **Goal:** Establish the authoritative public role-policy contract RDM can consume.
|
||||
- **Requirements:** DR16, DR17.
|
||||
- **Files:** Backend PR and release tag; no production service files.
|
||||
- **Approach:** Confirm Backend PR HEAD contains current main, public surface 2.15, schema v36, staged migration, and the reviewed tests. Require the `vet-and-test` GATE on that exact HEAD, complete agent Review, merge without force, and verify the merge commit on `origin/main`. Prepare an annotated `v0.6.0` command bound to that merge commit; the user executes it. Read the remote peeled tag before proceeding.
|
||||
- **Test scenarios:** CI failure; main advance; merge conflict; tag absent; tag already exists at the same commit; tag exists at another commit; push result unknown.
|
||||
- **Verification:** PR merge URL, exact merge SHA, successful exact-head CI, remote annotated tag and peeled commit equality.
|
||||
- **Dependencies:** U4. The user-owned tag action is an internal completion gate after the Backend merge.
|
||||
|
||||
### U6. Merge Frontend and Documents
|
||||
|
||||
- **Goal:** Land the accepted administration UI and its authoritative records before RDM fixes product pins.
|
||||
- **Requirements:** DR3, DR6–DR10, DR16, DR21.
|
||||
- **Files:** Frontend PR, Documents PR, `Agent-System-Documents/docs/proposals/INDEX.md`.
|
||||
- **Approach:** Sync and revalidate Frontend, including the IAM Playwright slice, then merge the reviewed PR. Sync Documents, verify links and claims, and merge its PR. UI requires no U4 PR when `e295cae` remains on authoritative main.
|
||||
- **Test scenarios:** Frontend main changes affecting AdminZone, styles, API types, or test harness; Documents link drift; UI main no longer contains the expected token commit.
|
||||
- **Verification:** Frontend tests/build/Playwright and platform status, Documents `git diff --check`, merged commits on each main, and UI ancestry check.
|
||||
- **Dependencies:** U4; Backend may merge before or during this unit, but RDM pinning waits for both U5 and U6.
|
||||
|
||||
### U7. Update and merge the RDM composition
|
||||
|
||||
- **Goal:** Replace the development overlay with authoritative merged pins and prove the real product composition.
|
||||
- **Requirements:** DR12–DR18, DR21.
|
||||
- **Files:** `Agent-System-RDM/pins/agent-system-backend`, `pins/agent-system-frontend`, `pins/agent-system-ui`, public-contract references, product manifest, and RDM task evidence.
|
||||
- **Approach:** Sync the RDM branch with current main and resolve its known divergence. Pin Backend to the verified tagged merge commit, Frontend to its merged commit, and UI to current authoritative main. Remove reliance on the temporary `go.work` overlay. Harden product assembly so it materializes committed Git objects, or rejects any tracked, untracked, or relevant ignored drift before copying, and records copied-content digests in addition to Git SHAs. Run the full RDM suite, product build, product smoke, migration rehearsal, and live IAM Playwright acceptance; review the resulting pin diff and merge the RDM PR after `build-and-test` succeeds.
|
||||
- **Test scenarios:** Pin mismatch; tag/commit mismatch; tracked, untracked, or ignored source drift; copied-content digest mismatch; missing embedded assets; migration staging failure; source-hash mismatch; provider schema mismatch; browser regression after real pins.
|
||||
- **Verification:** `PRODUCT_MANIFEST.json` matches the three gitlinks, RDM HEAD, and actual copied-content digests; a dirty-source negative test fails before packaging; full local receipts and exact-head RDM CI pass; merged RDM main contains the reviewed pins.
|
||||
- **Dependencies:** U5, U6.
|
||||
|
||||
### U8. Deliver the final Dev combination through the manual queue
|
||||
|
||||
- **Goal:** Merge one Dev candidate that pins only authoritative component commits and carries current evidence.
|
||||
- **Requirements:** DR16, DR19, DR20, DR21.
|
||||
- **Files:** `Agent-System-Dev/modules/backend`, `modules/frontend`, `modules/ui`, `modules/documents`, task card, PR, and merge-queue issue #518.
|
||||
- **Approach:** Re-read #518 and claim the only Dev main window. Run `make task-sync`, update gitlinks to merged component commits, fix the candidate, and store final receipts outside source. Run local `make test` and `make build`, complete final independent Review, push the Dev branch, and require `test`, `test-pg`, and `svc-realcombo` on the exact PR HEAD. Recheck main/head immediately before a non-force PR merge, then read back main and the component tree.
|
||||
- **Test scenarios:** Active competing window; main advance after CI; component SHA drift; CI transient failure within retry budget; repeated failure; unknown merge response; merged main content mismatch.
|
||||
- **Verification:** Queue claim/release entries, current base/head, three successful GATE jobs, review receipt, PR merge URL, final main SHA, and exact `git ls-tree` component values.
|
||||
- **Dependencies:** U7 and merged component records from U5–U6.
|
||||
|
||||
### U9. Verify, close, and clean the task
|
||||
|
||||
- **Goal:** Leave authoritative records and no abandoned IAM workspaces or branches.
|
||||
- **Requirements:** DR21.
|
||||
- **Files:** Dev task card and archive entry; component task records where applicable.
|
||||
- **Approach:** Verify actual merged contents and component ancestry. Record browser, SQLite, PostgreSQL, component CI, Dev CI, tag, PR, and merge evidence with their real source levels, then request stage 10 approval. Preserve cleanup receipts outside the worktree; after all writers have exited, run `TASK_WRITERS_STOPPED=1 make task-finish` from the registered task worktree. Then run `make task-close` from clean main and deliver its card-close PR through the applicable queue, Review, CI, merge, and readback path. Never force-delete a refused cleanup.
|
||||
- **Test scenarios:** Uncommitted or untracked file; live writer; unmerged local or remote commit; missing task-card PR link; cleanup refusal; post-merge content mismatch.
|
||||
- **Verification:** Stage 10 conclusion, archived task card on main, clean recursive state, merged ancestry, removed task worktrees, and preserved external receipts.
|
||||
- **Dependencies:** U8. Stage 10 approval is an internal gate before cleanup.
|
||||
|
||||
---
|
||||
|
||||
## Verification Contract
|
||||
|
||||
| Gate | Command or platform check | Applies to | Done signal |
|
||||
|---|---|---|---|
|
||||
| Backend SQLite | `GOWORK=off go test ./... -count=1` | U3, U5 | All packages pass at the recorded Backend HEAD |
|
||||
| Backend PostgreSQL | Isolated PostgreSQL 16 with `AGENT_PG_TEST_DSN`, then role, migration, delegation, and lock-order store tests | U3, U5 | No skips in the selected PG set; container removed |
|
||||
| Cross-database atomicity | Force version-conflict, unknown-action, and out-of-cap batch failures against SQLite and isolated PostgreSQL | U3 | Both stores retain identical pre-request state with zero partial writes |
|
||||
| Migration oracle | Trusted isolated Backend/RDM store harness against the frozen v35 fixture and expected oracle | U3, U7 | Freeze/stage/compare/activate/failure/retry results match on SQLite and PostgreSQL; mismatches block activation |
|
||||
| Frontend unit/build | `npm test && npm run build && node scripts/check-knip.mjs` | U2, U3, U6 | Tests pass, build succeeds, knip stays within baseline |
|
||||
| UI | `npm test` plus the design guard through the Frontend/Dev suite | U3, U6 | UI tests and design guard pass at the fixed UI commit |
|
||||
| Browser deterministic | `npm run e2e -- e2e/iam-role-authority.playwright.ts` with isolated preview | U2, U3, U6 | Required scenarios pass; failure traces retained |
|
||||
| Browser live product | `scripts/iam-u4-role-acceptance.sh` with exact source SHAs | U3, U7 | Embedded product scenarios pass on a fresh data root; screenshot and manifest digests recorded |
|
||||
| RDM overlay | `GOWORK=<fixed overlay> go test ./... -count=1` | U3 | Development contract passes before authoritative pins exist |
|
||||
| RDM pinned product | Dirty-source negative test, then `make test`, `make product`, and `make product-smoke` from committed sources | U7 | Drift is rejected; full suite, embedded product, copied-content digests, and manifest smoke pass without overlay |
|
||||
| Documents | `git diff --check` plus link and version review | U1, U6 | No malformed diff or stale reference |
|
||||
| Dev local combination | `make test && make build` after `make task-sync` | U3, U8 | Exact fixed combination passes and builds |
|
||||
| Component CI | Backend `vet-and-test`; RDM `build-and-test`; current platform-required Frontend/Documents checks when present | U5–U7 | Each available GATE is successful on the merged candidate HEAD |
|
||||
| Dev CI | Gitea `test`, `test-pg`, `svc-realcombo` | U8 | All three jobs succeed on the same final PR HEAD |
|
||||
| Merge integrity | PR head/base readback, non-force merge, `origin/main` and component tree readback | U5–U8 | Actual protected main matches the reviewed candidate |
|
||||
| Cleanup | External receipt preservation; guarded `task-finish`; then `task-close` PR Review/CI/merge/readback | U9 | No writer, unique file, unmerged commit, active lock, or cleanup result is lost |
|
||||
|
||||
Evidence rules:
|
||||
|
||||
- Local commands remain `SELF` unless an independent reviewer replays them.
|
||||
- Gitea Actions counts as `GATE` only when the run identity, job, exact SHA, and result are recorded.
|
||||
- A Playwright screenshot or trace proves only the scenario and build identified by its receipt.
|
||||
- `SKIP` and `NOT_RUN` remain visible; they are never converted to PASS by another layer's test.
|
||||
- Any source, base, pin, manifest, or test-definition change updates the named candidate and reruns the gates it affects. A post-stage-7 change also requires a renewed stage 7 conclusion before component merge; a final Dev HEAD change invalidates its three required CI jobs.
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
|
||||
Global completion requires all of the following:
|
||||
|
||||
- DR1–DR21 all have PASS evidence. These delivery requirements are mandatory; SKIP, NOT_RUN, and exclusions block completion.
|
||||
- The user has approved stage 7 and stage 10 on named candidate versions.
|
||||
- Independent final Review has no unresolved Critical or Important finding.
|
||||
- Backend, Frontend, Documents, RDM, and Dev PRs are merged in dependency order at the recorded commits.
|
||||
- The trusted Backend tag exists remotely and peels to the reviewed Backend merge commit.
|
||||
- RDM's product manifest uses authoritative merged pins and no development overlay.
|
||||
- Dev's final PR HEAD has successful `test`, `test-pg`, and `svc-realcombo` jobs.
|
||||
- Actual protected main branches and Dev gitlinks match the reviewed candidates.
|
||||
- Production migration and deployment remain NOT_RUN unless separately authorized.
|
||||
- Failed experiments, temporary build roots, credentials, processes, branches, and worktrees are removed or retained with an explicit reason; no abandoned attempt code remains in the diffs.
|
||||
- Task cards are archived through the guarded workflow and external receipts remain available.
|
||||
|
||||
Per-unit completion:
|
||||
|
||||
| Unit | Completion signal |
|
||||
|---|---|
|
||||
| U1 | Stage 6 approval and synchronized candidate manifest are recorded |
|
||||
| U2 | Repeatable deterministic and live IAM browser slices exist and pass locally |
|
||||
| U3 | User approves the exact stage 7 requirement-to-evidence map |
|
||||
| U4 | Current component PR candidates have valid review and applicable checks |
|
||||
| U5 | Backend is merged and the user-published tag is verified |
|
||||
| U6 | Frontend and Documents are merged; UI ancestry remains valid |
|
||||
| U7 | RDM is merged with authoritative pins and a matching product manifest |
|
||||
| U8 | Dev is merged through an exclusive queue window with three exact-head GATE jobs |
|
||||
| U9 | Stage 10 is approved, records are archived, and guarded cleanup succeeds |
|
||||
|
||||
---
|
||||
|
||||
## Appendix
|
||||
|
||||
### Sources
|
||||
|
||||
- `docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`
|
||||
- `docs/proposals/2026-10-15-iam-focused-role-permission-implementation-plan.md`
|
||||
- `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`
|
||||
- `Agent-System-Dev/policy/delivery.md`
|
||||
- `Agent-System-Dev/policy/merge-queue.md`
|
||||
- `Agent-System-Dev/.gitea/workflows/ci.yaml`
|
||||
- `Agent-System-Backend/.gitea/workflows/ci.yml`
|
||||
- `Agent-System-RDM/.gitea/workflows/ci.yml`
|
||||
- `Agent-System-RDM/scripts/build-product.sh`
|
||||
- `Agent-System-Frontend/playwright.config.ts`
|
||||
- `Agent-System-Frontend/e2e/rdm-duties.playwright.ts`
|
||||
@@ -0,0 +1,88 @@
|
||||
# 分层分支交付与规范精简 r1
|
||||
|
||||
- 状态:已批准实施(2026-10-18 用户指示按计划实施);推进中
|
||||
- 负责人:weichen(需求与批准)
|
||||
- 实施:Dev 治理大任务 `feature/delivery-model-v3`
|
||||
- 适用范围:Agent-System-Dev 开发流程与规范
|
||||
- 更新时间:2026-10-18
|
||||
|
||||
## 1. 背景
|
||||
|
||||
用户提出的需求:
|
||||
|
||||
- 减少合入 Dev main 的次数,让其他分支不必反复同步、反复跑 CI。
|
||||
- L2 合入 L1 要轻;按任务类别分别处理,修复优先。
|
||||
- 测试、验收、审批放在合适的层级,不在每一层重复。
|
||||
- 去掉 Agent 的长时间 sleep 等待;精简膨胀的规范。
|
||||
|
||||
2026-10-18 只读基线(均为 **SELF** 观察):
|
||||
|
||||
- main 推进:14 天共 410 次(first-parent),峰值 85 次/天。按 PR 标题粗分,闭合任务卡约 40%、功能约 30%、组件 pin 约 20%、修复约 6%。
|
||||
10-14 启用合入窗口后仍约 17 次/天,闭合 PR 仍占约 40%。
|
||||
- 分层使用:近期 291 个已合入 PR 中 288 个 base 为 main,initiative/child 模型只用过一次。
|
||||
- CI:3.3 天跑了 200 次,单次中位 8.3 分钟;闭合相关运行约占 31% 机时;main 的 push CI 有 95% 在复测与 PR head 完全相同的树;每个 PR 平均 2.4 次 CI。
|
||||
- 等待:Git 远端往返约 0.35 秒。长等待来自 Agent 手写 `sleep N; curl` 轮询 CI,以及串行克隆 105 个嵌套子模块(实际只有 13 个不同仓;并行 `--jobs 8` 实测约 26 秒)。
|
||||
- 规范体量:日常必读规范约 4.6 万字符;同一段 Agent 路由规则写了 7 处;归档任务卡中位 113 KB,原因是证据工具把完整 JSON 收据写进了卡。
|
||||
|
||||
结论:频繁触发的主因不是层级不够,而是记账类改动也在推进 main,加上每一层都执行同样重的门禁。
|
||||
|
||||
## 2. 目标与成功标准
|
||||
|
||||
- 任务卡在合入前随 PR 折叠闭合,不再单独开闭合 PR。
|
||||
- 统一一个阻塞式的 CI 等待入口,按精确 SHA 等待结果。
|
||||
- 完整门禁只在进入 main 时执行;L2 合入 L1 只做轻量检查。
|
||||
- 任务分通道:修复直接进 main 并优先;其他类别按主题聚合进 L1。
|
||||
- 规范单一权威、更精简;任务卡变小。
|
||||
|
||||
成功标准:采纳两周后按本次基线复测。main 推进次数、CI 次数与机时、每 PR 平均 CI 次数均下降;闭合 PR 数降到零(存量清理除外)。
|
||||
|
||||
## 3. 方案要点
|
||||
|
||||
### 3.1 固定两级分支
|
||||
|
||||
- L1 大任务:从 main 切出,最终一次合入 main。L2 子任务:从 L1 切出,合入 L1。
|
||||
- L2 内部用 agent-exec 单元控制上下文长度,单元不开 PR,是最轻的一层。
|
||||
- 不再加深层级:main 的推进次数只取决于根层交付次数;每多一层就多一条同步链,集成也被推迟;现行 Guidelines 与工具只定义了两级。
|
||||
- 太大时横向拆:L1 超过约 2 周或 6–8 个子任务时拆成多个并列 L1;L2 太大就拆成兄弟 L2 或更多单元。
|
||||
|
||||
### 3.2 L1 章程
|
||||
|
||||
- 章程写在 L1 任务卡顶部:用途与目标、接纳范围与非目标、方案和计划链接、子任务登记、分级测试策略、验收条件、追加的领域规则、生命周期。
|
||||
- 建 L1 时立即开一个 base 为 main 的 [WIP] PR,用于分流发现,评论区同时作为仓外调度记录。
|
||||
- L1 不追 main,只在三种情况下合入最新 main:最终交付窗口;需要 main 上的某个修复;长周期 L1 到达里程碑或每周一次。
|
||||
|
||||
### 3.3 任务通道
|
||||
|
||||
- 修复:`fix/` 直接进 main 并优先排队;P0 用 `hotfix/`。
|
||||
- 完善:按领域放进“完善列车” L1。
|
||||
- 扩展:加入(JOIN)同主题的在途 L1。
|
||||
- 新增:新建 L1 并写章程。
|
||||
- 治理与规范:集中在一个治理 L1 中。
|
||||
- 记录类改动不单独推进 main;组件 pin 跟随用到它的 L1 一起推进。
|
||||
|
||||
### 3.4 按层级分配门禁
|
||||
|
||||
- 单元合入 L2:worker 自检,host 做最小直接检查。
|
||||
- L2 合入 L1:先同步 L1 头,跑受影响模块的测试,由一个全新会话做独立 Review,轻量 CI 作为 GATE。
|
||||
不排合入窗口,不需要分流收据、三段证据和人工审批。
|
||||
- L1 的 WIP PR:完整 CI 照常运行,作为不阻塞任何人的提前预警。
|
||||
- L1 或修复合入 main:合入窗口、完整 CI、整体 Review、三段证据,以及一次用户专属验收。
|
||||
- 人工审批只保留三处:L1 章程确认、L1 最终的用户专属验收、高后果双钥匙操作。
|
||||
|
||||
## 4. 阶段与关联任务
|
||||
|
||||
| 阶段 | 内容 | 状态 |
|
||||
|---|---|---|
|
||||
| R | 本主记录、PROJECT.md 链接、治理 L1 及其 WIP PR | 推进中 |
|
||||
| P1 | 合入前折叠闭合并清理存量卡;`make ci-wait` 与等待规则;子模块并行物化 | 待实施 |
|
||||
| P2 | CI 按 PR base 区分完整与轻量;L2 免分流、免合入窗口、免三段证据;三种卡模板;通道写入 delivery.md | 待实施 |
|
||||
| P3 | 规范精简:AGENTS.md 改为入口;delivery.md 作为单一权威并并入 merge-queue;矩阵与采纳记录改为引用;证据摘要入卡、完整收据出卡 | 待实施 |
|
||||
| M | 采纳两周后按本次基线复测 | 待执行 |
|
||||
|
||||
实时进度以 Dev 治理大任务的任务卡和 PR 为准;本记录只在阶段完成时更新。
|
||||
|
||||
## 5. 未纳入与限制
|
||||
|
||||
- 分支保护等平台设置不在本方案范围内;如需变更,由用户按双钥匙规则亲手执行。
|
||||
- 不引入自动合入队列、合入列车或调度服务。
|
||||
- 基线数字按 PR 标题粗分,只用于判断方向;实际效果以复测结果为准。
|
||||
@@ -0,0 +1,276 @@
|
||||
# EXT-20:共享法规检索与网络证据扩展点
|
||||
|
||||
状态:**已获用户授权(2026-10-18)**;owner:weichen;主任务:Dev 仓 initiative `feature/regulations-kb`。
|
||||
来源计划:Dev 仓 `docs/superpowers/plans/2026-10-18-regulations-knowledge-base-plan.md` 的 KTD1、KTD14 与 U9(本文是 U9 的基座公开契约)。
|
||||
规范依据:[扩展面兼容与演进](2026-10-01-extension-compat-policy.md)(§6 扩展点申请模板);同类先例:EXT-19 账号目录(受信插件注册、Host 可选接口消费)。
|
||||
实现位置:Backend 分支 `feature/regulations-kb-ext20`(`pkg/extension/regulations.go`、`internal/app/regulations.go`、`internal/app/compiled.go`;本文核对的提交为 `499ad47`,SELF·源码级)。
|
||||
撰写日期:2026-10-18。
|
||||
|
||||
## 1. 申请四要素(compat-policy §6)
|
||||
|
||||
- **场景**:Agent-System-RDM 处理涉及法规、标准或审评指导原则的问题时,必须同时查询受控法规语料库和真实网络搜索(双源核验),
|
||||
并把每条法规结论绑定到固定语料修订、法规版本、条款节点与原文片段。当前两条腿都走不通:
|
||||
共享法规库没有公开检索入口;RDM 现有网络腿由模型提出 URL 和摘录后再抓取比对,无法证明真实搜索发生过,基座也没有搜索能力。
|
||||
- **希望符号**:`pkg/extension` 新增可选能力 `RegulationsHost`(分发 Host 侧消费)、`RegulationsRegistrar`(进程内 Registry 侧注册)、
|
||||
提供方接口 `RegulationsProvider`、请求/结果 DTO、状态常量、`RegulationsSchemaVersion` 与 `ErrRegulationsUnavailable`。
|
||||
`Host` 接口本身不加方法。
|
||||
- **为何现有注册点组合不出来**:见第 2 节。
|
||||
- **兼容**:纯加法的可选能力;`SurfaceVersion` 由 `2.14.0-experimental` 升为 `2.15.0-experimental`,见第 5 节。
|
||||
|
||||
## 2. 为何现有扩展点组合不出来
|
||||
|
||||
| 现有扩展点 | 不能承载的原因 |
|
||||
|---|---|
|
||||
| `Host.KnowledgeSearch` / `KnowledgeWrite` | owner 作用域的项目知识检索,返回 `core.RetrievalHit`。共享法规不属于任何 owner:写进每个 owner 的知识库会复制语料、失去修订不可变性;其返回类型没有语料修订、法规/版本/节点稳定 ID、原文哈希、定位与披露状态,加字段也改变不了 owner 隔离语义。计划 KTD14 明确禁止经它承载共享法规 |
|
||||
| 项目作用域知识(`projectID` 参数、项目授权) | 共享法规访问不应随项目扩大或缩小;借项目 ID 访问会让项目成员关系变成法规访问面,也让同一条款在不同项目下出现不同可见性 |
|
||||
| `AccountDirectoryRegistrar` | 机制相同(受信插件注册、Host 可选接口消费),但语义专用于账号目录候选判定,不能复用为法规检索 |
|
||||
| 通用工具 / 命令总线 / KV / 后台任务 / 审计 | 都不能让一个插件把只读能力安全地提供给另一个插件:没有可归属的真实搜索记录,也不在发往外网前做查询最小化;证据无法钉住语料修订;任何插件都可以注册同名命令冒充提供方,不存在“单一受信提供方” |
|
||||
| 下游直接导入法规仓 | 绕过基座的调用方身份、查询最小化与审计边界,违反 RDM“只经基座公开扩展”的约束 |
|
||||
|
||||
因此需要一个新扩展点同时满足四点:共享的非项目语料;可归属、做过查询最小化的真实网络搜索;钉住语料修订的不可变证据;单一受信提供方。
|
||||
|
||||
## 3. 公开符号(`pkg/extension/regulations.go`)
|
||||
|
||||
### 3.1 常量与错误
|
||||
|
||||
- `RegulationsSchemaVersion = 1`:基座唯一接受的请求 schema;其他值在到达提供方前被拒绝。
|
||||
- 法规检索状态:`RegulationsStateOK = "ok"`、`RegulationsStateEmpty = "empty"`、`RegulationsStateDegraded = "degraded"`、`RegulationsStateUnavailable = "unavailable"`。
|
||||
- 法规披露级别:`RegulationDisclosureFull = "full"`、`RegulationDisclosureExcerpt = "excerpt"`、`RegulationDisclosureMetadataOnly = "metadata_only"`、`RegulationDisclosureWithheld = "withheld"`。
|
||||
- 网络搜索状态:`WebSearchStateOK = "ok"`、`WebSearchStateEmpty = "empty"`、`WebSearchStateUnavailable = "unavailable"`、`WebSearchStateRateLimited = "rate_limited"`、`WebSearchStateRejected = "rejected"`、`WebSearchStateError = "error"`。
|
||||
- 网页抓取状态:`WebCaptureStateCaptured = "captured"`、`WebCaptureStateRejected = "rejected"`、`WebCaptureStateFailed = "failed"`。
|
||||
- 网页留存级别:`WebRetentionFull = "full"`、`WebRetentionExcerpt = "excerpt"`、`WebRetentionMetadataOnly = "metadata_only"`。
|
||||
- `ErrRegulationsUnavailable`(`"extension: regulations provider unavailable"`):能力不可用。调用方不得回退到 `KnowledgeSearch`。
|
||||
|
||||
### 3.2 接口
|
||||
|
||||
```go
|
||||
// 由受信的法规知识插件(插件 ID "kbreg")实现并注册;基座不内置实现。
|
||||
type RegulationsProvider interface {
|
||||
SearchRegulations(context.Context, RegulationsCaller, RegulationsSearchRequest) (RegulationsSearchResult, error)
|
||||
ResolveRegulationEvidence(context.Context, RegulationsCaller, RegulationEvidenceRequest) (RegulationEvidence, error)
|
||||
SearchWeb(context.Context, RegulationsCaller, WebSearchRequest) (WebSearchResult, error)
|
||||
CaptureWeb(context.Context, RegulationsCaller, WebCaptureRequest) (WebCapture, error)
|
||||
}
|
||||
|
||||
// 编译进程内 Registry 的可选能力;只有插件 ID "kbreg" 可注册,且只能注册一次。
|
||||
type RegulationsRegistrar interface {
|
||||
RegisterRegulationsProvider(RegulationsProvider) error
|
||||
}
|
||||
|
||||
// 分发 Host 句柄的可选能力(类型断言获取);缺失即不可用,调用方不得回退到 KnowledgeSearch。
|
||||
type RegulationsHost interface {
|
||||
SearchRegulations(context.Context, RegulationsSearchRequest) (RegulationsSearchResult, error)
|
||||
ResolveRegulationEvidence(context.Context, RegulationEvidenceRequest) (RegulationEvidence, error)
|
||||
SearchWeb(context.Context, WebSearchRequest) (WebSearchResult, error)
|
||||
CaptureWeb(context.Context, WebCaptureRequest) (WebCapture, error)
|
||||
}
|
||||
```
|
||||
|
||||
`RegulationsHost` 与 `RegulationsProvider` 方法一一对应,区别只在 Host 侧没有 `RegulationsCaller` 参数:调用方身份由基座填写,调用方无法注入。
|
||||
|
||||
### 3.3 DTO 字段
|
||||
|
||||
JSON 名为 camelCase。下表“必出”指没有 `omitempty`、零值也会序列化的字段,其余字段均为 `omitempty`。
|
||||
最小 `WebSearchRequest{SchemaVersion: 1, Query: "q"}` 序列化为 `{"schemaVersion":1,"query":"q"}`。
|
||||
|
||||
**`RegulationsCaller`**(基座从可信分发上下文填写)
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `PluginID` | `pluginId`(必出) | 发起调用的插件 ID(消费方,例如 `rdm`),不是提供方 ID |
|
||||
| `Actor` | `actor` | 本次分发的操作主体 |
|
||||
| `Owner` | `owner`(必出) | 本次分发的 owner |
|
||||
|
||||
刻意不含项目 ID:共享法规访问既不随项目扩大,也不随项目缩小。
|
||||
|
||||
**`RegulationsSearchRequest`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `SchemaVersion` | `schemaVersion`(必出) | 必须等于 `RegulationsSchemaVersion` |
|
||||
| `Query` | `query`(必出) | 检索文本 |
|
||||
| `CorpusRevision` | `corpusRevision` | 要检索的语料修订;空表示当前已发布修订,结果回显实际检索的修订 |
|
||||
| `Kinds` | `kinds` | 文献类别过滤(法规、标准、指导原则等,取值由提供方定义) |
|
||||
| `CurrentOnly` | `currentOnly` | 只返回现行版本 |
|
||||
| `DocNo` | `docNo` | 按文号过滤 |
|
||||
| `ClauseNo` | `clauseNo` | 按条款号过滤 |
|
||||
| `Limit` | `limit` | 结果数上限,由提供方解释 |
|
||||
|
||||
**`RegulationsSearchResult`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `CorpusRevision` | `corpusRevision`(必出) | 实际检索的语料修订 |
|
||||
| `State` | `state`(必出) | `ok` / `empty` / `degraded` / `unavailable` |
|
||||
| `DegradedReasons` | `degradedReasons` | 降级原因(例如向量索引不可用) |
|
||||
| `Hits` | `hits`(必出) | 命中列表 `[]RegulationHit` |
|
||||
|
||||
**`RegulationHit`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `RegulationID` / `EditionID` / `NodeID` | `regulationId` / `editionId` / `nodeId`(必出) | 法规、版本、条款节点的稳定 ID,重新建索引后不变 |
|
||||
| `DocNo` / `Title` | `docNo` / `title` | 文号与标题 |
|
||||
| `EditionLabel` / `LegalStatus` | `editionLabel` / `legalStatus` | 版本标签与法律效力状态(现行、废止等) |
|
||||
| `ClauseNo` / `HeadingPath` | `clauseNo` / `headingPath` | 条款号与章节标题路径 |
|
||||
| `Excerpt` / `ExcerptStart` / `ExcerptEnd` | `excerpt` / `excerptStart` / `excerptEnd` | 按披露级别允许返回的原文片段及其偏移 |
|
||||
| `NodeSHA256` / `SourceAssetSHA256` | `nodeSha256` / `sourceAssetSha256` | 节点文本哈希与来源资产(原件)哈希 |
|
||||
| `PageStart` / `PageEnd` | `pageStart` / `pageEnd` | 原件页码定位 |
|
||||
| `Disclosure` | `disclosure`(必出) | `full` / `excerpt` / `metadata_only` / `withheld` |
|
||||
| `WithheldReason` | `withheldReason` | 不披露原文的原因(例如来源权利限制) |
|
||||
| `IsSummary` | `isSummary` | 命中的是摘要而不是原文 |
|
||||
| `MatchKinds` | `matchKinds` | 命中方式(全文、向量、编号等,取值由提供方定义) |
|
||||
| `Score` | `score` | 相关度分数 |
|
||||
|
||||
**`RegulationEvidenceRequest`**(取回钉住修订的节点原文片段)
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `SchemaVersion` | `schemaVersion`(必出) | 必须等于 `RegulationsSchemaVersion` |
|
||||
| `CorpusRevision` | `corpusRevision`(必出) | 证据所钉住的语料修订 |
|
||||
| `NodeID` | `nodeId`(必出) | 条款节点 ID |
|
||||
| `Start` / `End` | `start` / `end` | 可选的片段偏移 |
|
||||
|
||||
**`RegulationEvidence`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `CorpusRevision` | `corpusRevision`(必出) | 证据所属修订 |
|
||||
| `RegulationID` / `EditionID` | `regulationId` / `editionId` | 法规与版本 ID |
|
||||
| `NodeID` | `nodeId`(必出) | 条款节点 ID |
|
||||
| `ClauseNo` | `clauseNo` | 条款号 |
|
||||
| `Excerpt` / `Start` / `End` | `excerpt` / `start` / `end` | 原文片段与偏移 |
|
||||
| `NodeSHA256` / `SourceAssetSHA256` | `nodeSha256` / `sourceAssetSha256` | 节点文本哈希与来源资产哈希 |
|
||||
| `PageStart` / `PageEnd` | `pageStart` / `pageEnd` | 页码定位 |
|
||||
| `Disclosure` | `disclosure`(必出) | 同 `RegulationHit.Disclosure` |
|
||||
| `WithheldReason` | `withheldReason` | 不披露原因 |
|
||||
| `HashVerified` | `hashVerified`(必出) | 提供方报告的哈希校验结果;基座原样透传,不自行校验 |
|
||||
|
||||
**`WebSearchRequest`**(真实网络搜索)
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `SchemaVersion` | `schemaVersion`(必出) | 必须等于 `RegulationsSchemaVersion` |
|
||||
| `Query` | `query`(必出) | 发往外网的搜索词;基座先去首尾空白再校验 |
|
||||
| `Exclusions` | `exclusions` | 调用方声明的机密词(项目名、编号、人员等);查询含任一词即由基座拒绝 |
|
||||
| `PreferOfficial` | `preferOfficial` | 优先官方来源 |
|
||||
| `Limit` | `limit` | 结果数上限,由提供方解释 |
|
||||
|
||||
**`WebSearchResult`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `Provider` | `provider` | 实际使用的搜索提供方 |
|
||||
| `SentQuery` | `sentQuery` | 实际发往外网的查询 |
|
||||
| `SearchedAt` | `searchedAt`(必出) | 搜索时间 |
|
||||
| `State` | `state`(必出) | `ok` / `empty` / `unavailable` / `rate_limited` / `rejected` / `error` |
|
||||
| `Reason` | `reason` | 非 `ok` 状态的原因 |
|
||||
| `Results` | `results` | 排序结果 `[]WebSearchHit` |
|
||||
|
||||
**`WebSearchHit`**:`Rank`(`rank`,必出,排名)、`URL`(`url`,必出)、`Title`(`title`)、`Snippet`(`snippet`,搜索摘要,不是已核验证据)、`Publisher`(`publisher`,发布机构)、`Official`(`official`,是否被提供方判定为官方来源)。
|
||||
|
||||
**`WebCaptureRequest`**(抓取先前 `SearchWeb` 找到的页面)
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `SchemaVersion` | `schemaVersion`(必出) | 必须等于 `RegulationsSchemaVersion` |
|
||||
| `URL` | `url`(必出) | 要抓取的地址 |
|
||||
| `SearchProvider` / `SearchQuery` / `SearchRank` | `searchProvider` / `searchQuery` / `searchRank` | 关联的来源搜索(提供方、查询与排名),用于证据溯源 |
|
||||
|
||||
**`WebCapture`**
|
||||
|
||||
| 字段 | JSON | 含义 |
|
||||
|---|---|---|
|
||||
| `CaptureID` | `captureId` | 抓取记录 ID |
|
||||
| `FinalURL` | `finalUrl` | 跟随重定向后的最终地址 |
|
||||
| `FetchedAt` | `fetchedAt`(必出) | 抓取时间 |
|
||||
| `ContentType` / `ByteSize` / `SHA256` | `contentType` / `byteSize` / `sha256` | 内容类型、字节数与内容哈希 |
|
||||
| `Retention` | `retention` | `full` / `excerpt` / `metadata_only` |
|
||||
| `Text` | `text` | 页面全文,仅当来源权利允许全文留存时出现 |
|
||||
| `Candidates` | `candidates` | 从页面解析出的条款候选 `[]WebClauseCandidate` |
|
||||
| `State` | `state`(必出) | `captured` / `rejected` / `failed` |
|
||||
| `Reason` | `reason` | 非 `captured` 状态的原因 |
|
||||
|
||||
**`WebClauseCandidate`**:`ClauseNo`(`clauseNo`)、`Excerpt`(`excerpt`,必出)、`Start` / `End`(`start` / `end`,必出,片段在抓取文本中的偏移)。
|
||||
|
||||
## 4. 基座强制行为(`internal/app/regulations.go`、`compiled.go`)
|
||||
|
||||
### 4.1 注册
|
||||
|
||||
1. 只有插件 ID 为 `kbreg` 的编译进程内插件可注册提供方;其他插件 ID 或 `nil` 提供方返回错误。
|
||||
2. 只能注册一次:重复注册返回错误,已注册的第一个提供方保持不变。
|
||||
3. 声明式 Registry(`compiledRegistry`)一律拒绝 `RegisterRegulationsProvider`。
|
||||
4. 宿主未装配提供方槽时,注册返回“registration unavailable”错误;带清单记录的包装 Registry(`recordRegistry`)只把注册转发给内层 `RegulationsRegistrar`。
|
||||
|
||||
### 4.2 每次 Host 调用
|
||||
|
||||
按以下顺序执行,四个方法一致:
|
||||
|
||||
1. 先做账号有效性守卫(`requireEffective`);停用等无效账号直接返回守卫错误,提供方不会被调用。
|
||||
2. 没有已注册提供方或没有审计存储时返回 `ErrRegulationsUnavailable`。调用绝不在无审计的情况下执行;这类不可用调用不写审计。
|
||||
3. `RegulationsCaller` 由基座填写为 `{PluginID: 调用插件, Actor: 分发主体, Owner: 分发 owner}`,不含项目 ID。
|
||||
4. `SchemaVersion != RegulationsSchemaVersion` 时拒绝并返回错误,提供方不会被调用;写一条 `state=invalid` 的审计。
|
||||
5. 只有 `SearchWeb` 做查询最小化校验:
|
||||
- 先去掉 `Query` 首尾空白,提供方收到的是去空白后的查询;
|
||||
- 去空白后为空、不是合法 UTF-8 或超过 256 个 rune 时返回错误(`1-256` 按 rune 计,不按字节计),写 `state=invalid` 审计;
|
||||
- 查询包含任一 `Exclusions` 词时(每个词先去首尾空白,空词忽略,按小写子串匹配,大小写不敏感),基座直接返回
|
||||
`WebSearchResult{State: "rejected", Reason: "query contains an excluded term"}` 且 error 为 nil,提供方不会收到该查询,审计记 `state=rejected`。
|
||||
6. 提供方返回的 `State`、`Disclosure`、`Retention`、`DegradedReasons`、`HashVerified` 等全部字段原样透传;基座不把 `degraded`、`withheld`、`rejected`、`rate_limited` 或失败包装成成功,提供方返回的 error 原样返回。
|
||||
7. `CaptureWeb` 在基座侧不校验 URL:抓取安全控制(私有/保留地址、重定向、大小、权利留存)属于提供方职责(计划 U5)。
|
||||
|
||||
### 4.3 审计
|
||||
|
||||
每次到达 schema 校验的调用恰好写一条基座审计事件:owner 为分发 owner,Actor 为 `plugin:<调用插件>`,Target 为 `kbreg`。
|
||||
|
||||
| 方法 | 动作 | `revision` | `count` | `state` | `querySha256` |
|
||||
|---|---|---|---|---|---|
|
||||
| `SearchRegulations` | `regulations.search` | 成功时为结果修订,否则为请求修订 | `len(Hits)` | 提供方 `State` | `Query` 原文的 SHA-256 |
|
||||
| `ResolveRegulationEvidence` | `regulations.evidence` | 结果修订非空时取结果,否则为请求修订 | 成功为 1 | 成功为 `ok` | 不记 |
|
||||
| `SearchWeb` | `web.search` | 不记 | `len(Results)` | 提供方 `State`,或基座的 `rejected` | 去空白后查询的 SHA-256 |
|
||||
| `CaptureWeb` | `web.capture` | 不记 | `len(Candidates)` | 提供方 `State` | 去空白后 `SearchQuery` 的 SHA-256 |
|
||||
|
||||
- 审计证据 JSON 只有 `revision`、`count`、`state`、`querySha256` 四个字段(`revision` 与 `querySha256` 为空时省略);查询明文、法规片段、搜索摘要与网页全文都不进审计。
|
||||
- 失败时 `state` 取 `invalid`(schema 或查询校验失败)或 `error`(提供方返回 error),`count` 为 0。
|
||||
- 提供方 error 优先返回;调用成功但审计写入失败时,结果被扣留并返回审计错误。
|
||||
|
||||
## 5. 兼容
|
||||
|
||||
- **可选能力**:`RegulationsHost` 与 `RegulationsRegistrar` 都经类型断言获取,`Host` 与 `Registry` 接口没有新增方法;旧插件、第三方 fake Host 与旧 Registry 实现照常编译。断言失败即能力不可用。
|
||||
- **纯加法**:新增 3 个接口、12 个 DTO、20 个状态常量、`RegulationsSchemaVersion` 与 `ErrRegulationsUnavailable`;`surface.txt` 快照同步增加这些顶层名与接口方法。按 compat-policy“每次加法升次版本号”,`SurfaceVersion` 由 `2.14.0-experimental` 升为 `2.15.0-experimental`。
|
||||
- **不改 `KnowledgeSearch`**:owner 作用域项目知识检索的签名与语义不变;法规能力缺失时调用方不得回退到它。
|
||||
- **组合方式**:法规插件以编译进程内插件注册(`extension.RegisterCompiled`,插件 ID `kbreg`),与 RDM 同二进制组合。当前只有进程内 `inprocHost` 实现 `RegulationsHost`;sidecar `/host/v1` HTTP 路由不在本扩展范围,出现真实需求再申请。
|
||||
- **请求演进**:请求 schema 以 `RegulationsSchemaVersion` 显式版本化;新增可选字段一律带 `omitempty`,不兼容的请求变更须升 schema 版本并另行申请。
|
||||
|
||||
## 6. 验收(对应 Backend 测试)
|
||||
|
||||
| 验收条件 | 测试 |
|
||||
|---|---|
|
||||
| 能力可选:空 Host/Registry 不暴露能力;schema 版本为 1;错误值已定义;下游仅凭 `pkg/extension` 即可实现提供方 | `pkg/extension` `TestRegulationsContractIsOptional` |
|
||||
| 状态常量的线上取值固定;可选字段省略;`RegulationsCaller` 序列化不含 project | `pkg/extension` `TestRegulationsWireNames` |
|
||||
| 无提供方或无提供方槽时四个方法都返回 `ErrRegulationsUnavailable`,且不写审计 | `internal/app` `TestRegulationsUnavailableWithoutProvider` |
|
||||
| 只有 `kbreg` 可注册;`nil` 被拒;重复注册被拒且不替换;无槽时注册被拒 | `TestRegulationsRegistrationOnlyTrustedKbregOnce` |
|
||||
| 声明式 Registry 拒绝注册;记录型包装 Registry 转发 `kbreg` 注册 | `TestRegulationsDeclarativeRegistrationRejected` |
|
||||
| 提供方收到的 Caller 由分发上下文填写 | `TestRegulationsCallerFilledFromDispatchContext` |
|
||||
| schema 版本 0 与 2 被拒,提供方不被调用 | `TestRegulationsRejectsOtherSchemaVersions` |
|
||||
| 空查询、纯空白查询、257 rune 查询被拒;256 rune 查询通过且提供方收到去空白后的查询 | `TestRegulationsWebQueryBounds` |
|
||||
| 含排除词(大小写不敏感)的查询返回 `rejected`、提供方不被调用、审计不含该词;空排除词不拦截 | `TestRegulationsExclusionRejectedBeforeProvider` |
|
||||
| 提供方的 `degraded`、`withheld`、`metadata_only`、`rate_limited`、`rejected` 与 error 原样透传 | `TestRegulationsProviderStatesPassThroughUnchanged` |
|
||||
| 每次调用一条审计,只含 revision、count、state、querySha256,不含查询明文、片段或网页文本 | `TestRegulationsAuditEvidenceOmitsQueryAndText` |
|
||||
| 账号无效时四个方法返回守卫错误,提供方不被调用 | `TestRegulationsRequireEffectiveAccount` |
|
||||
| 只用 `pkg/extension` 的测试插件以 `kbreg` 经编译插件路径注册,冒充插件注册失败,另一编译插件经 Host 断言调用成功并留下基座审计 | `TestRegulationsCompiledPluginCompatibility` |
|
||||
| 未编入提供方时消费方命令不会提交成功 | `TestRegulationsCompiledConsumerWithoutProvider` |
|
||||
|
||||
另需 `pkg/extension` 的 surface 快照测试通过(`surface.txt` 与导出面一致)。
|
||||
|
||||
## 7. 分工
|
||||
|
||||
- **Backend(本扩展,计划 U9)**:只负责公开契约、基座强制行为与兼容测试;不内置提供方、不实现搜索或抓取、不实现 RDM 适配器。
|
||||
- **`Agent-System-KB-Regulations`(计划 U2–U5)**:实现并以插件 ID `kbreg` 注册 `RegulationsProvider`,负责语料修订、检索、披露级别、真实搜索提供方适配、网页抓取安全控制与权利留存。
|
||||
- **Agent-System-RDM(计划 U6/U7)**:经 `RegulationsHost` 消费本能力,把语料检索与网络搜索作为两条独立证据腿做双源核验;负责项目授权、引用渲染与业务审计投影。能力不可用时必须显式降级,不得回退到 `KnowledgeSearch`,也不得宣称完成双源核验。
|
||||
- **Dev(计划 U8)**:组合 pin 与组合验证。
|
||||
|
||||
## 8. 不在本文范围
|
||||
|
||||
- sidecar `/host/v1` HTTP 路由与远程提供方形态。
|
||||
- 搜索提供方选择、实网联调与费用(需用户另行确认提供方与凭据)。
|
||||
- RDM 证据批次模型、引用展示与篡改检测记录(计划 U6/U7)。
|
||||
@@ -4,11 +4,26 @@
|
||||
本索引负责导航与差异提示,规划正文、批准范围和验收条件仍以链接的主记录及其批准版本为准。
|
||||
合入授权不批准新方向、不调整里程碑范围、不启动新的业务任务。
|
||||
|
||||
[分层分支交付与规范精简 r1](2026-10-18-delivery-model-v3.md):2026-10-18 经用户批准实施;
|
||||
由 Dev 治理大任务 `feature/delivery-model-v3` 推进,实时进度见该任务卡与 PR。
|
||||
|
||||
[EXT-20 共享法规检索与网络证据扩展点](2026-10-18-ext-20-regulations-provider.md):法规知识库计划 U9 的基座公开契约;
|
||||
2026-10-18 经用户授权,由 Dev initiative `feature/regulations-kb` 推进,实时进度见该任务卡与 PR。
|
||||
|
||||
待评审(收录不是批准):[svc 插件组合部署(--with)方案 r1](2026-10-14-svc-plugin-compose-deploy.md);
|
||||
[分层 Python 环境方案 r1](2026-10-15-layered-python-env.md)(8788 无法生成 docx 的环境层;白名单自动批准,其余审批)。
|
||||
|
||||
## 先看哪一份
|
||||
|
||||
- [IAM U4 阶段 7–10 验收与交付计划](2026-10-15-iam-u4-stage7-10-delivery-plan.md):
|
||||
基于已批准的聚焦角色权限实现,安排真实浏览器验收、跨仓 Review/PR/CI、Backend 受信 tag、RDM 权威 pin、Dev 手工合入窗口及安全收口。
|
||||
生产迁移、部署、多角色、账号恢复、Agent 权限和工作流改造不在本计划内。
|
||||
|
||||
- [IAM U3 账号资料、有效态、受控目录与本人恢复 r1](2026-10-14-iam-u3-account-contract-and-plan.md):
|
||||
2026-10-14 用户批准 U3 r1 阶段 4–5;[阶段 6 审阅后修订 r1(A1–A4)](2026-10-14-iam-u3-stage6-review-amendment.md)与[委托投影真实操作者修订](2026-10-14-iam-u3-delegated-projection-actor-amendment.md)均于 2026-10-14 经用户就受影响的阶段 4–5 批准。
|
||||
实施已合入:Backend PR #198 于 2026-10-14 合入(`2716b9e`,发布为 v0.5.0);Frontend PR #86(`6db528d`)、RDM PR #380(`7c968d9`)与 Dev 组合 PR #626(`7716b28`)于 2026-10-19 合入。
|
||||
用户在部署环境上的业务验收待完成;生产部署不在本条目范围内。
|
||||
|
||||
- [IAM I1 权限边界与公开扩展合同 r1](2026-10-13-iam-i1-permission-contract.md):
|
||||
依据已批准的[用户、职位角色与分层权限统一方案 r1](2026-10-13-identity-role-permission-plan.md)
|
||||
(Documents `a088809`)冻结系统/项目/资源边界、管理员无业务 bypass、创建者 cap、独立 `audit.read` 与公开扩展四要素。
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# 通用规范采纳记录
|
||||
|
||||
- 项目/负责人:Agent-System-Documents / weichen
|
||||
- 状态:active
|
||||
- 规范位置:`deps/development-guidelines`(gitlink,见下方版本权威)
|
||||
- 规范性文档目录:`policy/`(本仓当前仅含本采纳记录,无其他规范性文档;项目规则与制度放此处)
|
||||
- 过程文档位置:过程记录保存在本仓 `docs/` 固定目录,不另设独立过程仓库:实施记录 `docs/records/`、设计方案与计划 `docs/proposals/`(索引 `docs/proposals/INDEX.md`)、测试报告 `docs/reports/`、使用报告与插件开发指南 `docs/guides/`,目录入口见 `README.md`(仓内固定目录的登记见下方项目差异表);`.task/` 仅为治理交付流程的任务卡与证据位置(随 PR 维护),任务主记录在 PR 描述中
|
||||
- 版本权威:本仓 gitlink `deps/development-guidelines` @ `2688fd2be6a52c0bf110e7e6366b6f291ab5e4eb`(可读标签:无,固定提交的 VERSION 为 0.1.0-draft.12)
|
||||
- 批准/生效依据:`feature/governance-adoption` PR(Agent-System-Documents)
|
||||
- 替代范围和迁移说明:无。既有 AGENTS.md、PROJECT.md、AGENT-EXECUTION.md、docs/ 与 .task/ 全部保留,不替代、不迁移历史记录。
|
||||
|
||||
| 规范/工作流 | 适用条件与范围 | 采纳或不适用理由 | 执行方式/证据 |
|
||||
|---|---|---|---|
|
||||
| core/baseline.md | 本仓全部工作(文档、治理与任务交付) | 适用:所有变更遵循其强度与授权定义 | manual;AGENTS.md 入口 |
|
||||
| workflows/delivery.md、debugging.md、handoff.md | 交付(任务卡 + PR)、排障、跨会话交接 | 适用:交付经 `.task/` 任务卡与 PR 进行;交接状态写入任务主记录 | manual;AGENTS.md 与 `.task/feature-governance-adoption.md` |
|
||||
| standards/collaboration.md | 并行共同起点、两级分支、节点同步与陈旧候选拒绝 | 适用:AGENTS.md 启动门禁要求从权威 origin/main 固定起点并同步 | manual;AGENTS.md 启动门禁与任务卡同步记录 |
|
||||
| standards/merge-queue.md | 多个就绪任务同一目标分支的串行交付调度 | 不适用:本仓不单独维护同一目标分支的多任务交付队列,本仓自身的交付不经队列调度。本仓存放的合入调度试行记录 [docs/records/2026-10-14-merge-queue-trial.md](../docs/records/2026-10-14-merge-queue-trial.md) 与实施计划 [docs/proposals/2026-10-13-merge-queue-implementation-plan.md](../docs/proposals/2026-10-13-merge-queue-implementation-plan.md) 记录的是 Agent-System-Dev 的调度(依据 Dev 仓 `policy/merge-queue.md` 与 Dev #518),本仓仅以文档形式保存 | 不适用 |
|
||||
| standards/agent-execution.md | 工程 Agent CLI 执行路由、会话隔离与受控集成 | 适用:允许 AI 协助文档与治理变更,必须采纳;配置见 AGENT-EXECUTION.md | AGENT-EXECUTION.md + scripts/agent-exec(validate / admit);证据:governance check 与 CLI 正负例(`.task/feature-governance-adoption.md`) |
|
||||
| standards/verification.md | 验证与结论 | 适用:本仓无可编译程序,验证以文档检查与治理检查为主 | `git diff --check`、`scripts/dm governance check --repo .`、`scripts/agent-exec validate --repo .`(PROJECT.md 验证节);`.gitea/workflows/governance.yml` |
|
||||
| standards/documentation.md | 文档结构、相对链接、版本与引用一致性 | 适用:本仓即系统文档仓(docs/ 与 README 目录) | manual;PROJECT.md 验证节 |
|
||||
| standards/multi-repo.md | 跨仓 gitlink 与组合验证 | 适用:Development-Guidelines 与 Development-Manager 以 gitlink 挂载,gitlink 变更须经 Dev 仓组合验证后推进 | manual;AGENTS.md 仓内规则 |
|
||||
| standards/security.md | 凭证与秘密不入仓 | 适用:AGENTS.md 规定 token 不入仓、不回显、不写入产出物 | manual;AGENTS.md Gitea 访问事实节 |
|
||||
| workflows/design-verification.md、standards/testability.md、standards/failure-containment.md | 功能增量的设计/验证、模块测试与故障控制 | 不适用:本仓不含产品代码、运行服务或业务测试套件(见 PROJECT.md 验证节) | 不适用 |
|
||||
| standards/module-contracts.md、observability.md、data-reliability.md、release-operations.md、configuration.md、tool-adaptation.md | 业务模块契约、观测、数据可靠性、发布运维、运行配置、工具适配 | 不适用:本仓无业务模块、运行服务、发布制品或运行配置;模块边界如出现时再启用 | 不适用 |
|
||||
|
||||
## 项目差异
|
||||
|
||||
| 原规则/章节 | 项目采用方式 | 原因与风险 | 负责人/复查条件 |
|
||||
|---|---|---|---|
|
||||
| standards/documentation.md 过程记录位置 | 登记为仓内固定目录豁免:过程记录保存于 `docs/records/`、`docs/proposals/`、`docs/reports/`、`docs/guides/`(按 `README.md` 目录),任务卡与证据保存于 `.task/`;不另设独立过程仓库 | 本仓即系统文档与过程记录仓(见 `README.md`),过程记录本身是其内容,拆出独立仓会使文档与其引用的实施记录分离。风险:规范文档与过程记录同仓,需依靠目录边界与 `docs/proposals/INDEX.md` 区分 | weichen;复查条件:建立独立过程仓库,或 Development-Guidelines 固定版本变更过程记录位置规则时复查 |
|
||||
|
||||
默认做法差异说明理由;硬边界冲突须有明确有效决策,不能用此表自授权。建议不采用无需登记。
|
||||
|
||||
## 本次升级或首次接入验证
|
||||
|
||||
本次为首次接入,采用 `governance init --adopt` 固定 Development-Guidelines `2688fd2be6a52c0bf110e7e6366b6f291ab5e4eb` 与 Development-Manager `bd3a7de57a3a9e1a4495b94c5fa5c08d71e7be8c`。既有 AGENTS.md、PROJECT.md、AGENT-EXECUTION.md 保留,新增治理文件见 `scripts/`、`.gitea/workflows/governance.yml`、`.task/templates/task-card.md`。正负例与 `governance check`、`agent-exec validate` 的输出证据记录于 `.task/feature-governance-adoption.md`。
|
||||
|
||||
回退方式:删除两个 gitlink(`deps/development-guidelines`、`deps/development-manager`)、`.gitmodules`、`scripts/`、`.gitea/workflows/governance.yml`、`.task/templates/task-card.md` 与本记录,并撤销 AGENTS.md 与 PROJECT.md 中新增的段落。回退前须确认不再有任务依赖 `scripts/agent-exec` 路由。
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# 共享 Agent 执行器薄入口:转调 scripts/dm 的 agent-exec 子命令(validate / run / apply / admit / cleanup)。
|
||||
set -euo pipefail
|
||||
exec "$(dirname "$0")/dm" agent-exec "$@"
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# 共享 dm-server 薄入口:读取 deps/development-manager 的 gitlink 钉住提交,
|
||||
# 按该提交构建并缓存 dm-server,再 exec 其子命令(agent-exec、task、governance)。执行语义与退出码以该二进制为准。
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
dm="$root/deps/development-manager"
|
||||
|
||||
die() { echo "dm: $*" >&2; exit 2; }
|
||||
|
||||
# git 钩子会导出仓库本地变量(GIT_DIR、GIT_INDEX_FILE 等),它们会让 `git -C "$dm"` 解析到外层仓库。
|
||||
# 所有针对 DM 检出的 git 与 go 调用都在清除这些变量后执行;读取外层 gitlink 与最终 exec 仍保留继承环境。
|
||||
dm_local_env="GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE GIT_PREFIX GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_COMMON_DIR GIT_GRAFT_FILE GIT_NAMESPACE GIT_CONFIG GIT_CONFIG_PARAMETERS GIT_CONFIG_COUNT GIT_SHALLOW_FILE GIT_IMPLICIT_WORK_TREE $(git rev-parse --local-env-vars 2>/dev/null || true)"
|
||||
dm_clean_env() { for v in $dm_local_env; do unset "$v"; done; }
|
||||
dmgit() ( dm_clean_env; git -C "$dm" "$@" )
|
||||
|
||||
# 1. 钉住版本只来自索引中的 gitlink(mode 160000),不读取工作区文件。
|
||||
entry=$(git -C "$root" ls-files -s -- deps/development-manager 2>/dev/null || true)
|
||||
[ -n "$entry" ] || die "索引中缺少 deps/development-manager 的 gitlink,无法确定钉住的 DM 版本"
|
||||
read -r mode pin _stage _path <<<"$entry"
|
||||
[ "$mode" = 160000 ] || die "deps/development-manager 不是 gitlink(mode=$mode)"
|
||||
[[ "$pin" =~ ^[0-9a-f]{40}$ ]] || die "gitlink 提交格式无效:$pin"
|
||||
|
||||
# 2. 检出必须存在且确为 DM 独立仓库(避免 git -C 误解析到外层仓库),HEAD 必须等于钉住提交。
|
||||
[ -e "$dm/.git" ] || die "deps/development-manager 未检出;请按钉住提交 $pin 检出该 gitlink"
|
||||
top=$(dmgit rev-parse --show-toplevel 2>/dev/null || true)
|
||||
[ -n "$top" ] && [ "$top" = "$(cd "$dm" && pwd -P)" ] \
|
||||
|| die "deps/development-manager 不是独立检出;请按钉住提交 $pin 检出该 gitlink"
|
||||
head=$(dmgit rev-parse HEAD 2>/dev/null || true)
|
||||
[ "$head" = "$pin" ] \
|
||||
|| die "deps/development-manager HEAD(${head:-未知})与钉住提交 $pin 不一致;请按钉住提交检出"
|
||||
|
||||
# 3. 检出工作树必须干净(忽略嵌套 submodule 状态)。
|
||||
dirty=$(dmgit status --porcelain --ignore-submodules=all) \
|
||||
|| die "无法读取 deps/development-manager 的工作树状态"
|
||||
[ -z "$dirty" ] || die "deps/development-manager 存在未提交改动,拒绝运行(请先清理该检出)"
|
||||
|
||||
# 4. 二进制按钉住提交缓存;缺失时在缓存目录内临时构建,完成后原子改名,避免留下半成品。
|
||||
cache="${DM_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/development-manager/dm-server}"
|
||||
bin="$cache/dm-server-$pin"
|
||||
if [ ! -x "$bin" ]; then
|
||||
command -v go >/dev/null 2>&1 || die "缺少 go,无法构建 dm-server($pin)"
|
||||
mkdir -p "$cache"
|
||||
cache=$(cd "$cache" && pwd -P)
|
||||
bin="$cache/dm-server-$pin"
|
||||
work=$(mktemp -d "$cache/build-$pin.XXXXXX")
|
||||
if ! (dm_clean_env; cd "$dm" && GOWORK=off go build -trimpath -o "$work/dm-server" ./cmd/dm-server); then
|
||||
rm -rf "$work"
|
||||
die "构建 dm-server 失败($pin)"
|
||||
fi
|
||||
mv -f "$work/dm-server" "$bin"
|
||||
rm -rf "$work"
|
||||
fi
|
||||
|
||||
exec "$bin" "$@"
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# 本地检查(pre-commit):集成分支直提拒绝 + 分支命名 + 本地完整性核对。权威准入在 PR(baseline.md:26)。
|
||||
branch=$(git rev-parse --abbrev-ref HEAD)
|
||||
case "$branch" in
|
||||
main|master)
|
||||
echo "pre-commit: 拒绝——集成分支 ${branch} 不得直接提交,走 feature 分支 + PR(collaboration.md)" >&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo "$branch" | grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' || {
|
||||
echo "pre-commit: 拒绝——分支名须为 feature|fix|hotfix|release/<slug>,当前:${branch}" >&2
|
||||
exit 1
|
||||
}
|
||||
root=$(git rev-parse --show-toplevel)
|
||||
# admit 只是本地完整性/篡改证据(SELF):同一 OS 用户可读取本地 HMAC 密钥并绕过本钩子,
|
||||
# 它不是 GATE,也不是可信身份。缺失或无效的本地记录仍拒绝;交付信任来自精确 HEAD 的 CI 或独立 REPLAY。
|
||||
"$root/scripts/agent-exec" admit --repo "$root"
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# 通用任务入口:转调 scripts/dm 的 task 子命令(triage / start / pr-check)。
|
||||
set -euo pipefail
|
||||
exec "$(dirname "$0")/dm" task "$@"
|
||||
Reference in New Issue
Block a user