diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index 2d4a3256..5d9da845 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -18,6 +18,7 @@ | owner | task | branch | worktree | mutex | status | |---|---|---|---|---|---| +| Cursor | governance-console-p1-design-2026-09-24 | feature/governance-console-p1-design-2026-09-24 | .worktrees/governance-console-p1-design-2026-09-24 | agentctl-console-design | in_progress | | Cursor | governance-visibility-p0-2026-09-24 | feature/governance-visibility-p0-2026-09-24 | .worktrees/governance-visibility-p0-2026-09-24 | agentctl-visibility | done | | Cursor | worktree-cleanup-closeout-2026-09-24 | feature/worktree-cleanup-closeout-2026-09-24 | .worktrees/worktree-cleanup-closeout-2026-09-24 | worktree-cleanup | in_progress | | Cursor | non-author-closeout-2026-09-24 | feature/non-author-closeout-2026-09-24 | .worktrees/non-author-closeout-2026-09-24 | docs+git:dev | done | diff --git a/.doc/ops/short-term/2026-09-24-governance-console-p1-design.md b/.doc/ops/short-term/2026-09-24-governance-console-p1-design.md new file mode 100644 index 00000000..b3139198 --- /dev/null +++ b/.doc/ops/short-term/2026-09-24-governance-console-p1-design.md @@ -0,0 +1,66 @@ +--- +title: "治理控制台 P1 设计定稿" +task_id: "governance-console-p1-design-2026-09-24" +status: in_progress +created: 2026-09-24 +updated: 2026-09-24 +owner: Cursor +task_type: research +task_tier: small +policy_revision: "agent-governance-2026-09-21-r1 + cursor-governance-2026-09-24-r1 @ 4844041b" +branch: feature/governance-console-p1-design-2026-09-24 +worktree: .worktrees/governance-console-p1-design-2026-09-24 +base_dev_sha: "4844041b1b417b5ac415fe779a3784959c0afbbe" +write_scope: [".doc"] +allowed_environments: [] +head_sha: "" +mutex: "agentctl-console-design" +review_state: pending +integration_state: pending +deployment: not_required +cleanup: pending +cleanup_owner: "独立维护者" +cleanup_trigger: "合入 local_dev 且文档验收通过后" +remote_sync: not_requested +--- + +# 治理控制台 P1 设计定稿 + +## 用户目标与授权 + +- 用户目标:把 P1 骨架扩成可实施规格(12 节),不写实现代码。 +- 交付终点:`.doc/ops/short-term/2026-09-24-governance-console-plan.md` 定稿合入 local `dev`。 +- 禁止:scripts 编码、产品 web/cmd/internal、push、晋升、跨机协调实施。 + +## 范围与非目标 + +- 允许:设计文档原地扩充、任务主记录、CURRENT 认领。 +- 非目标:P1-a…e 任何代码实施。 + +## 相关问题与经验 + +- catalog:`R-governance-console-p1`;overlap 探测无 hard/soft(与 R-governance-visibility-p0 语义相关但未命中门禁)。 +- 前序:P0 合入 `dev@4844041b`;证据见 [测试报告](../../test-reports/2026-09-24-governance-visibility-p0-test-report.md)。 + +## 步骤与验收 + +1. [x] 建场 / register / CURRENT 认领 +2. [x] 设计文档 12 节扩充 +3. [x] 链接/锚点/mermaid 检查(12 节 / 0 断链 / 4 mermaid / 594 行) +4. [ ] 合入收口 + +## 验证与证据 + +| 范围 | 命令与环境 | 预期 | 实际 / 判定 | 证据 | +|---|---|---|---|---| +| 纯文档 | 链接/锚点/mermaid + 符号对照 | 无断链、可渲染 | PASS:links_ok=16 broken=0 mermaid=4 符号均存在 | 本任务验证节 | +| 产品 PI / go test | — | NOT REQUIRED | — | 验证契约「工程规范/文档」层 | + +## 收口与交接 + +待定稿后填写五态。 + +## 相关链接 + +- 设计主体:[2026-09-24-governance-console-plan.md](./2026-09-24-governance-console-plan.md) +- P0 任务:[2026-09-24-governance-visibility-p0.md](./2026-09-24-governance-visibility-p0.md) diff --git a/.doc/ops/short-term/2026-09-24-governance-console-plan.md b/.doc/ops/short-term/2026-09-24-governance-console-plan.md index 601b6f6c..c0791623 100644 --- a/.doc/ops/short-term/2026-09-24-governance-console-plan.md +++ b/.doc/ops/short-term/2026-09-24-governance-console-plan.md @@ -1,174 +1,594 @@ --- -title: "治理控制台 P1 方案" -task_id: "governance-visibility-p0-2026-09-24" +title: "治理控制台全景设计(P1 定稿)" +task_id: "governance-console-p1-design-2026-09-24" status: planned created: 2026-09-24 updated: 2026-09-24 owner: Cursor task_type: research related_p0: governance-visibility-p0-2026-09-24 +design_revision: "governance-console-p1-r1" +base_dev_sha: "4844041b1b417b5ac415fe779a3784959c0afbbe" --- -# 治理控制台全景设计(P1) +# 治理控制台全景设计(P1 定稿) 覆盖方向:**A 状态可视化**、**B 流程自动化**、**C MCP 只读巡览**、**D PI 派发包生成**。 -P0 已落地:`--format table`、`brief`、MCP 四只读工具、`claim archive`。本文件是后续实施蓝图,**不在 P0 编码**。 +本文件是**可实施规格**;实施须另开 task + `catalog overlap`。本轮不写代码。 -## 1. 目标 +前置:P0 已合入 `dev@4844041b`(`--format table`、`brief`、MCP +4、`claim archive`)。证据:[测试报告](../../test-reports/2026-09-24-governance-visibility-p0-test-report.md)。 -给人类与 Agent 一个本机治理面:一眼看到任务/租约/队列/漂移/清理积压,并把高频手工步骤(认领、归档、集成候选、PI packet)收成白名单动作——**全部复用既有租约与 `docs apply` CAS,不新增旁路写入口**。 +--- -## 2. 架构 +## 1. 现状与缺口 -```text -Browser (localhost) ──► static SPA ──► agentctl serve (127.0.0.1) - │ -Cursor MCP / CLI ────────────────────────────┤ - ▼ - agentctl 核心 - (SQLite / leases / docs apply / queue / catalog) +### 1.1 P0 已交付(机器权威层可读) + +| 能力 | 入口 | 核心实现 | +|---|---|---| +| 人类可读表 | `agentctl status\|queue list\|claim list\|cleanup report --format table` | [`emit_payload`](../../../scripts/agentctl.py) + [`agentctl_visibility`](../../../scripts/agentctl_visibility.py) | +| 一页简报 | `agentctl brief` | `collect_*` + `cmd_brief` | +| done 行归档 | `agentctl claim archive [--apply]` | `claim_archive` → `@document` docs apply CAS | +| MCP 只读 | `queue_list` / `claim_status` / `cleanup_report` / `catalog_drift` | [`agentctl-mcp.mjs`](../../../scripts/mcp/agentctl-mcp.mjs) | + +### 1.2 仍存缺口(本设计覆盖) + +| ID | 缺口 | 用户摩擦 | +|---|---|---| +| A2 | 无 dashboard / HTTP 面 | 终端 JSON/table 仍需手敲;多源聚合靠 `brief` 文本 | +| B1 | 认领仍三步 | skill 要求 snapshot → 手写 JSON → apply | +| B2 | 每任务双 worktree | 手工建 `integrate-*` + `--no-ff` | +| D1 | PI packet 手工组装 | task_id/request_id/source_sha/approved_commands 易漏 | +| C2 | MCP 未暴露 brief/archive | 会话内仍偶发 shell 拼装 | + +跨机 claim 权威不在本方案:见 [coordination-db-daemon phased plan](../../remediation/2026-09-23-coordination-db-daemon-phased-plan.md)。 + +--- + +## 2. 目标与非目标 + +### 2.1 目标 + +1. 本机治理面:一眼看 tasks / runs / claims / queue / drift / cleanup。 +2. 高频动作白名单:`claim new`、`claim archive`、`integrate prepare`、`pi packet`——**全部复用既有租约与 `docs apply`,不新增旁路写入口**。 +3. CLI 与 HTTP 同构:同一 Python 函数;MCP 可薄封装。 +4. 可测:工程工具单测覆盖路由、错误码、幂等、loopback。 + +### 2.2 非目标 + +- 远程多用户 Web 登录 / 鉴权体系。 +- 产品 [`web/`](../../../web/) AdminConsole 内嵌治理页。 +- 绕过 `agentctl with` / `docs apply` 的文件写。 +- 自动 `--force` 清理 worktree、push、晋升 test/main。 +- 替代 Herdr / 产品 PI Agent Runner。 +- 把 `serve` 装进 systemd/launchd(保持手工前台进程)。 + +--- + +## 3. 总体架构 + +```mermaid +flowchart TB + subgraph clients [Clients] + Cursor[Cursor_MCP_CLI] + Term[Terminal] + Browser[Browser_localhost] + end + subgraph console [Governance_Console_P1] + Spa[static_SPA] + Serve[agentctl_serve] + CliExt[claim_new_integrate_prepare_pi_packet] + end + subgraph core [Existing_agentctl_core] + Collect[collect_star_pure_fns] + Docs[docs_apply_CAS] + Lease[Lease_with] + Queue[queue_daemon_ff] + Db[(governance_SQLite)] + end + Cursor --> Serve + Cursor --> CliExt + Term --> CliExt + Browser --> Spa --> Serve + Serve --> Collect + Serve --> CliExt + CliExt --> Docs + CliExt --> Lease + CliExt --> Queue + Collect --> Db + Docs --> Db + Lease --> Db + Queue --> Db ``` -| 组件 | 职责 | 约束 | +### 3.1 进程拓扑 + +| 进程 | 生命周期 | 说明 | |---|---|---| -| `agentctl serve` | HTTP JSON API + 静态文件;仅绑 `127.0.0.1` | 无鉴权外放;动作白名单;写路径内部调 `with`/`docs apply` | -| 静态 SPA | 单页四区:任务租约 / 队列集成 / 文档漂移 / 清理积压 | **不进产品 `web/`**,避免发布门禁耦合;建议路径 `scripts/governance-console/` | -| CLI 增强 | `claim new`、`integrate prepare`、`pi packet` | 与 HTTP 动作共用同一 Python 函数 | +| `agentctl daemon` | 已装载 systemd/launchd | 仅队列 ff-only;**不**承载 HTTP | +| `agentctl serve` | 人工前台 / 可选 tmux | 绑 `127.0.0.1:7432`;退出即停 | +| SPA | 由 serve 静态托管 | 零构建单页;无独立 Node 服务 | -跨机权威认领不在本方案重复设计,衔接 [coordination-db-daemon phased plan](../remediation/2026-09-23-coordination-db-daemon-phased-plan.md)。 +### 3.2 组件职责 -## 3. HTTP API(草案) - -基址:`http://127.0.0.1:7432`(端口可配,默认避开产品 8080/8081)。 - -### 3.1 只读 - -| Method | Path | 映射 | +| 组件 | 路径(建议) | 职责 | |---|---|---| -| GET | `/api/status` | `collect_status_payload` + 可选 `?format=table` | -| GET | `/api/brief` | `cmd_brief` Markdown 或 JSON | -| GET | `/api/queue` | `queue list` | -| GET | `/api/claims` | `claim list` | -| GET | `/api/cleanup` | `cleanup report` | -| GET | `/api/docs/drift` | `docs drift` | -| GET | `/api/catalog/drift` | `catalog drift` | -| GET | `/api/daemon` | `daemon status` | +| `agentctl serve` | [`scripts/agentctl.py`](../../../scripts/agentctl.py) 子命令 | HTTP JSON + 静态文件 | +| 动作实现 | `scripts/agentctl_console.py`(新建)或内嵌 | `claim_new` / `integrate_prepare` / `pi_packet` 纯逻辑 | +| SPA | `scripts/governance-console/index.html` | 四区仪表盘 | +| MCP 增补 | [`scripts/mcp/agentctl-mcp.mjs`](../../../scripts/mcp/agentctl-mcp.mjs) | 可选:`brief`、`claim_archive`、动作薄封装 | -### 3.2 动作白名单(均要求 `task` + 可选 `authorization_ref`) +--- -| Method | Path | 行为 | +## 4. 数据模型与 API 资源映射 + +### 4.1 治理 SQLite(只读投影,不改 schema) + +| 表 | 关键字段 | 消费端点 | |---|---|---| -| POST | `/api/claim/new` | 见 §4.1:snapshot→delta→apply 封装 | -| POST | `/api/claim/archive` | 既有 `claim archive --apply` | -| POST | `/api/integrate/prepare` | 见 §4.2:候选 worktree + `--no-ff` | -| POST | `/api/pi/packet` | 见 §4.3:生成 dispatch JSON | +| `tasks` | id, owner, worktree, branch, state, scopes, capabilities | `GET /api/status` | +| `runs` | id, task, state, pid, start, boot, resources | `GET /api/status` | +| `queue_items` | id, task, head_sha, expected_dev, priority, state | `GET /api/queue` | +| `claims` | id, task, resource, state, updated | `GET /api/claims` | +| `documents` / `receipts` | run, task, file, hash | 动作审计;不直接暴露 | +| `catalog_*` | requirements, features, overlaps | `GET /api/catalog/drift` | +| `matrix_state_snapshot` | record_id, status | `GET /api/docs/drift` | -拒绝:任意 shell、env-service、push、晋升、删 worktree、绕过租约的文件写。 +CURRENT.md / FEATURE-MATRIX.md 仍是人类投影;机器权威在 SQLite + docs apply 收据。 -## 4. B/D CLI 完整版 +### 4.2 只读 payload 契约(与 P0 `collect_*` 同源) -### 4.1 `agentctl claim new`(B) +```text +GET /api/status → collect_status_payload(repo) + {status, lock_root, main, version, epoch, tasks[], runs[], queue[], policy} +GET /api/brief → cmd_brief 逻辑;?format=markdown|json +GET /api/queue → queue_list 同构 {status, items[]} +GET /api/claims → claim_list 同构 {status, claims[]} +GET /api/cleanup → collect_cleanup_payload {worktrees[]} +GET /api/docs/drift → collect_docs_drift_payload {regressions[], open_capabilities[]} +GET /api/catalog/drift → collect_catalog_drift_payload +GET /api/daemon → collect_daemon_payload {manager_lock, heartbeat, queue} +``` + +查询参数: + +- `?format=json|table`(status/queue/claims/cleanup):table 时 `Content-Type: text/plain`,body 为 `render_*_table` 文本。 +- `?all=1`(queue/claims):含终态行。 + +### 4.3 动作请求/响应信封 + +```json +{ + "request_id": "uuid-or-client-supplied", + "task": "governance-…", + "authorization_ref": "optional-user-chat-ref", + "...action_fields": {} +} +``` + +成功: + +```json +{"status": "ok|archived|prepared|created", "run_id": "…", "request_id": "…", "...": {}} +``` + +失败(HTTP 状态见 §5.3): + +```json +{"status": "denied", "code": "BASE_CHANGED", "message": "…", "...details": {}} +``` + +--- + +## 5. 后端 `agentctl serve` 详细设计 + +### 5.1 启动 + +```bash +agentctl serve [--host 127.0.0.1] [--port 7432] [--static scripts/governance-console] +``` + +约束: + +- 默认 host **仅** `127.0.0.1`;传入 `0.0.0.0` → 启动前拒绝 `INVALID_REQUEST`。 +- 单线程或 `ThreadingHTTPServer`;写动作串行化靠既有 Lease/SQLite,不另造全局锁。 +- 不注册为 daemon;SIGINT 干净退出。 + +### 5.2 路由表 + +#### 只读(8) + +| Method | Path | 处理 | +|---|---|---| +| GET | `/api/status` | `collect_status_payload` | +| GET | `/api/brief` | brief 聚合 | +| GET | `/api/queue` | `queue_list` | +| GET | `/api/claims` | `claim_list` | +| GET | `/api/cleanup` | `collect_cleanup_payload` | +| GET | `/api/docs/drift` | `collect_docs_drift_payload` | +| GET | `/api/catalog/drift` | `collect_catalog_drift_payload` | +| GET | `/api/daemon` | `collect_daemon_payload` | +| GET | `/` 与 `/static/*` | SPA | + +#### 动作白名单(4)——同进程函数调用,禁止 `subprocess` 再调 CLI + +| Method | Path | 函数 | +|---|---|---| +| POST | `/api/claim/new` | `claim_new(repo, args)` | +| POST | `/api/claim/archive` | 既有 `claim_archive`(`apply=True`) | +| POST | `/api/integrate/prepare` | `integrate_prepare(repo, args)` | +| POST | `/api/pi/packet` | `pi_packet(repo, args)` | + +未列出的 `/api/*` → 404。任意 shell / env-service / push / 删 worktree → **永不实现**。 + +### 5.3 错误码 → HTTP + +| agentctl `Denied.code` | HTTP | 客户端建议 | +|---|---|---| +| `INVALID_REQUEST` | 400 | 修请求体 | +| `CAPABILITY_REQUIRED` / `PATH_OUT_OF_SCOPE` | 403 | 补 capability 或换 task | +| `BASE_CHANGED` / `WORKTREE_MISMATCH` / `LOCK_BUSY` / `UNMANAGED_DOCUMENT_CHANGE` | 409 | 重新 snapshot / prepare;不自动重试 | +| `EVIDENCE_REQUIRED` | 422 | 补证据后重试 | +| `UNKNOWN_*` | 404 | 检查 id | +| 未捕获异常 | 500 | 查 run 审计;`RECOVERY_REQUIRED` | + +### 5.4 幂等 + +- 客户端可传 `request_id`;缺省服务端生成 uuid。 +- 写路径进入 `Lease(..., request_id, digest)`:与现有 `docs_apply` replay 一致——相同 request_id 返回 `replayed`,不二次写。 +- `integrate prepare`:若 integrate worktree 已存在且 tip 已含任务分支 merge → 返回既有 `{head, integrate_task}`(幂等成功),不强制重建。 + +### 5.5 并发与 expected-dev 竞态 + +```text +prepare 冻结 expected_dev = tip(dev) + → validate + → enqueue(expected_dev, head) +daemon:若 tip(dev) != expected_dev → 该项失败/取消(既有行为) +客户端:收到失败或 BASE_CHANGED → 重新 prepare,禁止盲重试同 head +``` + +HTTP 层不自动重试;SPA 按钮在 409 时提示「dev 已前进,请重新 prepare」。 + +### 5.6 安全 + +1. Loopback only。 +2. 无 Cookie / Bearer;本机信任模型(与 CLI 相同)。 +3. 动作白名单硬编码;请求体禁止 `command` / `shell` / `argv` 字段。 +4. 所有写动作写 `runs`/`documents` 审计(既有 Lease)。 +5. CORS:仅同 origin(静态同 host);或不设 CORS(同源足够)。 + +### 5.7 实现落点(实施时) + +- argparse:`serve` 子命令。 +- 标准库 `http.server` 或轻量 handler;**零新 pip 依赖**。 +- 静态根默认 `scripts/governance-console/`(gitignore 可选缓存,源码入库)。 + +--- + +## 6. CLI 动作详细设计 + +HTTP 与 CLI **共用**下列函数;CLI 为 argparse 薄包装。 + +### 6.1 `agentctl claim new`(B1) ```bash agentctl claim new --task "$ID" --owner Cursor --mutex \ - [--branch feature/$ID] [--worktree .worktrees/$ID] + [--branch feature/$ID] [--worktree .worktrees/$ID] \ + [--status in_progress] [--replace] [--request-id RID] ``` -内部: +**内部步骤** -1. `docs snapshot --file CURRENT --record $ID` -2. 若 `sha256` 已存在 → 拒绝或 `--replace` 显式更新 -3. 构造一行 `| owner | task | … | in_progress |` -4. `docs apply`(单行 CAS;无需 `@document`) +1. `task_row` 校验:cwd == 注册 worktree;需 `docs:publish`(单行写 CURRENT,无需 admin)。 +2. `docs_snapshot(CURRENT, record=task)`。 +3. 若 `sha256` 非空且未 `--replace` → `deny('INVALID_REQUEST', 'row exists; pass --replace')`。 +4. 构造一行:`| {owner} | {task} | {branch} | {worktree} | {mutex} | {status} |`(worktree 用相对 `.worktrees/…`)。 +5. `docs_apply` 单行 CAS(`expected_sha256` 来自 snapshot;新建为 null)。 +6. emit `{status:'created'|'replaced', record_id, run_id}`。 -消除当前 skill 里 snapshot → 手写 JSON → apply 三步。 +**拒绝码**:`WORKTREE_MISMATCH`、`CAPABILITY_REQUIRED`、`BASE_CHANGED`、`AMBIGUOUS_RECORD`。 -### 4.2 `agentctl integrate prepare`(B) +**替代关系**:取代 skill [`agentctl-worktree-setup`](../../../.cursor/skills/agentctl-worktree-setup/SKILL.md) 中「CURRENT 认领」三段手工;建 worktree / `task register` 仍由 skill 前半段负责(本命令不创建 git worktree)。 + +**幂等**:同 request_id replay;`--replace` 在 BASE_CHANGED 后需新 snapshot。 + +### 6.2 `agentctl integrate prepare`(B2) ```bash -agentctl integrate prepare --task "$ID" --expected-dev \ - [--integrate-id integrate-$ID] +agentctl integrate prepare --task "$ID" --expected-dev \ + [--integrate-id integrate-$ID] [--request-id RID] ``` -内部(仍在租约内): +**前置**:调用方 cwd 可为任务 worktree 或 main;函数内切换到新建 integrate worktree 执行 merge。 -1. 从 freeze 的 `expected-dev` 建 `feature/integrate-` worktree(若已存在则复用并校验干净) -2. 在 integrate worktree `task register`(scope 含 git:dev 所需能力) -3. `--no-ff` 合并任务分支到候选 HEAD -4. 输出 `{integrate_task, worktree, head}` 供后续 `validate` / `queue enqueue` +**内部步骤** -不替代 daemon ff-only;只去掉「手工第二个 worktree」摩擦。 +1. 校验 `expected-dev == rev-parse(refs/heads/dev)`,否则 `BASE_CHANGED`。 +2. `integrate_id = integrate-id or f"integrate-{task}"`;branch `feature/{integrate_id}`。 +3. 若 worktree 不存在:调用与 `agent-worktree.sh create` 等价逻辑(`git worktree add -b … expected-dev`)。 +4. 在 integrate worktree:`task register --capability git:dev`(若已注册且匹配则跳过)。 +5. Lease(`path:.`,`git:index`,`git:dev`) 内:`git merge --no-ff -m "merge: candidate {task} into dev" feature/{task-branch}`。 +6. 校验 `HEAD^1 == expected-dev`。 +7. emit `{status:'prepared', integrate_task, worktree, head, expected_dev, first_parent}`。 -### 4.3 `agentctl pi packet`(D) +**不做**:validate、enqueue(留给既有命令 / SPA 分步按钮,避免隐式合入)。 + +**拒绝码**:`BASE_CHANGED`、`LOCK_BUSY`、`CAPABILITY_REQUIRED`、`GIT_ERROR`、worktree 脏 → `INVALID_REQUEST`。 + +**替代关系**:压缩 skill [`agentctl-integrate-closeout`](../../../.cursor/skills/agentctl-integrate-closeout/SKILL.md) 的「候选」段;后续仍 `validate` → `queue enqueue`。 + +### 6.3 `agentctl pi packet`(D1) ```bash -agentctl pi packet --task "$ID" --role research|planning|… \ +agentctl pi packet --task "$ID" --role research|planning|reviews|diagnosis|test_run|acceptance_check|report \ --goal '…' --authorization-ref '…' \ - [--approved-command '…']… + [--source-sha FULL] [--timeout-seconds 600] \ + [--input-file PATH]… [--approved-command 'argv…']… \ + [--request-id RID] [--write] ``` -从 task 行读取 `worktree`/`HEAD`,生成: +**内部步骤** + +1. 读 task 行 → worktree;`source_sha = freeze_source_sha(worktree, arg or HEAD)`(复用 [`pi_subagents.freeze_source_sha`](../../../scripts/pi_subagents.py) 规则:必须满 40/64 hex)。 +2. `request_id = arg or uuid`;校验 `REQUEST_ID_RE`。 +3. 组装 packet: ```json { "task_id": "…", - "request_id": "", + "request_id": "…", "role_id": "research", "goal": "…", "authorization_ref": "…", - "source_sha": "", - "input_files": [], + "source_sha": "<40hex>", + "input_files": ["…"], "timeout_seconds": 600, - "approved_commands": [] + "approved_commands": [["python3","-B","-m","unittest", "…"]] } ``` -写出到 `.doc/test-reports/assets/pi-subagents///packet.json`(路径仍须在 `pi_subagents` 租约约定内)。不自动 `run`——生成与派发分离,避免误触模型。 +4. 默认 stdout JSON;`--write` 时原子写入 + `.doc/test-reports/assets/pi-subagents///packet.json` + (目录与 [`REPORT_ROOT_REL`](../../../scripts/pi_subagents.py) 一致),并要求 Lease `path:.doc/test-reports/…`。 +5. **不**调用 `pi_subagent_run`——生成与派发分离。 -## 5. 前端 SPA(A) +**拒绝码**:`UNKNOWN_TASK`、`INVALID_SOURCE_SHA`、`UNKNOWN_ROLE`(对照模型配置角色表)、`PATH_OUT_OF_SCOPE`。 -单页四区,无路由也可: +**替代关系**:减少 [`pi-subagent-dispatch`](../../../.cursor/skills/pi-subagent-dispatch/SKILL.md) 手工拼包;skill 改为「packet 路径 → run」。 -1. **任务 / 租约**:活跃 tasks、live runs、held claims;链到 worktree 路径文本。 -2. **队列与集成**:pending/claimed;按钮「prepare」(调 `/api/integrate/prepare`,需确认)。 -3. **文档漂移**:MATRIX hard regressions + catalog drift。 -4. **清理积压**:`cleanup report` 表;仅展示,删除仍人工/`agent-worktree remove`。 +### 6.4 `claim archive`(已有,HTTP 暴露) -技术选型建议:零构建或 Vite 单包静态资源,依赖控制台已有 `brief`/`--format table` 文案风格,不套产品 Admin UI。 +行为维持 P0:默认 dry-run;POST 等同 `--apply`。需 `docs:admin` + 任务 worktree。 -## 6. 与 P0 / 既有系统衔接 +--- -| 能力 | 状态 | +## 7. 前端 SPA 设计 + +### 7.1 技术选型 + +- **零构建**:单 `index.html` + 内联 CSS/JS(或同目录 `app.js`)。 +- 原生 `fetch`;轮询间隔默认 **10s**(可配置);可选后续 SSE(P1-e)。 +- 不引入 React/Vite;不依赖产品 `web/node_modules`。 + +### 7.2 线框(四区) + +```text ++------------------------------------------------------------------+ +| agentctl console daemon: ok queue: 0 [刷新] [10s] | ++-------------------------------+----------------------------------+ +| 1. 任务 / 租约 | 2. 队列与集成 | +| active tasks table | pending/claimed table | +| live runs | [Prepare…] 需选 task+确认 | +| held claims | [Enqueue…] 仅展示指引/复制命令 | ++-------------------------------+----------------------------------+ +| 3. 文档漂移 | 4. 清理积压 | +| MATRIX hard regressions | cleanup report table | +| catalog drift | 无删除按钮(只读) | ++-------------------------------+----------------------------------+ +| brief 预览(折叠) | ++------------------------------------------------------------------+ +``` + +### 7.3 区 ↔ 端点 + +| 区 | 数据 | 动作按钮 | +|---|---|---| +| 1 | `/api/status` + `/api/claims` | 「认领」→ modal → `POST /api/claim/new`(二次确认) | +| 2 | `/api/queue` + status.queue | 「Prepare」→ `POST /api/integrate/prepare`;Enqueue 默认复制 CLI(避免 SPA 误合入) | +| 3 | `/api/docs/drift` + `/api/catalog/drift` | 无写 | +| 4 | `/api/cleanup` | 无写 | +| 顶栏 | `/api/daemon` + `/api/brief` | 刷新 | + +### 7.4 UX 规则 + +- 所有 POST 前 modal:展示将发送的 JSON;确认后才发。 +- 409/403 用红色 toast + 原始 `code`。 +- 路径只显示文本,不 `file://` 链出(避免浏览器限制)。 + +--- + +## 8. 关键时序图 + +### 8.1 claim new + +```mermaid +sequenceDiagram + participant U as User_or_SPA + participant S as agentctl_serve_or_CLI + participant D as docs_apply + participant DB as SQLite + U->>S: claim_new task owner mutex + S->>S: snapshot CURRENT row + alt row_exists_without_replace + S-->>U: 400 INVALID_REQUEST + else + S->>D: apply single_row CAS + D->>DB: lease documents receipts + D-->>S: applied run_id + S-->>U: 200 created + end +``` + +### 8.2 integrate prepare → enqueue → daemon ff + +```mermaid +sequenceDiagram + participant U as User + participant P as integrate_prepare + participant V as validate + participant Q as queue_enqueue + participant Daemon as agentctl_daemon + participant Dev as refs_heads_dev + U->>P: expected_dev tip + P->>Dev: rev_parse + alt tip_mismatch + P-->>U: 409 BASE_CHANGED + else + P->>P: worktree_add_merge_no_ff + P-->>U: head first_parent + U->>V: unittest_or_scope_checks + V-->>U: validated + U->>Q: expected_dev head + Q->>Daemon: pending_item + Daemon->>Dev: ff_only_if_tip_matches + Daemon-->>U: merged_or_failed + end +``` + +### 8.3 pi packet(生成与派发分离) + +```mermaid +sequenceDiagram + participant U as User + participant Gen as pi_packet + participant Disk as report_assets + participant Run as pi_subagent_run + U->>Gen: role goal auth_ref + Gen->>Gen: freeze_source_sha + Gen->>Disk: optional_write_packet_json + Gen-->>U: packet + Note over U,Run: 另一步:用户或 skill 显式 run + U->>Run: packet_fields_plus_lease + Run-->>U: receipt_pending_verdict +``` + +--- + +## 9. 测试矩阵 + +验证层级:验证契约「工程工具验收」(非产品 PI)。 + +### 9.1 `scripts/tests/test_agentctl_serve.py`(实施时新增) + +| 用例 | 预期 | |---|---| -| `--format table` / `brief` / MCP 只读 / `claim archive` | **P0 done** | -| MCP 再暴露 `brief`、`claim_archive` | P1 可选 | -| `serve` + SPA | P1 | -| `claim new` / `integrate prepare` / `pi packet` | P1 | -| 跨机 claim 权威 | 见 coordination-db-daemon 方案,本控制台只读本地 SQLite | +| bind 127.0.0.1 成功;0.0.0.0 拒绝 | 启动失败码 | +| GET 八端点 200 且字段与 CLI collect 一致 | JSON 同构 | +| 未知 POST `/api/shell` | 404 | +| POST claim/new 缺 task | 400 | +| POST claim/new 无 docs:publish | 403 | +| 幂等:同 request_id 两次 | 第二次 replayed | +| 非 loopback 模拟(若可) | 拒绝或无法连接 | -## 7. 分阶段实施顺序 +### 9.2 CLI 黑盒(扩展 `test_agentctl.py` fixture) -1. **P1-a**:抽出 `collect_*` 为稳定库(P0 已部分完成)→ `agentctl serve` 只读 API + 极简 HTML。 -2. **P1-b**:`claim new` + 控制台认领表单。 -3. **P1-c**:`integrate prepare` + 队列区按钮。 -4. **P1-d**:`pi packet` + 可选把 packet 路径注入 `pi-subagent-dispatch` skill。 -5. **P1-e**(可选):把 SPA 挂进 Cursor 简单 Browser tab 工作流说明。 +| 命令 | 覆盖 | +|---|---| +| `claim new` | 新建行 / `--replace` / BASE_CHANGED | +| `integrate prepare` | first_parent==expected_dev;脏树拒绝 | +| `pi packet` | source_sha 拒绝符号名;`--write` 路径落盘 | -## 8. 验收标准(实施时) +复用临时 git 仓 + 拷贝 `agentctl*.py` 模式(与 P0 visibility fixture 一致)。 -- 只读 API 与 CLI 同构(同 payload 字段)。 -- 任一写动作在审计表留下 run_id;CURRENT/MATRIX 变更有 `docs apply` 收据。 -- `serve` 默认拒绝非 loopback;集成测试用临时端口。 -- 工程工具单测:`scripts/tests/test_agentctl_serve.py` + 既有 visibility 测试。 -- 不改产品 compose / 不占用 8080。 +### 9.3 SPA 手动验收清单 -## 9. 非目标 +- [ ] 四区数据在 daemon ok 时非空或显式 empty +- [ ] 轮询不导致写动作 +- [ ] Prepare 确认框取消无请求 +- [ ] 409 展示 code 文本 -- 产品 AdminConsole 内嵌治理页。 -- 远程多用户 Web 登录。 -- 自动 `--force` 清理 worktree。 -- 替代 Herdr / 产品 PI Runner。 +### 9.4 本设计任务验证(文档层) -## 10. 授权与下一步 +- 链接/锚点可达;mermaid 三角可渲染;与 P0 符号名一致。 +- **不**要求独立 `*-test-report.md`(NOT REQUIRED:纯设计文档)。 -本文件为方案;实施需单独授权并新开 task(建议 `governance-console-p1-YYYY-MM-DD`),先 `catalog overlap` 再编码。 +--- + +## 10. 分阶段实施 + +| 阶段 | 范围 | 依赖 | 验收 | +|---|---|---|---| +| **P1-a** | `serve` 只读 8 端点 + 极简 HTML 展示 status/brief | P0 `collect_*` | test_agentctl_serve 只读用例绿;本机浏览器打开 | +| **P1-b** | `claim new` CLI + POST + SPA 认领表单 | P1-a | 黑盒新建行;CURRENT 有收据 | +| **P1-c** | `integrate prepare` CLI + POST + SPA Prepare | P1-b | first_parent 断言;不自动 enqueue | +| **P1-d** | `pi packet` CLI + POST;skill 文档改为引用 packet | P1-a | packet JSON schema 单测;不触发 run | +| **P1-e** | SPA 打磨、可选 MCP `brief`/`claim_archive`、可选 SSE | P1-a…d | 手动清单勾完 | + +每阶段独立 task_id + catalog overlap + 工程工具单测;禁止跨阶段大揉。 + +--- + +## 11. 风险与缓解 + +| 风险 | 影响 | 缓解 | +|---|---|---| +| expected-dev 竞态(多会话并行合入) | enqueue 失败 | 明确 409/失败提示;prepare 可重入;不隐藏错误 | +| serve 闲置占端口 | 本机冲突 | 默认 7432;文档说明与产品 8080/8081 隔离 | +| `@document` 归档与并行 docs apply | BASE_CHANGED | archive 持 docs:admin;短租约;冲突则重跑 dry-run | +| SPA 误 enqueue 推进 dev | 错误合入 | SPA 默认不提供一键 enqueue;仅 Prepare | +| 与产品 web 目录混淆 | 发布门禁污染 | 静态资源仅 `scripts/governance-console/` | +| packet 被当成已派发 | 假 PASS | 生成不写 verdict;skill 强制 run_status≠叙事 | + +--- + +## 12. 衔接与授权 + +### 12.1 外部衔接 + +| 主题 | 文档 | 本方案关系 | +|---|---|---| +| 跨机权威 | [coordination-db-daemon phased plan](../../remediation/2026-09-23-coordination-db-daemon-phased-plan.md) | 不重复;控制台只读本机 SQLite | +| P0 可视化 | [governance-visibility-p0](./2026-09-24-governance-visibility-p0.md) | 本设计复用 collect/archive | +| Cursor hooks | 规则 `cursor-hooks` | serve 不经 hooks;CLI 写仍走既有钩子 | +| PI 执行器 | [pi-subagent-executor](../2026-09-24-pi-subagent-executor.md) | packet 字段对齐;不改执行器门禁 | + +### 12.2 实施授权门槛 + +1. 用户显式授权「实施 P1-a(或指明阶段)」。 +2. 新 task(建议 `governance-console-p1a-YYYY-MM-DD`)+ `catalog overlap`(相对本 R-ID / mutex `agentctl-console`)。 +3. 写路径限 `scripts/` + 必要 `.doc`;产品树禁止。 +4. 合入走 validate → queue enqueue;不 push。 + +### 12.3 设计修订 + +- `design_revision`: `governance-console-p1-r1` +- 变更本规格须更新 revision 并在实施 task 记录 `policy_revision` 差分。 + +--- + +## 附录 A. 符号索引(实施对照) + +| 符号 | 文件 | 状态 | +|---|---|---| +| `collect_status_payload` | `scripts/agentctl.py` | P0 已有 | +| `collect_cleanup_payload` | 同上 | P0 已有 | +| `collect_docs_drift_payload` | 同上 | P0 已有 | +| `collect_daemon_payload` | 同上 | P0 已有 | +| `collect_catalog_drift_payload` | 同上 | P0 已有 | +| `claim_archive` | 同上 | P0 已有 | +| `emit_payload` / `visibility.render_*` | agentctl + visibility | P0 已有 | +| `claim_new` | 待建 | P1-b | +| `integrate_prepare` | 待建 | P1-c | +| `pi_packet` | 待建 | P1-d | +| `agentctl serve` | 待建 | P1-a | +| SPA `index.html` | `scripts/governance-console/` | P1-a/e | + +## 附录 B. 端口与路径速查 + +| 项 | 值 | +|---|---| +| HTTP | `http://127.0.0.1:7432` | +| 静态根 | `scripts/governance-console/` | +| PI packet 根 | `.doc/test-reports/assets/pi-subagents///` | +| CURRENT 归档追加 | `.doc/archive/ops/2026-09-21-current-history.md` |