# Code Agent Chat Readiness Runbook

本文定义 Cloud Workbench `POST /v1/agent/chat` 的 readiness 判定。它只处理
Code Agent 回复链路是否具备复测条件，不修复、不读取、不打印、不创建也不修改任何
Secret 或 token。

## 运行边界

- DEV Cloud Web 入口是 `http://74.48.78.17:17666/`，同源代理到
  `/v1/agent/chat`。
- DEV API/edge 入口是 `http://74.48.78.17:17667/`；它不能替代 Workbench
  同源聊天入口的真实回复证据。
- `internal/cloud/code-agent-chat.ts` 是 `/v1/agent/chat` 的后端处理入口。
- `scripts/code-agent-chat-smoke.mjs` 是 Code Agent chat schema 与 readiness
  合同检查。
- `scripts/dev-cloud-workbench-smoke.mjs --static` 只验证 Workbench 源码合同和
  `/v1/agent/chat` 前端接线；它不是 DEV-LIVE 回复证明。

## Provider 前置条件

当前 v0.2 部署合同中，`hwlab-cloud-api` 把 Code Agent 执行委托给 AgentRun `v0.1`。运行时必须具备可用的 AgentRun manager、runner job、`ResourceBundleRef`、`SessionRef`/PVC、profile SecretRef 和目标 backend profile；如果该 backend 使用 Codex app-server，runner 内还必须证明 workspace、`CODEX_HOME`、Codex binary 和 native thread/session continuation 可用。Per-profile config/credential/dynamic slug 管理见 [spec-v02-provider-management.md](spec-v02-provider-management.md)，dispatch/session/child inheritance 见 [agentrun-code-agent-dispatch.md](agentrun-code-agent-dispatch.md)。

Codex token boundary 仍由授权路径把 `OPENAI_API_KEY` 注入到 DEV runtime。DEV Pod
不能直接指向 public `api.openai.com`。G14 默认 `deepseek` profile 指向
`http://hwlab-deepseek-proxy.<namespace>.svc.cluster.local:4000/v1/responses`；该
Service 必须先进入 `hwlab-deepseek-responses-bridge`，由 bridge 解压 Codex
Responses 的 zstd request body、规范化 `/v1/models` 返回、丢弃非 `function` tool，
再转发到同 Pod 内 Moon Bridge 4001。Moon Bridge 是 DeepSeek profile 的真实
Responses 转换和 prompt cache 保留层；不要在 HWLAB 中手写完整转换器替代它。
`codex-api` profile 是独立的 Codex/OpenAI-compatible Responses 通道。G14 上
`hwlab-cloud-api` 应把 `codex-api` base URL 指向同 Pod 的 `127.0.0.1` loopback
forwarder；forwarder 再直连 `hyueapi.com` / `.hyueapi.com`，并把这两个域名显式保留在
`NO_PROXY` / `no_proxy`。这不是 DeepSeek bridge，也不是公网 `api.openai.com`，不能把
hyueapi 流量改成 HTTP/SOCKS proxy。`http://172.26.26.227:17680/v1/responses` 只能作为
D601 legacy Code Queue runner 或历史 egress 的对照线索；迁移到 G14 后不得把它当作
G14 默认 `codex-api` base URL，也不得用 DeepSeek bridge 伪装 `codex-api` 通过。

Runner 不得尝试修补、读取、回显或替换该 Secret。若 DEV runtime 缺少该授权凭证注入，
`provider_unavailable` 且 `error.missingEnv` 包含 `OPENAI_API_KEY` 必须判为
`BLOCKED/credential`。

部署前和部署后的自动化只允许证明 env 名称、`secretKeyRef` 的 Secret 名和 key 名、以及
DEV egress/base-url 合同是否声明和保留；不得读取 Secret data，也不得把 Secret 值写入
report、issue、PR 或截图。

## Provider 切换排查方法论

Provider/profile 切换故障必须先在目标 pod/host 上打通最小真实闭环。普通 profile config/credential/validate 走 AgentRun 管理 API，不进入 GitOps render 或服务发布；只有共享 bridge/forwarder/runtime/env/SecretRef plumbing 变化才进入 CI/CD。`deepseek`、`dsflash-go`、`codex-api` 和未来 provider 共享 cloud-api 会话与 Workbench UI，但排查时必须把 profile config、SecretRef、认证、网络、模型、AgentRun runner 和 Codex CLI/app-server（若适用）逐层拆开，避免用一个 profile 的成功掩盖另一个 profile 的退化。

