Files
Agent-System/.doc/ops/short-term/2026-09-24-governance-console-plan.md
T
weichen fad32cd820 docs(ops): finalize governance console P1 implementable design
Expand the P1 plan to a 12-section implementable spec (serve API,
claim new, integrate prepare, pi packet, SPA, tests, risks) without
code. Task record and CURRENT claim included.
2026-09-24 13:54:22 +00:00

23 KiB
Raw Blame History

title, task_id, status, created, updated, owner, task_type, related_p0, design_revision, base_dev_sha
title task_id status created updated owner task_type related_p0 design_revision base_dev_sha
治理控制台全景设计(P1 定稿) governance-console-p1-design-2026-09-24 planned 2026-09-24 2026-09-24 Cursor research governance-visibility-p0-2026-09-24 governance-console-p1-r1 4844041b1b

治理控制台全景设计(P1 定稿)

覆盖方向:A 状态可视化、B 流程自动化、C MCP 只读巡览、D PI 派发包生成。
本文件是可实施规格;实施须另开 task + catalog overlap。本轮不写代码。

前置:P0 已合入 dev@4844041b(--format table、brief、MCP +4、claim archive)。证据:测试报告。


1. 现状与缺口

1.1 P0 已交付(机器权威层可读)

能力 入口 核心实现
人类可读表 agentctl status|queue list|claim list|cleanup report --format table emit_payload + agentctl_visibility
一页简报 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

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。


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/ AdminConsole 内嵌治理页。
  • 绕过 agentctl with / docs apply 的文件写。
  • 自动 --force 清理 worktree、push、晋升 test/main。
  • 替代 Herdr / 产品 PI Agent Runner。
  • 把 serve 装进 systemd/launchd(保持手工前台进程)。

3. 总体架构

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 daemon 已装载 systemd/launchd 仅队列 ff-only;不承载 HTTP
agentctl serve 人工前台 / 可选 tmux 绑 127.0.0.1:7432;退出即停
SPA 由 serve 静态托管 零构建单页;无独立 Node 服务

3.2 组件职责

组件 路径(建议) 职责
agentctl serve 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 可选:brief、claim_archive、动作薄封装

4. 数据模型与 API 资源映射

4.1 治理 SQLite(只读投影,不改 schema)

表 关键字段 消费端点
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

CURRENT.md / FEATURE-MATRIX.md 仍是人类投影;机器权威在 SQLite + docs apply 收据。

4.2 只读 payload 契约(与 P0 collect_* 同源)

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 动作请求/响应信封

{
  "request_id": "uuid-or-client-supplied",
  "task": "governance-…",
  "authorization_ref": "optional-user-chat-ref",
  "...action_fields": {}
}

成功:

{"status": "ok|archived|prepared|created", "run_id": "…", "request_id": "…", "...": {}}

失败(HTTP 状态见 §5.3):

{"status": "denied", "code": "BASE_CHANGED", "message": "…", "...details": {}}

5. 后端 agentctl serve 详细设计

5.1 启动

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 竞态

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)

agentctl claim new --task "$ID" --owner Cursor --mutex <key> \
  [--branch feature/$ID] [--worktree .worktrees/$ID] \
  [--status in_progress] [--replace] [--request-id RID]

内部步骤

  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}。

拒绝码:WORKTREE_MISMATCH、CAPABILITY_REQUIRED、BASE_CHANGED、AMBIGUOUS_RECORD。

替代关系:取代 skill agentctl-worktree-setup 中「CURRENT 认领」三段手工;建 worktree / task register 仍由 skill 前半段负责(本命令不创建 git worktree)。

幂等:同 request_id replay;--replace 在 BASE_CHANGED 后需新 snapshot。

6.2 agentctl integrate prepare(B2)

agentctl integrate prepare --task "$ID" --expected-dev <full_sha> \
  [--integrate-id integrate-$ID] [--request-id RID]

前置:调用方 cwd 可为任务 worktree 或 main;函数内切换到新建 integrate worktree 执行 merge。

