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.
23 KiB
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 目标
- 本机治理面:一眼看 tasks / runs / claims / queue / drift / cleanup。
- 高频动作白名单:
claim new、claim archive、integrate prepare、pi packet——全部复用既有租约与docs apply,不新增旁路写入口。 - CLI 与 HTTP 同构:同一 Python 函数;MCP 可薄封装。
- 可测:工程工具单测覆盖路由、错误码、幂等、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_applyreplay 一致——相同 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 安全
- Loopback only。
- 无 Cookie / Bearer;本机信任模型(与 CLI 相同)。
- 动作白名单硬编码;请求体禁止
command/shell/argv字段。 - 所有写动作写
runs/documents审计(既有 Lease)。 - 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]
内部步骤
task_row校验:cwd == 注册 worktree;需docs:publish(单行写 CURRENT,无需 admin)。docs_snapshot(CURRENT, record=task)。- 若
sha256非空且未--replace→deny('INVALID_REQUEST', 'row exists; pass --replace')。 - 构造一行:
| {owner} | {task} | {branch} | {worktree} | {mutex} | {status} |(worktree 用相对.worktrees/…)。 docs_apply单行 CAS(expected_sha256来自 snapshot;新建为 null)。- 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。
内部步骤
- 校验
expected-dev == rev-parse(refs/heads/dev),否则BASE_CHANGED。 integrate_id = integrate-id or f"integrate-{task}";branchfeature/{integrate_id}。- 若 worktree 不存在:调用与
agent-worktree.sh create等价逻辑(git worktree add -b … expected-dev)。 - 在 integrate worktree:
task register --capability git:dev(若已注册且匹配则跳过)。 - Lease(
path:.,git:index,git:dev) 内:git merge --no-ff -m "merge: candidate {task} into dev" feature/{task-branch}。 - 校验
HEAD^1 == expected-dev。 - 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]
内部步骤
- 读 task 行 → worktree;
source_sha = freeze_source_sha(worktree, arg or HEAD)(复用pi_subagents.freeze_source_sha规则:必须满 40/64 hex)。 request_id = arg or uuid;校验REQUEST_ID_RE。- 组装 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", "…"]]
}
- 默认 stdout JSON;
--write时原子写入
.doc/test-reports/assets/pi-subagents/<task>/<request>/packet.json
(目录与REPORT_ROOT_REL一致),并要求 Leasepath:.doc/test-reports/…。 - 不调用
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 实施授权门槛
- 用户显式授权「实施 P1-a(或指明阶段)」。
- 新 task(建议
governance-console-p1a-YYYY-MM-DD)+catalog overlap(相对本 R-ID / mutexagentctl-console)。 - 写路径限
scripts/+ 必要.doc;产品树禁止。 - 合入走 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 |