129 lines
9.3 KiB
Markdown
129 lines
9.3 KiB
Markdown
# Host Codex Commander Skeleton
|
||
|
||
本文定义 host Codex 指挥官的本地 skeleton 阶段。仓库内正式 `commander` CLI 当前只提供 `/health`、`/api/commander/contract`、`.state/commander/` 状态读写、trace summary dry-run 聚合和 approval draft preview;不接 live bridge,不发送 ClaudeQQ,不接管人工指挥官。
|
||
|
||
当前允许存在一个 operator-run 的高鲁棒 stdio 保活形态,用于在人工授权下让 host Codex 持续承担指挥官工作。该形态不是仓库内正式 daemon,也不替代 skeleton contract;它应被视为过渡运行方式,后续产品化仍必须回到本文件的 contract、state、approval 和安全边界。
|
||
|
||
## 边界
|
||
|
||
- `host-codex-commander` 是独立的本地 skeleton,不是运行中的 live daemon。
|
||
- 只允许本地文件状态、trace 摘要和审批草案预览。
|
||
- 所有输出必须 redaction token/secret/URL credential。
|
||
- 不得重启或接管 Code Queue backend,不得 cancel/interrupt 运行任务,不得打印 token 明文。
|
||
- Host commander 观察到 `bun scripts/cli.ts codex pr-preflight --remote` 返回 `scheduler-runner-env` / `auth-missing` 时,只能把它解释为 scheduler runtime preflight surface 缺少 GitHub auth;不得据此判定当前 active runner 或 dev container 不能 push、创建 PR 或评论 PR。
|
||
|
||
## PR 能力判读
|
||
|
||
PR 能力判读以 `docs/reference/code-queue-supervision.md` 的 runner preflight 规则为 source of truth。`authScopeSummary`、`scopeBoundary`、`activeRunnerDevContainer` 和 `recommendedActions` 必须同时保留:scheduler auth 与当前 CLI/dev container auth 是两个 scope,active runner 能力只能用当前 runner 内的 `bun scripts/cli.ts gh auth status --repo pikasTech/unidesk`、`gh pr create --dry-run` 和 `gh pr comment create --dry-run` 判定。
|
||
|
||
Host commander 可以把 scheduler-side `auth-missing` 记录为长期 auth-broker 或 runtime secret 缺口,但只有 active runner/dev-container 检查也失败时,才允许把本 turn 归类为无法创建 PR。所有诊断只报告 token 来源和存在性,不打印 token 值。
|
||
|
||
## CLI
|
||
|
||
```bash
|
||
bun scripts/cli.ts commander contract
|
||
bun scripts/cli.ts commander plan --dry-run [--session-id primary]
|
||
bun scripts/cli.ts commander smoke --dry-run [--session-id primary]
|
||
bun scripts/cli.ts commander approval request --action <action> --dry-run [--reason text] [--task-id id]
|
||
```
|
||
|
||
`plan`、`smoke` 与 `approval request` 必须显式使用 `--dry-run`,缺失时返回 `error=dry-run-required`。
|
||
|
||
## Operator-Run Stdio Loop
|
||
|
||
当前高鲁棒指挥官循环脚本固定放在 `/root/.unidesk/commander_loop.py`,工作目录使用 `/root/unidesk/`。它通过 `codex app-server --listen stdio://` 直连 Codex app-server JSON-RPC,而不是反复启动普通交互式 CLI;外层必须有持续循环和异常保护,fatal error 后自动重启,避免单次 stdio、网络或 app-server 错误让指挥官长期停止。
|
||
|
||
指挥 prompt 固定放在 `/root/.unidesk/commander_prompt.md`。脚本启动时读取该文件;运行中检测到 prompt 文件变化时,优先通过 `turn/steer` 注入当前 active turn。如果 steer 失败,脚本应记录失败、interrupt 当前 turn 并回到外层循环重启;不能静默忽略 prompt 变更。默认可以尝试 resume 指定 commander thread;resume 失败时必须降级为新 thread 继续运行。
|
||
|
||
前台输出应保持低噪声、单行化和可读:只显示时间、总运行时间、message、tool command start/completed/failed、stderr 和关键状态;完整 app-server JSON-RPC 事件不应直接刷屏。前台输出只是监督界面,分析和追责以 JSONL 文件日志为准。
|
||
|
||
完整日志必须写入 `/root/unidesk/logs/commander.log.<启动时间戳>.jsonl`,其中启动时间戳精确到秒,例如 `commander.log.YYYYMMDD_HHMMSS.jsonl`。后续分析时先用以下命令定位当前或最近一次运行日志:
|
||
|
||
```bash
|
||
ls -t /root/unidesk/logs/commander.log.*.jsonl | head -n 1
|
||
```
|
||
|
||
日志中应保留完整 JSON-RPC 事件和脚本派生事件,重点关注:
|
||
|
||
- `thread/resume/requested`、`thread/resumed`、`thread/start/requested`:确认是否复用了预期 thread。
|
||
- `turn/started`:确认当前 active turn。
|
||
- `prompt/loaded`、`prompt/changed`、`prompt/steered`、`prompt/steer_failed`:确认外部 prompt 是否真实注入。
|
||
- `item/completed` 中的 `agentMessage` 和 `commandExecution`:复盘 commander 说了什么、派了什么命令以及命令结果。
|
||
- `stderr`:定位 Codex core、stdio bridge 或工具路由错误。
|
||
|
||
该运行形态可以用于指挥 Code Queue、审阅任务、创建/更新 issue、PR 收口和必要的热修复例外,但不得被解释为普通 worker。涉及实现、测试和 PR 创建的常规工作仍应优先通过 Code Queue 派单;host commander 直接执行只适用于授权的 PR 收口、基础设施热修复或 runner 权限缺口,并且必须留下复盘和长期改进 issue。
|
||
|
||
## 指挥职责
|
||
|
||
host commander 的工作是调度、监督、steer、审阅和 PR 收口;它维护并发窗口、阻塞分类、issue/#20/#24 记录,并在 checks 和审阅通过后负责合并裁决或走已批准的合并路径。Code Queue runner 的工作是实现、验证、提交、push head branch 和创建 PR;runner 不合并自己的 PR。
|
||
|
||
HWLAB 业务目标、验收和优先级属于 `pikasTech/HWLAB#7`;UniDesk 指挥治理、队列监督、并发窗口和 runner/PR handoff 属于 `pikasTech/unidesk#20`。#20 可以链接 HWLAB #7 的证据和状态,但不能替代 HWLAB 业务 issue,也不能把 HWLAB 代码实现决策写成 UniDesk 指挥规则。
|
||
|
||
host commander 不直接编辑 HWLAB 业务代码,不以本地热修绕过 HWLAB runner PR。只有用户明确授权的 PR 收口、基础设施热修或 runner 权限缺口可以例外;例外必须最小化变更,在相关 issue/PR 中留下命令、diff、验证证据和影响范围,并创建或更新 follow-up issue 消除该例外原因。
|
||
|
||
## Dev 验证计划
|
||
|
||
`commander smoke --dry-run` 是无 daemon smoke contract。它只输出验证计划,不启动 HTTP daemon、不打开 SSH/PTY/stdio bridge、不发送 ClaudeQQ、不重启服务、不 interrupt/cancel 任务、不部署、不跑全量 check/e2e。
|
||
|
||
需要验证的 source/contract 面:
|
||
|
||
- health endpoint:用 `createCommanderRequestHandler` 和临时 `RuntimeConfig` 调用 `GET /health`,期望返回 `service=host-codex-commander`、`stateRoot` 和日志文件路径;禁止 `Bun.serve` 和端口监听。
|
||
- state file:只在临时目录写读 `sessions/<sessionId>.json`、`events/<sessionId>.jsonl` 和 `approvals/draft.json`,确认 session 状态和 redaction round-trip;禁止触碰真实 `.state/commander/`。
|
||
- trace summary dry-run:只喂 mock JSONL 给 `summarizeCommanderTrace`,确认 `taskId`、`sessionId`、`lastSeq`、`status`、`redactionsApplied` 和有界摘要;禁止读取 live Code Queue trace、标记已读、interrupt 或 cancel。
|
||
- approval draft preview:只运行 `commander approval request --dry-run` 或 `buildCommanderApprovalDraft`,确认 `requiresExplicitUserApproval=true`、`claudeqq.mutation=false`、`sendImplemented=false` 和敏感信息脱敏;禁止 POST ClaudeQQ。
|
||
- SSH bridge boundary:只检查 `commander plan --dry-run` 中 `bridge.mutation=false`、`startPlan.enabled=false` 和 `safetyBoundary.phaseOneMutationAllowed=false`;禁止打开 SSH、PTY 或 stdio bridge。
|
||
|
||
轻量契约测试是:
|
||
|
||
```bash
|
||
bun scripts/host-codex-commander-no-daemon-smoke-contract-test.ts
|
||
```
|
||
|
||
该测试只执行 CLI dry-run 和短命 source-level handler/helper,不启动长期进程。
|
||
|
||
## HTTP
|
||
|
||
| Method | Path | 说明 |
|
||
| --- | --- | --- |
|
||
| GET | `/health` | 返回 service id、启动时间、state root 和日志文件 |
|
||
| GET | `/api/commander/contract` | 返回机器可读 contract |
|
||
| GET | `/api/commander/sessions` | 读取本地 session 摘要 |
|
||
| POST | `/api/commander/state` | 写入本地 session state |
|
||
| GET | `/api/commander/trace-summary` | 对 mock trace JSONL 做 dry-run 摘要 |
|
||
| POST | `/api/commander/trace-summary` | 读取 mock trace JSONL 并更新本地 session 状态 |
|
||
| POST | `/api/commander/approvals` | 生成 approval draft preview 并落盘 |
|
||
|
||
## State
|
||
|
||
状态根目录固定为 `.state/commander/`,至少包含:
|
||
|
||
- `sessions/<sessionId>.json`
|
||
- `events/<sessionId>.jsonl`
|
||
- `approvals/<approvalId>.json`
|
||
- `logs/commander.jsonl`
|
||
|
||
session 状态只保留 `unknown`、`discovered`、`planned`、`starting`、`running`、`attention_required`、`stopping`、`stopped`、`degraded`。
|
||
|
||
## Trace summary
|
||
|
||
trace summary 输入 mock Code Queue trace JSONL 和可选 task summary,输出:
|
||
|
||
- `taskId`
|
||
- `sessionId`
|
||
- `lastSeq`
|
||
- `status`
|
||
- `keyEvents`
|
||
- `openQuestions`
|
||
- `recommendedNextActions`
|
||
- `redactionsApplied`
|
||
|
||
输出只做摘要,不返回 live transcript。
|
||
|
||
## Approval draft
|
||
|
||
高风险动作只生成 approval draft JSON / Markdown preview。preview 必须显示 redaction 结果,并明确 `sendImplemented=false`。
|
||
|
||
## 进入真实运行态前
|
||
|
||
启用 daemon、PTY/stdio bridge、SSH bridge 或 ClaudeQQ 发送路径前,必须先获得人工授权。授权必须绑定一个精确 action 和目标 session/task/service,已有 smoke/skeleton contract 通过,风险审查确认不会打印 token、不会直接 patch 数据库、不会绕过 backend 确认策略,并且已有可审计的 approval id、回滚步骤和观测步骤。
|