最小闭环按以下顺序分层，任何一层失败都不能跳到正式 CI/CD 试错：

1. 运行面确认：在 G14 `/root/hwlab` 与 G14 k3s 目标 pod 内确认当前分支、镜像、env overlay、
   `CODEX_HOME`、Codex 版本和 workspace；D601 只能作为 legacy 对照，不作为 G14 source truth。
2. 凭证边界：只检查 Secret 引用、`auth.json` 顶层 key 和值长度，不打印 secret。Codex CLI 的
   `auth.json` 应能暴露 `OPENAI_API_KEY` 顶层 key；形如 `auth` 的不明结构必须先按 blocker 处理。
3. 直连边界：`hyueapi.com` / `.hyueapi.com` 必须在 `NO_PROXY` 与 `no_proxy` 内。需要同时检查
   shell env 和 Codex/Rust trace；若 trace 显示 `network_proxy: None` 且直接连接 `hyueapi.com:443`，
   不能再把问题归类为 proxy 污染。
4. 裸 Responses API：在同一个目标 pod 内用同一份 auth、同一模型、同一 base host 发
   `/responses` 或 `/v1/responses` 流式请求，确认网络、认证和模型是否可用。裸 API 通过只证明
   upstream 可达，不等于 Workbench 或 Codex runner 通过。
5. Codex CLI 对照：用同一模型、同一 `CODEX_HOME`、同一 prompt 运行 `codex exec --json`；同时在
   D601 Code Queue runner 上做同模型对照，记录版本、config 形态、NO_PROXY、proxy env 和 transport
   摘要。D601 对照只用于定位差异，不能把 D601 路径写回 G14 默认运行态。
6. Loopback forwarder 对照：如果裸 Responses API 通过、Codex CLI 直连失败，并且 trace 已确认
   `network_proxy: None`，必须在同一个目标 Pod 内增加只监听 `127.0.0.1` 的临时 forwarder，使用同一份
   auth、同一模型和同一 prompt 复测 `codex exec` 与 `/v1/agent/chat`。forwarder 只能把流量转到可配置的
   hyueapi upstream，不能硬编码 D601 IP，也不能复用 DeepSeek bridge。
7. AgentRun command 闭环：最终证据必须来自真实 AgentRun command result，返回 `completed` 且 final assistant reply 非空，并暴露真实 backend/profile/provider/model。Node/raw HTTPS、stub、bridge、source-only smoke、前端状态都不能升级成 DEV-LIVE reply pass。

排查记录应保留稳定结论和判定方法，不写 Secret、一次性 trace 全文或日期化流水账。遇到
“裸 API 通过但 Codex CLI/app-server 失败”时，优先抓 Codex/Rust transport 日志并与 D601 成功
路径比对：模型、service tier、`auth.json` 结构、base URL、是否直连、实际连接 IP、请求体大小、
SSE 是否 completed。只有这些证据归一后，才修改源码、render、SecretRef 或发布配置。

## Codex API 转发根因

`codex-api` 在 G14 上采用 pod-local loopback forwarder 是一个受控传输边界修复，不是为了规避
hyueapi 直连要求。根因判定按以下证据链成立：

- 同一目标 Pod 内，使用同一份 auth、同一模型、同一 Responses payload，通过 Node HTTPS 直接请求
  `hyueapi.com` 能获得完整 SSE `response.completed`；这排除了模型、Secret、请求体、基本网络可达性和
  hyueapi 账号本身不可用。
- 同一目标 Pod 内，Codex CLI/Rust 传输直连 `https://hyueapi.com/responses` 会在 streaming 阶段断开；
  Rust trace 显示 `network_proxy: None` 且连接目标是 `hyueapi.com:443`，这排除了全局 proxy 污染和
  `NO_PROXY` 未生效作为主因。
- 把 Codex CLI 的 base URL 改为同 Pod `127.0.0.1`，再由 Node forwarder 使用直连 HTTPS 转发到
  hyueapi，`codex exec` 与 `/v1/agent/chat` 均能完成并返回非空 assistant 回复；这把失败边界收敛到
  Codex CLI 的 Rust HTTPS/SSE transport 与 G14->hyueapi 边缘路径组合，而不是 Workbench、DeepSeek、
  模型或 auth。
- D601 Code Queue runner 使用同模型可成功，只能证明 D601 legacy 路径可作为对照；不能把
  `172.26.26.227:17680` 写回 G14 默认配置。

