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.
This commit is contained in:
weichen
2026-09-24 13:54:22 +00:00
parent 4844041b1b
commit fad32cd820
3 changed files with 588 additions and 101 deletions
+1
View File
@@ -18,6 +18,7 @@
| owner | task | branch | worktree | mutex | status | | 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 | 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 | 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 | | 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 |
@@ -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)
@@ -1,174 +1,594 @@
--- ---
title: "治理控制台 P1 方案" title: "治理控制台全景设计(P1 定稿)"
task_id: "governance-visibility-p0-2026-09-24" task_id: "governance-console-p1-design-2026-09-24"
status: planned status: planned
created: 2026-09-24 created: 2026-09-24
updated: 2026-09-24 updated: 2026-09-24
owner: Cursor owner: Cursor
task_type: research task_type: research
related_p0: governance-visibility-p0-2026-09-24 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 派发包生成**。 覆盖方向:**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 ### 1.1 P0 已交付(机器权威层可读)
Browser (localhost) ──► static SPA ──► agentctl serve (127.0.0.1)
│ | 能力 | 入口 | 核心实现 |
Cursor MCP / CLI ────────────────────────────┤ |---|---|---|
▼ | 人类可读表 | `agentctl status\|queue list\|claim list\|cleanup report --format table` | [`emit_payload`](../../../scripts/agentctl.py) + [`agentctl_visibility`](../../../scripts/agentctl_visibility.py) |
agentctl 核心 | 一页简报 | `agentctl brief` | `collect_*` + `cmd_brief` |
(SQLite / leases / docs apply / queue / catalog) | 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` | | `agentctl daemon` | 已装载 systemd/launchd | 仅队列 ff-only;**不**承载 HTTP |
| 静态 SPA | 单页四区:任务租约 / 队列集成 / 文档漂移 / 清理积压 | **不进产品 `web/`**,避免发布门禁耦合;建议路径 `scripts/governance-console/` | | `agentctl serve` | 人工前台 / 可选 tmux | 绑 `127.0.0.1:7432`;退出即停 |
| CLI 增强 | `claim new`、`integrate prepare`、`pi packet` | 与 HTTP 动作共用同一 Python 函数 | | 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` | | `agentctl serve` | [`scripts/agentctl.py`](../../../scripts/agentctl.py) 子命令 | HTTP JSON + 静态文件 |
| GET | `/api/brief` | `cmd_brief` Markdown 或 JSON | | 动作实现 | `scripts/agentctl_console.py`(新建)或内嵌 | `claim_new` / `integrate_prepare` / `pi_packet` 纯逻辑 |
| GET | `/api/queue` | `queue list` | | SPA | `scripts/governance-console/index.html` | 四区仪表盘 |
| GET | `/api/claims` | `claim list` | | MCP 增补 | [`scripts/mcp/agentctl-mcp.mjs`](../../../scripts/mcp/agentctl-mcp.mjs) | 可选:`brief`、`claim_archive`、动作薄封装 |
| GET | `/api/cleanup` | `cleanup report` |
| GET | `/api/docs/drift` | `docs drift` |
| GET | `/api/catalog/drift` | `catalog drift` |
| GET | `/api/daemon` | `daemon status` |
### 3.2 动作白名单(均要求 `task` + 可选 `authorization_ref`) ---
| Method | Path | 行为 | ## 4. 数据模型与 API 资源映射
### 4.1 治理 SQLite(只读投影,不改 schema)
| 表 | 关键字段 | 消费端点 |
|---|---|---| |---|---|---|
| POST | `/api/claim/new` | 见 §4.1:snapshot→delta→apply 封装 | | `tasks` | id, owner, worktree, branch, state, scopes, capabilities | `GET /api/status` |
| POST | `/api/claim/archive` | 既有 `claim archive --apply` | | `runs` | id, task, state, pid, start, boot, resources | `GET /api/status` |
| POST | `/api/integrate/prepare` | 见 §4.2:候选 worktree + `--no-ff` | | `queue_items` | id, task, head_sha, expected_dev, priority, state | `GET /api/queue` |
| POST | `/api/pi/packet` | 见 §4.3:生成 dispatch JSON | | `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 ```bash
agentctl claim new --task "$ID" --owner Cursor --mutex <key> \ agentctl claim new --task "$ID" --owner Cursor --mutex <key> \
[--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` 1. `task_row` 校验:cwd == 注册 worktree;需 `docs:publish`(单行写 CURRENT,无需 admin)。
2. 若 `sha256` 已存在 → 拒绝或 `--replace` 显式更新 2. `docs_snapshot(CURRENT, record=task)`。
3. 构造一行 `| owner | task | … | in_progress |` 3. 若 `sha256` 非空且未 `--replace` → `deny('INVALID_REQUEST', 'row exists; pass --replace')`。
4. `docs apply`(单行 CAS;无需 `@document`) 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 ```bash
agentctl integrate prepare --task "$ID" --expected-dev <sha> \ agentctl integrate prepare --task "$ID" --expected-dev <full_sha> \
[--integrate-id integrate-$ID] [--integrate-id integrate-$ID] [--request-id RID]
``` ```
内部(仍在租约内): **前置**:调用方 cwd 可为任务 worktree 或 main;函数内切换到新建 integrate worktree 执行 merge。
1. 从 freeze 的 `expected-dev` 建 `feature/integrate-<id>` 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 ```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 '…' \ --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 ```json
{ {
"task_id": "…", "task_id": "…",
"request_id": "<uuid>", "request_id": "…",
"role_id": "research", "role_id": "research",
"goal": "…", "goal": "…",
"authorization_ref": "…", "authorization_ref": "…",
"source_sha": "<frozen>", "source_sha": "<40hex>",
"input_files": [], "input_files": ["…"],
"timeout_seconds": 600, "timeout_seconds": 600,
"approved_commands": [] "approved_commands": [["python3","-B","-m","unittest", "…"]]
} }
``` ```
写出到 `.doc/test-reports/assets/pi-subagents/<task>/<request>/packet.json`(路径仍须在 `pi_subagents` 租约约定内)。不自动 `run`——生成与派发分离,避免误触模型。 4. 默认 stdout JSON;`--write` 时原子写入
`.doc/test-reports/assets/pi-subagents/<task>/<request>/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 路径文本。 ### 6.4 `claim archive`(已有,HTTP 暴露)
2. **队列与集成**:pending/claimed;按钮「prepare」(调 `/api/integrate/prepare`,需确认)。
3. **文档漂移**:MATRIX hard regressions + catalog drift。
4. **清理积压**:`cleanup report` 表;仅展示,删除仍人工/`agent-worktree remove`。
技术选型建议:零构建或 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** | | bind 127.0.0.1 成功;0.0.0.0 拒绝 | 启动失败码 |
| MCP 再暴露 `brief`、`claim_archive` | P1 可选 | | GET 八端点 200 且字段与 CLI collect 一致 | JSON 同构 |
| `serve` + SPA | P1 | | 未知 POST `/api/shell` | 404 |
| `claim new` / `integrate prepare` / `pi packet` | P1 | | POST claim/new 缺 task | 400 |
| 跨机 claim 权威 | 见 coordination-db-daemon 方案,本控制台只读本地 SQLite | | 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` + 队列区按钮。 | `claim new` | 新建行 / `--replace` / BASE_CHANGED |
4. **P1-d**:`pi packet` + 可选把 packet 路径注入 `pi-subagent-dispatch` skill。 | `integrate prepare` | first_parent==expected_dev;脏树拒绝 |
5. **P1-e**(可选):把 SPA 挂进 Cursor 简单 Browser tab 工作流说明。 | `pi packet` | source_sha 拒绝符号名;`--write` 路径落盘 |
## 8. 验收标准(实施时) 复用临时 git 仓 + 拷贝 `agentctl*.py` 模式(与 P0 visibility fixture 一致)。
- 只读 API 与 CLI 同构(同 payload 字段)。 ### 9.3 SPA 手动验收清单
- 任一写动作在审计表留下 run_id;CURRENT/MATRIX 变更有 `docs apply` 收据。
- `serve` 默认拒绝非 loopback;集成测试用临时端口。
- 工程工具单测:`scripts/tests/test_agentctl_serve.py` + 既有 visibility 测试。
- 不改产品 compose / 不占用 8080。
## 9. 非目标 - [ ] 四区数据在 daemon ok 时非空或显式 empty
- [ ] 轮询不导致写动作
- [ ] Prepare 确认框取消无请求
- [ ] 409 展示 code 文本
- 产品 AdminConsole 内嵌治理页。 ### 9.4 本设计任务验证(文档层)
- 远程多用户 Web 登录。
- 自动 `--force` 清理 worktree。
- 替代 Herdr / 产品 PI Runner。
## 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/<task>/<request>/` |
| CURRENT 归档追加 | `.doc/archive/ops/2026-09-21-current-history.md` |