# 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，不接管人工指挥官。高风险 Code Queue recovery 的 ClaudeQQ 审批在本阶段只生成 200 字以内中文纯文本草案和 backend-core proxy 命令，不走本机 skill、powershell 或本地 ClaudeQQ server。这里的 `contract` 是历史接口名，不代表合同测试入口。

当前允许存在一个 operator-run 的高鲁棒 stdio 保活形态，用于在人工授权下让 host Codex 持续承担指挥官工作。该形态不是仓库内正式 daemon，也不替代 skeleton dry-run surface；它应被视为过渡运行方式，后续产品化仍必须回到本文件的 state、approval 和安全边界。

## 边界

- `host-codex-commander` 是独立的本地 skeleton，不是运行中的 live daemon。
- 只允许本地文件状态、trace 摘要和审批草案预览；审批通知草案必须是 200 字以内中文纯文本，不使用 Markdown。
- 所有输出必须 redaction token/secret/URL credential。
- 不得重启或接管 Code Queue backend，不得 cancel/interrupt 运行任务，不得打印 token 明文。
- ClaudeQQ 正式发送未在 skeleton 中实现；如后续获得授权，唯一允许路径是 UniDesk backend-core microservice proxy：`bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw`。
- 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。
- `pikasTech/unidesk#20` 只作为 UniDesk commander / Code Queue / CLI / infra governance 入口；HWLAB 用户和产品 issue 必须路由到 `pikasTech/HWLAB`，不能作为 #20 看板行维护。

## 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`。

GPT-5.5 PR/收口类任务的边界由指挥官在 AgentRun 派单前直接审查，prompt 必须写清 PR 创建/更新、自合并/关闭、rebase/update/冲突处理、repo-owned CI/CD、artifact build/publish、DEV rollout、PROD/secret/DB/破坏性回滚禁止范围和 closeout 字段。

## 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；当 GPT-5.5 runner 的 prompt 明确包含普通 PR 收口授权，且 PR 不涉及 prod/runtime/release/security/database/破坏性回滚、无冲突并通过任务要求检查时，runner 可以自行用 repo-owned GitHub merge/close 路径完成收口并报告 SHA。高风险、边界不清或用户/指挥官保留 final action 的 PR 仍由 host commander 审查。

DEV 发布职责必须区分镜像构建/发布与滚动上线。runner 可以在任务内完成实现、验证、PR、自合并、镜像构建、artifact publish 和报告镜像 tag/digest；同一 DEV 环境的 rollout/apply/verify owner 默认是 host commander。host commander 应在已构建镜像和 artifact 证据就绪后，通过 repo-owned 的唯一 DEV CD 入口和发布锁执行滚动上线、等待 rollout、复核 live health，并更新对应 issue/看板。除非 prompt 明确授权，runner 不应自行竞争 DEV CD lock、重复执行 deploy apply 或发起互相覆盖的 rollout。

HWLAB 业务目标、验收和优先级属于 `pikasTech/HWLAB#7`；UniDesk 指挥治理、队列监督、并发窗口和 runner/PR handoff 属于 `pikasTech/unidesk#20`。#20 可以链接 HWLAB #7 的证据和状态，但不能替代 HWLAB 业务 issue，也不能把 HWLAB 代码实现决策写成 UniDesk 指挥规则。

host commander 路由 HWLAB 工作时必须遵循 `docs/reference/code-queue-supervision.md` 的 HWLAB / #20 监督口径：HWLAB 产品和用户交付进入 `pikasTech/HWLAB` issue/PR 与 #7 看板，UniDesk#20 只记录指挥控制、Code Queue、CLI、基础设施和治理态势。

host commander 不直接编辑 HWLAB 业务代码，不以本地热修绕过 HWLAB runner PR。只有用户明确授权的 PR 收口、基础设施热修或 runner 权限缺口可以例外；例外必须最小化变更，在相关 issue/PR 中留下命令、diff、验证证据和影响范围，并创建或更新 follow-up issue 消除该例外原因。

## Dev 验证计划

`commander smoke --dry-run` 是无 daemon smoke plan。它只输出验证计划，不启动 HTTP daemon、不打开 SSH/PTY/stdio bridge、不发送 ClaudeQQ、不重启服务、不 interrupt/cancel 任务、不部署、不跑全量 check/e2e。

指挥官派发或审阅任何 `DEV smoke` 时，必须沿用 `docs/reference/code-queue-supervision.md` 的 DEV 测试授权分级：未显式授权的 smoke 默认是 `read-only`；读取 live DEV 状态必须标成 `live-read`；触发 deploy、rollout、task、operation、audit、evidence 或硬件/虚拟硬件链路的 smoke 必须标成 `live-mutating`，并在 prompt 中逐项列出授权命令和 closeout 证据要求。

需要验证的 source summary 面：

- 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`、`dryRunNoClaudeQqSend=true`、`notificationDraft.message` 为 200 字以内中文纯文本、`notificationPath.error=notification-path-unavailable`，并返回 backend-core `microservice proxy claudeqq /api/push/text` 命令；禁止 POST ClaudeQQ。
- SSH bridge boundary：只检查 `commander plan --dry-run` 中 `bridge.mutation=false`、`startPlan.enabled=false` 和 `safetyBoundary.phaseOneMutationAllowed=false`；禁止打开 SSH、PTY 或 stdio bridge。

默认 dry-run 验证入口是：

```bash
bun scripts/cli.ts commander smoke --dry-run
```

## HTTP

| Method | Path | 说明 |
| --- | --- | --- |
| GET | `/health` | 返回 service id、启动时间、state root 和日志文件 |
| GET | `/api/commander/contract` | 返回机器可读 dry-run surface |
| 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`。

`commander approval request --dry-run` 的 ClaudeQQ 字段必须包含：

- `notificationDraft.message`：给用户看的 200 字以内中文纯文本审批草案，一段话，不含 Markdown。
- `dryRunNoClaudeQqSend=true`：确认 dry-run 不发送。
- `notificationPath.error=notification-path-unavailable`：说明当前 skeleton 未开放真实发送。
- `notificationPath.backendCoreProxyCommand`：授权后才可执行的 repo-owned 发送命令，路径固定为 `bun scripts/cli.ts microservice proxy claudeqq /api/push/text --method POST --body-json '<payload>' --raw`。

不得使用 `/root/.agents/skills/claudeqq`、`powershell.exe`、本地 skill server 或 9082 端口作为高风险审批通知路径。若 backend-core ClaudeQQ proxy 发送失败，失败必须结构化为 `notification-proxy-failed` / `microservice-proxy-failed`，不得打印 token，不回滚已经完成的 GitHub issue 更新；指挥官应把失败原因写入 #24 或对应 blocker issue。

## 进入真实运行态前

启用 daemon、PTY/stdio bridge、SSH bridge 或 ClaudeQQ 发送路径前，必须先获得人工授权。授权必须绑定一个精确 action 和目标 session/task/service，已有 smoke/skeleton dry-run 检查通过，风险审查确认不会打印 token、不会直接 patch 数据库、不会绕过 backend 确认策略，并且已有可审计的 approval id、回滚步骤和观测步骤。ClaudeQQ 发送只能通过 backend-core `/api/microservices/claudeqq/proxy`，并设置超时和结构化失败输出。