因此，在不修改 Codex CLI 二进制、不要求 hyueapi 改边缘行为、也不把 hyueapi 流量送进 HTTP/SOCKS
proxy 的前提下，pod-local Node forwarder 是当前可控的最小修复。它的职责只是替换 Codex CLI 失败的
直接 HTTPS/SSE transport；hyueapi 仍由 forwarder 进程直连，`hyueapi.com` / `.hyueapi.com` 仍必须在
`NO_PROXY` 和 `no_proxy` 中。

## 转发器迁移与隔离

Codex API forwarder 必须是 Pod 内部能力，推荐作为 `hwlab-cloud-api` 同 Pod sidecar 或等价的同 Pod
受控进程运行。`hwlab-cloud-api` 只连接 `http://127.0.0.1:<port>`；forwarder 通过 env 配置 upstream，
默认 upstream host 为 `hyueapi.com`。以下约束保证它可迁移且不会污染其他运行面：

- 禁止硬编码 provider host IP、D601 `172.26.26.227`、G14 节点 IP、namespace 名或 NodePort。可配置项只应是
  loopback listen port、upstream base URL、模型 profile env 和 Secret 引用。
- forwarder 不创建 Kubernetes Service、Ingress、NodePort 或 host port；它只监听 Pod network namespace 内的
  `127.0.0.1`。同一 k3s 集群内 `hwlab-dev`、`hwlab-prod` 或其他 namespace 可以同时各自运行一个
  `hwlab-cloud-api` Pod 和同端口 forwarder，因为每个 Pod 都有独立 loopback。
- 不要给 `hwlab-cloud-api` Pod 启用 `hostNetwork` 来承载 forwarder。若某个迁移目标必须使用 hostNetwork，
  必须重新评估端口冲突和隔离边界，不能沿用“Pod 内同端口无冲突”的结论。
- 迁移到其他 k3s 时，只需要保证目标 Pod 能直连 `hyueapi.com:443`、SecretRef 仍以 `OPENAI_API_KEY` 注入、
  `CODEX_HOME/auth.json` 形态正确、`NO_PROXY/no_proxy` 包含 `hyueapi.com` 与 `.hyueapi.com`，以及
  loopback forwarder 进程跟 `hwlab-cloud-api` 在同一 Pod network namespace。
- DEV、PROD 和临时 smoke Pod 的证据必须分开记录。DEV forwarder 通过不能自动证明 PROD 通过；PROD
  需要在 PROD namespace 的目标 Pod 内复跑最小 `/v1/agent/chat` 闭环。

## 自动化兼容性

Forwarder 不需要人工维护长驻进程。正式固化后，它应由 Kubernetes Deployment 管理生命周期：Pod 创建时随
`hwlab-cloud-api` 启动，Pod 删除时一起退出，异常退出由 kubelet 按 Pod/容器 restart policy 重启。人工只允许在
临时 smoke Pod 中手动启动 forwarder 做分层诊断；运行态不应依赖手动 `kubectl exec` 后台进程。

自动化合同验证（一次性检查，非长期门禁。门禁最小化见 AGENTS.md P0）：


- `codex-api` profile base URL 指向 Pod-local loopback，不指向 D601 IP 或 `api.openai.com`。
- forwarder upstream base URL 默认 `hyueapi.com`，`NO_PROXY/no_proxy` 包含 `hyueapi.com` / `.hyueapi.com`。
- `hwlab-cloud-api` Pod 包含 forwarder sidecar，forwarder 不暴露 Service/NodePort/Ingress/hostPort。
- `deepseek` profile 仍指向 DeepSeek bridge/Moon Bridge；`codex-api` profile 不依赖 DeepSeek bridge。

完整 CI/CD、GitOps render 或发布流水线只能在目标 Pod 最小闭环已经通过、且当前 CI/CD 基础设施可用时继续。
最小闭环没有通过时，不要通过反复推送 CI/CD 来探索 provider transport；CI/CD 只能固化已经在目标运行面证明可行的
配置和代码。

## 判定标准