内部步骤

  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}。

不做:validate、enqueue(留给既有命令 / SPA 分步按钮,避免隐式合入)。

拒绝码:BASE_CHANGED、LOCK_BUSY、CAPABILITY_REQUIRED、GIT_ERROR、worktree 脏 → INVALID_REQUEST。

替代关系:压缩 skill agentctl-integrate-closeout 的「候选」段;后续仍 validate → queue enqueue。

6.3 agentctl pi packet(D1)

agentctl pi packet --task "$ID" --role research|planning|reviews|diagnosis|test_run|acceptance_check|report \
  --goal '…' --authorization-ref '…' \
  [--source-sha FULL] [--timeout-seconds 600] \
  [--input-file PATH]… [--approved-command 'argv…']… \
  [--request-id RID] [--write]

内部步骤

  1. 读 task 行 → worktree;source_sha = freeze_source_sha(worktree, arg or HEAD)(复用 pi_subagents.freeze_source_sha 规则:必须满 40/64 hex)。
  2. request_id = arg or uuid;校验 REQUEST_ID_RE。
  3. 组装 packet:
{
  "task_id": "…",
  "request_id": "…",
  "role_id": "research",
  "goal": "…",
  "authorization_ref": "…",
  "source_sha": "<40hex>",
  "input_files": ["…"],
  "timeout_seconds": 600,
  "approved_commands": [["python3","-B","-m","unittest", "…"]]
}
  1. 默认 stdout JSON;--write 时原子写入
    .doc/test-reports/assets/pi-subagents/<task>/<request>/packet.json
    (目录与 REPORT_ROOT_REL 一致),并要求 Lease path:.doc/test-reports/…。
  2. 不调用 pi_subagent_run——生成与派发分离。

拒绝码:UNKNOWN_TASK、INVALID_SOURCE_SHA、UNKNOWN_ROLE(对照模型配置角色表)、PATH_OUT_OF_SCOPE。

替代关系:减少 pi-subagent-dispatch 手工拼包;skill 改为「packet 路径 → run」。

6.4 claim archive(已有,HTTP 暴露)

行为维持 P0:默认 dry-run;POST 等同 --apply。需 docs:admin + 任务 worktree。


7. 前端 SPA 设计

7.1 技术选型

  • 零构建:单 index.html + 内联 CSS/JS(或同目录 app.js)。
  • 原生 fetch;轮询间隔默认 10s(可配置);可选后续 SSE(P1-e)。
  • 不引入 React/Vite;不依赖产品 web/node_modules。

7.2 线框(四区)

+------------------------------------------------------------------+
| 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

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

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(生成与派发分离)

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(实施时新增)

用例 预期
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 模拟(若可) 拒绝或无法连接

9.2 CLI 黑盒(扩展 test_agentctl.py fixture)

命令 覆盖
claim new 新建行 / --replace / BASE_CHANGED
integrate prepare first_parent==expected_dev;脏树拒绝
pi packet source_sha 拒绝符号名;--write 路径落盘

复用临时 git 仓 + 拷贝 agentctl*.py 模式(与 P0 visibility fixture 一致)。

9.3 SPA 手动验收清单

  • 四区数据在 daemon ok 时非空或显式 empty
  • 轮询不导致写动作
  • Prepare 确认框取消无请求
  • 409 展示 code 文本

9.4 本设计任务验证(文档层)

  • 链接/锚点可达;mermaid 三角可渲染;与 P0 符号名一致。
  • 不要求独立 *-test-report.md(NOT REQUIRED:纯设计文档)。

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 不重复;控制台只读本机 SQLite
P0 可视化 governance-visibility-p0 本设计复用 collect/archive
Cursor hooks 规则 cursor-hooks serve 不经 hooks;CLI 写仍走既有钩子
PI 执行器 pi-subagent-executor 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/<task>/<request>/
CURRENT 归档追加 .doc/archive/ops/2026-09-21-current-history.md