| 观测结果 | readiness |
| --- | --- |
| `status: "failed"`，`error.code: "provider_unavailable"`，且 `error.missingEnv` 包含 `OPENAI_API_KEY` | `BLOCKED/credential`；provider 凭证缺失，不能标真实回复通过。 |
| AgentRun dispatch/runner/profile blocker，或 blocker 包含 `runner_lifecycle_missing`、`workspace_mount_missing`、`workspace_write_boundary_blocked`、`codex_cli_binary_missing`、`codex_cli_not_executable`、`codex_home_missing`、`provider_token_boundary` | 真实 AgentRun runner / backend profile 未具备；必须按 blocker 处理，不能表述为完整 Code Agent session。 |
| `status: "completed"`，但来自 mock、fixture、本地 stub、source-only smoke、浏览器本地回显或人工拼接 | 不是 DEV-LIVE reply pass。 |
| 真实 DEV `POST /v1/agent/chat` 返回 `status: "completed"`，且 `reply.content` 是非空 assistant 回复 | 可标 DEV-LIVE reply pass。 |
| 传输失败、schema 不完整、HTTP 非预期、`reply.content` 为空或缺失 | `BLOCKED`，按 runtime/schema/transport 分析。 |

只有“真实 DEV 路由 + `completed` + 非空 assistant reply”能作为 DEV-LIVE 回复通过依据。
不得把 mock、fixture、本地 echo、source report、静态检查或前端状态当作通过。

`agentrun-v01` 只能表示执行基础设施，不等同于真实 provider/model。Code Agent result `completed` 要被 Web 或 CLI 标记为真实完成，必须同时具备 final assistant response、真实 provider/model/backend profile、`providerTrace`、traceId、conversationId/sessionId 和可回放 trace；否则应显示“完成证据不足”，不能用 SOURCE、fixture、echo、mock 或 stub 补齐。AgentRun adapter 应把真实执行形态暴露为具体 backend profile，例如 `dsflash-go`、`deepseek`、`codex-api` 或 `minimax-m3`，把 `agentrun-v01` 保留在 adapter、runner 或 infrastructure 字段里。

同一 conversation/session 的后续消息必须在 AgentRun runner reuse window 有效时进入既有 run/runner 的新 command/turn，避免每次消息都重新 bundle、重新启动 runner、再把历史 thread 当作新 runner 可 resume 的 rollout。每条消息都重新 bundle 或启动新 runner 是功能缺口，不得只靠 trace 说明包装成通过。只有 run 已 terminal、runner reuse window 过期或 AgentRun manager 明确拒绝复用时才允许新建 runner；trace/result 可以说明原因，但该说明只是诊断证据，不替代复用能力本身。

Trace 展示问题按“同源 row 转换优先”排查：先用 `hwlab-cli client agent trace <traceId> --render web` 输出 Cloud Web 同一 trace row 摘要；CLI 也乱说明 row 转换逻辑问题，CLI 正常而浏览器乱再查 DOM、CSS、滚动和增量 patch。

## 自然语言单一路由

`/v1/agent/chat` 的自然语言请求唯一执行路径是 AgentRun v0.1 delegated Code Agent command。cloud-api 不再把自然语言预分类到 M3 Skill CLI、`/v1/m3/io`、
`external.network.check`、`session_context`、`security.hardware-boundary`、
`hardware.invoke.shell` shortcut 或 OpenAI text fallback。

自然语言里即使出现 M3、DO/DI、DAP、PWM、gateway、box-simu、patch-panel、Keil、
serial-monitor、Windows skill、串口、下载、烧录、启动日志等词，也必须把完整请求交给
AgentRun Code Agent turn。Agent turn 自己根据仓库、skill 文档和可用工具决定调用 repo wrapper、
Windows skill CLI、项目脚本或其他真实可达路径；cloud-api 只负责 session 生命周期、trace、
result 轮询和 schema 化返回。

如果 AgentRun Code Agent 不具备运行条件，`/v1/agent/chat` 只能返回 AgentRun readiness
blocker，不能降级到 M3 Skill CLI、受控硬件 shortcut、外网专用检查或普通 OpenAI 文本回复。
显式 `/v1/m3/io` 控制面可以作为独立 API 或 UI 控制面继续存在，但聊天自然语言不得自动路由
到该 API，也不得保留要求自然语言先满足 M3 白名单的源码检查或测试。该显式控制面也不得在
进入 gateway 前保留固定 `DO1/DI1` 或固定 gateway 身份白名单预拦截；真实下游执行失败可以
返回执行失败，但不能由 cloud-api 用旧白名单提前拒绝。

显式持久 session 是默认合同：用户、Workbench 或 CLI 必须先显式创建或选择 Code Agent
session，之后同一个 `conversationId/sessionId` 才能映射到 AgentRun `SessionRef`/PVC/thread 和固定 workspace。刷新前端、重新打开页面或短连接 result 轮询只能恢复已显式选中的 session，
不得创建新的短期 runner，也不得在无 session 时自动生成 `conversationId/sessionId/threadId`。
没有已选 session 时，`/v1/agent/chat` 必须返回 `session_required`；session failed/stale/canceled
时必须返回 `session_not_usable` 或等价 blocker。失败 session 保留 trace、thread、partial output
和错误证据，不自动滚动、不隐式清理后继续；继续工作前必须显式创建或选择另一个 session。
除非运行面无法恢复，usable session 的 workspace、thread/session 绑定和
可见 trace 应持续存在。Workbench 前端可以持久化这些会话标识和最近消息，但只作为显式 session
selection 的缓存；用户显式清空对话或登出时才清除该本地状态。

## PC Gateway Windows Skill 调用

Code Agent 通过已登记 PC gateway 执行 Windows 侧命令时，必须让 Codex turn 自己调用仓库 wrapper，不能由 cloud-api 字符串匹配短路到 gateway：

```sh
node /app/tools/hwlab-gateway-shell.mjs --json --timeout-ms <ms> --powershell-stdin <<'PS1'
<bounded PowerShell script>
PS1
```

PowerShell 默认使用 wrapper 的 `-EncodedCommand` 路径；不要手写 `cmd /c powershell ...` 的管道、引号或中文转义。简单 `cmd` 命令仍可用 `--command "cmd /d /s /c ..."`，但涉及目录枚举、Unicode、管道、排序或 JSON 输出时优先用 `--powershell-stdin`。

Workbench 会把“Gateway 命令超时”控件的毫秒值随 `/v1/agent/chat` 传入 `gatewayShellTimeoutMs`。Codex prompt 必须把该值落实到 wrapper 的 `--timeout-ms`，cloud-api `hardware.invoke.shell` dispatch timeout 必须取环境配置、请求 `input.timeoutMs` 和 120s 默认值中的较大值并加 grace；不得让 20s/30s 的旧默认提前返回 `dispatchStatus=timed_out`。wrapper 自身 HTTP request timeout 要比 shell timeout 稍长，确保用户看到的是 gateway/cloud 返回的结构化 `status/operationId/dispatch`，不是 wrapper 先超时丢失结果。

调大 timeout 不能替代正确的长任务控制语义。Gateway poll loop 必须支持后台 in-flight 执行，长 Keil/UV4 命令运行期间仍能处理短 `job-status`、state/log 读取和健康探测；如果 trace 出现 `shellExecuted=false` 的 dispatch timeout，优先检查 gateway 是否队头阻塞或离线，而不是把所有 wrapper 调用改成长等待。

Workbench trace 对已知 JSON-RPC gateway 响应应按普通 tool call 展示：前端首行用中性 `tool gateway.shell status=<status> op=<operationId> exit=<exitCode> s=<duration>`，正文展示 request、gateway/resource/capability、dispatch、command、audit/evidence 以及有界 stdout/stderr。不要把整段 JSON 原样刷屏；复制/下载完整 trace 仍保留原始 JSON。

## 短连接 result 轮询

## AgentRun/Codex Runner 请求处理与 trace 可见性

当 AgentRun backend 使用 Codex app-server stdio 时，runner client 必须把 app-server 发来的 JSON-RPC client request 当作一等协议处理，不能只等待 notification。请求 id 可能是数字也可能是字符串；只支持数字 id 会导致 approval/requestUserInput 等请求永远得不到响应，最终表现为已有 assistant partial output 后卡在 `waitingFor=app-server-notification`，直到 idle timeout。

非交互 HWLAB Code Agent 的处理规则如下：

- `item/commandExecution/requestApproval`、`item/fileChange/requestApproval` 和 `item/permissions/requestApproval` 必须自动返回可用的 approved 决策，优先使用 `availableDecisions` 中的 `approved`、`approved_for_session` 或其他 `approved*`，不能因为旧 approval gate 把真实工具调用拒绝掉。
- `item/tool/requestUserInput` 和 `mcpServer/elicitation/request` 必须返回结构化 denied/canceled；聊天运行中不能弹人工输入，也不能静默等待用户。
- 未知 client request 必须返回 JSON-RPC unsupported error，并在 trace 中记录 `client/request:unsupported`；禁止忽略请求。
- 每个已处理 client request 都必须追加 `client/request/handled` trace 事件，包含 method、decision、itemId/targetItemId，并把 `waitingFor` 推进到 `turn/completed`，避免前端只显示未知 `providerTrace 缺失` 或 `app-server-notification`。

commandExecution trace 必须 bounded：命令文本和 stdout/stderr summary 默认只展示有界摘要，但必须保留 `commandBytes`、`commandTruncated`、`outputBytes` 和 stderr 摘要，复制/下载 trace 仍可按后端保留策略展开原始事件。前端和 CLI 不能为了避免输出爆炸而要求 Code Agent 默认加 `| head`、`grep` 或 shell pipe；输出裁剪应在 trace/result 层完成，不能把 stdout pipe 行为变成工具调用是否完成的隐式前提。

在 Linux container 内启动 Codex app-server 时，应优先直接执行 `@openai/codex-linux-*` native binary，并把同包 `path` 目录加入 `PATH`；只有找不到 native binary 时才回退到 npm wrapper。直接启动 native binary 可以减少 Node wrapper 双进程、孤儿进程和 close 后未清理进程组导致的 session 卡死风险。关闭 stdio client 时必须终止整个子进程组，不只关闭父进程 stdin。

Workbench 与 Code Agent 的用户请求必须是短连接 submit + 短连接 result/trace 轮询；浏览器或 cloud-web 不应持有一次长 HTTP 请求等待整个 Codex turn 结束。`POST /v1/agent/chat` 返回 `202` 后，前端轮询 `/v1/agent/chat/result/<traceId>` 获取终态，并用 `/v1/agent/chat/trace/<traceId>` 刷新可视 trace。

Code Agent backend 的 completed 语义只能来自真实 Codex app-server `turn/completed` 成功事件。`item/agentMessage/delta`、`item/completed`、已有 assistant 文本、transport close 或 activity idle timeout 都不能单独升级成 `status: "completed"`。如果已经收到部分 assistant 文本但没有收到 `turn/completed`，终态必须是 timeout/partial blocker，并保留 trace、session、thread、partial output 摘要和可重试提示；Workbench 只能显示“部分回复/超时”，不能标 DEV-LIVE reply pass。

`Codex app-server transport closed after partial assistant output but before turn/completed` 不能单独判定为 CI/CD 滚动中断。排查必须同时看三类证据：失败 trace 的 `providerTrace`/`runnerTrace`、对应 `hwlab-cloud-api` Pod 的 restart/age、以及同一时间窗口内的 Kubernetes rollout/kill 事件。若 Pod 在 trace 开始前已经稳定且 `RESTARTS=0`，应归类为 Codex app-server transport/session 失败，提示用户显式创建新 session 后重试；只有 trace 时间窗口内存在当前会话所在 Pod 的删除、重建或 restart 证据时，才归类为滚动导致的中断。失败响应也必须尽量保留 `providerTrace`，即使 `terminalStatus=failed`，避免前端把“providerTrace 缺失”误报成未知降级。

cloud-web 同源代理必须把短连接语义原样转发给 cloud-api，至少包括 `Prefer: respond-async`、`X-HWLAB-Short-Connection` 和 `X-Trace-Id`。如果这些 header 在 cloud-web 层被过滤，cloud-api 会把同一个请求当成长同步请求处理，用户入口会表现为 `17666` 卡住或代理超时，而 `17667` 直连 cloud-api 正常。此类问题应先比对同一 trace 在 `17666` 与 `17667` 的 submit 行为，再修代理 header 透传，而不是调大前端等待时间。

`/v1/agent/chat/result/<traceId>` 是终态摘要接口，不是完整 trace 下载接口。它可以携带压缩后的 `runnerTrace` 窗口用于传输保活，但 Workbench 用户界面不得把该窗口显示为“压缩窗口”或“显示全部”。只要结果或轮询快照声明 `eventsCompacted=true`，前端必须自动请求 `/v1/agent/chat/trace/<traceId>` 并用完整 trace 替换可视事件线；回放完成前只能显示“完整 trace 回放中/当前已载入”状态。result 响应仍必须保留 `eventCount`、`lastEvent`、`providerTrace`、`threadId/sessionId` 和终态 reply/blocker；完整 trace 只能从 `/v1/agent/chat/trace/<traceId>`、复制 JSON 或下载 trace 入口取得。默认 result trace 窗口上限由 `HWLAB_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT` 控制；不要把数百个大 chunk 原样塞进 result 响应，避免 cloud-web 代理层或浏览器 fetch 把“正常执行中的大响应”表现成 503、非 JSON 或空响应。

result 轮询的 408/425/429/5xx、浏览器 timeout、非 JSON 或空响应应按“可恢复传输抖动”处理：前端先拉取一次 trace 刷新活性，再带退避继续轮询，只有后端返回结构化 terminal blocker、真实终态失败，或 trace 按无新事件 idle timeout 超时，才向用户显示失败。只要 `/trace` 仍显示新事件或 `waitingFor` 仍在推进，就不能把一次 result poll 失败标成“Code Agent API 错误”并停止。

Workbench 的 trace 展示不是终态真相。只要 trace 中出现 `result:completed`、`result:failed`、`result:canceled`、`session:session_busy` 这类 terminal event，或浏览器从本地状态恢复出带 `traceId` 的非终态消息，前端必须再请求 `/v1/agent/chat/result/<traceId>` 做 result reconciliation，并用 result 的 reply/blocker/session/providerTrace 替换消息卡片。仅回放 `/trace` 不能把旧 running 消息改成完成，也不能把 `Code Agent result is ready for short-connection polling.` 当作用户可见最终回复。

长耗时和前后端协同缺陷必须优先用 mock/fixture 复现，不得把真实 token 调用作为日常回归测试。后端单元测试使用 fake Codex app-server JSON-RPC client 模拟 `thread/started`、`turn/started`、assistant delta、command output、`turn/completed` 缺失、transport close 和 idle timeout；前端测试使用固定 `/result`、`/trace`、EventSource、localStorage fixture 验证 result reconciliation、刷新恢复、transient poll error 和 compact trace 回放。真实 `--live` Code Agent smoke 只作为显式授权的 DEV 验证，不进入默认 PR 单元测试路径。

Windows 侧 skill、编译器、脚本工具和多参数命令都应走同一个通用传输模式，不新增某个工具的专用 wrapper 子命令：

- 工作目录优先用 wrapper 的 `--cwd <windows path>` 或脚本内 `Set-Location -LiteralPath <path>` 表达，不要在 prompt 里拼 `cmd /c "cd ... && ..."`。
- 命令参数在 PowerShell 脚本里用数组或显式变量传递，例如 `$args = @("subcommand", "-p", $projectPath)` 后 `& $exe @args`，避免让模型手动嵌套引号。
- 对所有 Windows skill 都先复用 `C:\Users\liang\.agents\skills\<skill>\SKILL.md` 和该 skill 自带 CLI；Code Agent 只负责通过 repo wrapper 调用，不把 skill 行为复制到 cloud-api 或 wrapper。
- 一次探测命令失败后，只做一次更窄、更结构化的修正；若仍失败，返回失败 `operationId`、stderr 摘要和下一步，而不是连续试错。

Windows 文件系统探测必须是有界小输出：

- 对 `F:\work`、`F:\work\ConStart` 或同类目录先做顶层目录/项目标记探测，不要读取 Secret、env、kubeconfig、DB URL 或完整源码内容。
- 使用 `-LiteralPath`、`Select-Object -First <N>`、`ConvertTo-Json -Compress -Depth <N>`，stdout 目标控制在约 12 KB 内。
- wrapper 的 PowerShell prologue 已设置 UTF-8 console/output，并提供 `Read-HwlabText`、`Select-HwlabText`、`ConvertTo-HwlabJson`。读取中文 `SKILL.md`、Keil 日志或 manifest 时优先用这些 helper，避免 `Get-Content`/`Select-String` 的扩展对象字段和系统代码页造成乱码。
- 不要先输出完整目录 JSON 再依赖终端截断；需要更多信息时按明确候选项目二次查询。
- 如果命令已经到达 gateway 但因脚本语法或输出大小失败，只允许简化修正一次；最终回复要记录失败 `operationId`、修正后的成功 `operationId` 和有界输出摘要。

Keil 编译、下载或探测请求必须优先使用 Windows 侧 skill，而不是在 prompt 中重写 Keil 调用逻辑：

```sh
cd C:\Users\liang\.agents\skills\keil
py -3 keil-cli.py build -p <project.uvprojx> -t <target>
py -3 keil-cli.py job-status <job_id>
```

多 probe、烧录和 reset-run 的具体参数以 Windows 侧 `C:\Users\liang\.agents\skills\keil\SKILL.md` 为准；Code Agent 只负责通过 repo wrapper 调用该 skill CLI 并返回 trace、operation/evidence 和 bounded stdout/stderr 摘要。

对 build/download 这类长任务，Code Agent 应优先使用 skill 自带的异步 job 语义：启动命令用短 wrapper timeout 拿到 job id 或明确的启动失败，再用短 `job-status`、state 文件和日志读取轮询进展。除非用户明确要求同步等待并设置了足够大的 Gateway 命令超时，不要通过 gateway 执行 `--wait` 长轮询；同步等待会占用一个 in-flight 槽位，旧 gateway 还会造成队头阻塞。

串口启动日志请求必须优先使用 Windows 侧 `serial-monitor` skill，而不是在 cloud-api 新增串口专用 route：

```sh
cd C:\Users\liang\.agents\skills\serial-monitor
npm run cli -- server status
npm run cli -- server start
npm run cli -- monitor start -p <COMx> -b <baud>
npm run cli -- fetch --session-only --no-dedup
```

Keil 下载后的启动日志抓取应和 build/download 共用同一个 Codex stdio session 与 gateway wrapper trace。71-FREQ 类项目的串口参数以 Windows 侧 `serial-monitor\SKILL.md` 和实时设备枚举为准；需要轮询时用短 wrapper 调用读取 session/state/log，而不是新增聊天层白名单或 blocker。

## Smoke Checks

本地合同检查：

```sh
node scripts/code-agent-chat-smoke.mjs
```

该命令验证 schema、`provider_unavailable` provider gap、`OPENAI_API_KEY`
missing-env 分类，以及本地 stub completion 不能升级为 DEV-LIVE pass。

Workbench 静态接线检查：

```sh
node scripts/dev-cloud-workbench-smoke.mjs --static
node scripts/dev-cloud-workbench-smoke.mjs --dom-only --url http://74.48.78.17:17666/
```

该命令验证 Workbench 默认页、同源只读边界和 `/v1/agent/chat` 前端主流程接线。它只产出
`SOURCE` 级证据。`--dom-only` 会保留部署 runtime/web-asset identity preflight，
但只做真实 DEV DOM/help 只读观察；它不会发送 `/v1/agent/chat`，Code Agent journey
必须记录为 `not_applicable`，不能冒充真实 DEV-LIVE reply。

授权凭证注入后的真实 DEV 复测：

```sh
node scripts/code-agent-chat-smoke.mjs --live
node scripts/code-agent-chat-smoke.mjs --live --url http://74.48.78.17:17667/ --timeout-ms 45000
```

`--live` 会向真实 DEV `/v1/agent/chat` 发送一条最小聊天请求。输出只包含
readiness、provider/model/backend、assistant 回复是否非空和长度、错误分类等摘要；不打印
assistant 回复正文，不读取或打印任何 Secret 值。

默认 `--live` 应指向当前 G14 DEV API/edge 入口 `17667`，而不是历史 D601 端口。
健康的 Codex stdio 冷启动首个 assistant token 可能需要数十秒；10 秒级 transport
timeout 会把健康环境误报为 transport blocker。把 timeout 提高到 45 秒左右只是在真
实 DEV 路由上减少误报，不能替代 `completed` + 非空 assistant reply 的最终判定标
准。

## 复测结果解释

- 若输出 `readiness.level: "BLOCKED/credential"`，后续动作是由授权路径注入
  `hwlab-code-agent-provider/openai-api-key`，不是由 runner 临时补 Secret。
- 若输出 `readiness.level: "#143 DEV-LIVE reply pass"`，只说明真实回复链路通过；
  它不自动证明 M3、M4、M5 或硬件闭环通过。
- 若 `scripts/dev-cloud-workbench-smoke.mjs --static` 通过，而 `--live` 未通过，结论是
  Workbench 接线和源合同通过，但真实 provider readiness 仍 blocked。

## 稳定来源

- [docs/reference/cloud-workbench.md](cloud-workbench.md)：Cloud Workbench 默认页和同源边界。
- [docs/reference/dev-runtime-boundary.md](dev-runtime-boundary.md)：DEV 端口、k3s 与运行态边界。
- [docs/reference/architecture.md](architecture.md)：`SOURCE`、`LOCAL`、`DRY-RUN`、
  `DEV-LIVE`、`BLOCKED` 证据分级。
