Files
pikasTech-agentrun/docs/reference/spec-v01-hwlab-manual-dispatch.md
Lyon 74b83b43c2 feat: 为 gitbundle 装配 required skills (#138)
* Add gitbundle required skills validation

* fix: 限定 required skill blocked result 覆盖

---------

Co-authored-by: AgentRun Codex <agentrun@example.invalid>
Co-authored-by: Codex <codex@pikas.tech>
2026-06-10 10:36:26 +08:00

244 lines
30 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# v0.1 HWLAB 手动调度接入规格
本文定义 AgentRun `v0.1` 面向 HWLAB v0.2 的下一阶段服务目标:不以前置自动 scheduler 为条件,而是通过 `agentrun-mgr` 的手动调度 API 为 HWLAB 提供 canary Code Agent 执行服务。实施跟踪见 [pikasTech/agentrun#31](https://github.com/pikasTech/agentrun/issues/31)。
## 在系统中的职责划分
- HWLAB `hwlab-cloud-api` 是业务 dispatcher:继续负责用户登录、session owner、HWPOD 授权、Workbench `/v1/agent/chat` 合同、result/trace/cancel 对外接口和业务权限判断。
- AgentRun `agentrun-mgr` 是执行事实 authority:负责 run、command、event、runner job、backend profile、SecretRef、terminal status 和手动调度 API。
- Provider profile API Key 管理由 HWLAB Cloud API 鉴权后委托 AgentRun managerAgentRun 信任 HWLAB 后端服务端调用,不实现 HWLAB 用户鉴权。完整合同见 [spec-v01-provider-profile-management.md](spec-v01-provider-profile-management.md)。
- `agentrun-runner` 是手动启动的执行者:从 manager claim run、poll command、调用 backend adapter、append events、ack command、上报 command terminal,并在同一 runner Job 生命周期内继续等待同 run 的后续 commandrun terminal 只由显式 run cancel 或 runner 级不可恢复失败产生。
- HWLAB 不直接写 AgentRun Postgres、不读取 AgentRun Secret、不直接创建 Kubernetes Job;所有跨服务操作只走 AgentRun RESTful API。
- AgentRun 不内建 HWLAB HWPOD/gateway 业务授权;涉及 HWPOD 四要素、用户授权或硬件访问凭证的判断仍由 HWLAB 自己完成。
## 非目标
- 本阶段不要求自动 scheduler、pending scan、capacity selection 或长驻调度器。
- 不改变 HWLAB 对外 `/v1/agent/chat``/result``/trace``/cancel` 用户合同。
- 不使用 SSE、WebSocket、long-polling 或长同步 `turn` 请求替代 durable resource 模型。
- 不把 HWLAB 的 provider Secret、HWPOD token、gateway route 或 kubeconfig 复制给业务客户端。
- 不把 mock、fixture、source-only smoke 或 dry-run 结果当作 HWLAB canary 通过证据。
- 不把 trace、日志、诊断文案、降级原因或“已观测到缺口”当作功能完成。若 HWLAB v0.2 原有 Code Agent 需要的能力在 AgentRun v0.1 中缺失,必须补齐 AgentRun 自身能力;若实现未修复,必须修复实现本身,观测只作为定位和验收证据。
## 目标调用链
```text
HWLAB Workbench
-> hwlab-cloud-api /v1/agent/chat
-> HWLAB 用户/session/HWPOD 权限判断
-> AgentRun POST /api/v1/runs
-> AgentRun POST /api/v1/runs/:runId/commands
-> AgentRun POST /api/v1/runs/:runId/runner-jobs
-> agentrun-runner claim/poll/report
-> 同一 run 后续 turn 复用已 claim 的 runner Job 继续 poll command
-> AgentRun events/command status/terminal_status
-> hwlab-cloud-api 映射为 HWLAB result/trace
```
HWLAB 应保存 `traceId -> runId/commandId/attemptId/jobName` 映射。用户轮询 HWLAB result/trace 时,HWLAB 从 AgentRun command status 与 run events 读取进展,再转换为 HWLAB 自己的前端 schema;浏览器不直接理解 AgentRun 内部 event schema。
## HWLAB v0.2 Code Agent 能力吸收基线
AgentRun `v0.1` 承接 HWLAB v0.2 时,只吸收原有 Code Agent 的通用执行能力,不吸收 HWLAB 的用户鉴权、HWPOD 授权、Workbench UI 或业务 trace schema。下表是后续实现时的代码追溯入口,目的是复用 HWLAB 已验证的边界和判定标准,而不是重新发明一套 Agent 行为。
| HWLAB v0.2 原有能力 | HWLAB 代码/文档追溯入口 | AgentRun 自身承接点 | SPEC 归属 |
| --- | --- | --- | --- |
| 短连接 submit + result/trace 轮询 | `internal/cloud/server-code-agent-http.ts``docs/reference/spec-v02-hwlab-cloud-api.md` | `run + command + runner-job` 三段式手动调度,所有写操作短返回 JSON | [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md) |
| 只有真实 terminal completed + 非空 reply 才算通过 | `docs/reference/code-agent-chat-readiness.md``internal/cloud/code-agent-chat.ts` | result envelope 和 terminal status 规则;partial、stdout、transport close 不升级为 completed | [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md)、[spec-v01-backend-adapter.md](spec-v01-backend-adapter.md) |
| runnerTrace 可见,能展示请求、工具、输出、错误和终态 | `internal/cloud/code-agent-trace-store.ts``web/hwlab-cloud-web/app-trace.ts` | 标准 event schema、单 run 内 seq 单调、bounded payload、Secret redaction | [spec-v01-backend-adapter.md](spec-v01-backend-adapter.md)、[spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md) |
| 取消正在执行的 turn | `internal/cloud/server-code-agent-http.ts``internal/cloud/codex-stdio-session.ts` | durable run/command cancel、pending 阻止启动、running interrupt、terminal 幂等返回 | [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md)、[spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md) |
| conversation/session/thread 复用 | `internal/cloud/codex-stdio-session.ts``internal/cloud/code-agent-session-registry.ts` | `SessionRef` 保存 session/thread 摘要;同一 run/runner Job 处理后续 command,不重新 materialize bundlerunner 内每 turn 有 thread 则 resume,无 thread 则 start | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md) |
| 固定 repo workspace 执行 | `internal/cloud/code-agent-contract.ts``docs/reference/code-agent-chat-readiness.md` | `ResourceBundleRef` 使用 Git-only `repoUrl + workspaceRef.branch/ref` checkout 到隔离 workspace,并记录 materialized commit | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md) |
| 初始业务 prompt 注入 | `internal/cloud/codex-stdio-session.ts` 的 boundary instructions、`internal/cloud/codex-stdio-session-helpers.ts``buildCodexUserPrompt()` | HWLAB 把稳定业务 instruction 文件放入同一 Git bundle,并通过 `ResourceBundleRef.promptRefs` 指定;AgentRun 只在新 thread 首轮注入,resume 不重复注入 | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-backend-codex.md](spec-v01-backend-codex.md) |
| skill discovery 与 skill facts | `internal/cloud/skills-store.ts``internal/cloud/codex-stdio-session-helpers.ts``discoverSkillsForStdio()``codexSidecarSkillsPrompt()` | HWLAB 把完整 `skills/` 子树通过 `kind="gitbundle"` 复制到 workspace `.agents/skills`AgentRun 发现多文件 skill 并向 Codex 暴露有界 skill facts | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md) |
| provider profile 隔离和 Secret 不泄露 | `internal/cloud/code-agent-contract.ts``docs/reference/code-agent-chat-readiness.md` | `ProfileRef/SecretRef` profile-scoped 投影、缺失为 `secret-unavailable`、禁止 fallback 和泄露值 | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-backend-adapter.md](spec-v01-backend-adapter.md) |
| HWPOD/HWLAB runtime 短期 env 注入 | HWLAB Cloud API 的 Code Agent env assembly(历史实现名可能含 device-pod | `runner-jobs.transientEnv` 只在本次 Kubernetes Job env 中生效;只记录 name/count,不保存或输出 value | [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md)、[spec-v01-secret-distribution.md](spec-v01-secret-distribution.md) |
| UniDesk SSH passthrough | HWLAB Code Agent 通过 `tran` 访问 G14/D601/HWLAB/GitHub 维护面 | `toolCredentials[].tool=unidesk-ssh` 注入 `UNIDESK_SSH_CLIENT_TOKEN`;非敏感 endpoint 由调度方 `transientEnv` 显式提供,或由 manager 受控默认值自动补齐;UniDesk frontend 负责 route allowlist | [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md)、[spec-v01-secret-distribution.md](spec-v01-secret-distribution.md) |
| provider/backend/cancel 等失败可区分 | `scripts/src/code-agent-response-contract.mjs``internal/cloud/code-agent-chat.ts` | failureKind 最小矩阵和 JSON 错误响应 | [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md)、[spec-v01-backend-adapter.md](spec-v01-backend-adapter.md) |
| stdout/stderr/tool 输出必须有界 | `docs/reference/code-agent-chat-readiness.md``internal/cloud/code-agent-trace-store.ts` | `command_output`/`tool_call` 记录摘要、字节数、截断标记和必要引用 | [spec-v01-backend-adapter.md](spec-v01-backend-adapter.md) |
| runner/job 失败需要定位证据 | `internal/cloud/server-code-agent-http.ts` 的 trace/result 可见性 | runner job identity、attempt、jobName、pod/log identity 和最小 phase/exit 摘要 | [spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md)、[spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md) |
## 手动调度 API
`POST /api/v1/runs/:runId/runner-jobs` 是 HWLAB canary 的正式手动调度入口。它只负责为一个已存在 run 和 command 显式创建 runner Job,不负责扫描 pending queue。
请求最小字段:
| 字段 | 规则 |
| --- | --- |
| `commandId` | 必填,必须属于 `runId`。 |
| `attemptId` | 可选;未提供时由 manager 生成,返回值必须可持久查询。 |
| `idempotencyKey` | HWLAB 必须用 `traceId``messageId` 或等价稳定 key;相同 key 和相同 payload 返回既有 job/attempt。 |
| `image` / `backendImageRef` | 只能来自 manager allowlist、GitOps/catalog 或受控默认值;客户端不能传任意镜像扩大执行面。 |
| `retention` / `ttlSecondsAfterFinished` | 可选;默认遵循 runner Job TTL 规格。 |
| `transientEnv` | 可选,只用于本次 runner Job 的 Kubernetes env 渲染;不得写入 run/command/result/event 明文。用于承接 HWLAB dispatcher 生成的 owner-scoped HWPOD/runtime context,例如 `HWLAB_API_KEY``HWLAB_RUNTIME_API_URL``HWLAB_RUNTIME_WEB_URL` 和 endpoint metadata。 |
响应必须短返回 JSON,不等待完整模型 turn,至少包含:`runId``commandId``attemptId``jobName``namespace``runnerId``logPath``podIdentity`、后续 `commands show``events` 轮询入口。重复提交若 payload 不同,必须结构化失败,不能创建第二个同名业务 attempt。
`transientEnv` 是 runner-job 层的临时执行上下文,不是 AgentRun run 的 durable fact。manager 不对条目数量设固定上限,只校验数组形态、env name 合法且唯一、value 非空和单值长度;payload hash 只保存 value hashresponse、event、dry-run manifest 和错误详情不得输出明文 value。业务授权仍由 HWLAB 自己负责,AgentRun 只把调度方明确提供或 manager 受控默认补齐的短期 env 交给本次 runner。UniDesk SSH passthrough 的长期 token 不得放入 `transientEnv`HWLAB dispatcher 可以把 `UNIDESK_MAIN_SERVER_IP``UNIDESK_MAIN_SERVER_HOST``UNIDESK_FRONTEND_URL` 这类非敏感定位信息放入 `transientEnv`,也可以只通过 run `executionPolicy.secretScope.toolCredentials[]` 请求 `tool=unidesk-ssh` 让 manager 默认补齐 endpoint。
## Run / Command 映射
HWLAB canary 创建 run 时应使用以下字段口径:
| 字段 | HWLAB canary 口径 |
| --- | --- |
| `tenantId` | `hwlab`。 |
| `projectId` | `pikasTech/HWLAB`。 |
| `providerId` | `G14`,只表示目标 provider,不授予 HWLAB 业务权限。 |
| `backendProfile` | `deepseek``codex``minimax-m3`,由 HWLAB 或调度方显式选择;缺少 matching SecretRef 必须失败,不 fallback。 |
| `workspaceRef` | 必须提供 Git-only repo/branch 语义,HWLAB v0.2 使用 `branch=v0.2`;不得由 runner 猜 host path。 |
| `resourceBundleRef.kind` | 必须是 `gitbundle`。 |
| `resourceBundleRef.bundles[]` | 用于承接 HWLAB 固定工具和 skill 子树,默认 `tools -> tools``skills -> .agents/skills`;旧字段不得再发送。 |
| `resourceBundleRef.promptRefs[]` | 用于承接 HWLAB 稳定初始 prompt,例如 `hwlab-v02-runtime`;必须来自同一 materialized gitbundle checkout`inject=thread-start`,新 thread 首轮注入,resume 不注入。 |
| `resourceBundleRef.requiredSkills[]` | 用于声明本次运行必须存在的 gitbundle skill,例如 `{ "name": "dad-dev" }`runner 只校验 `.agents/skills/<name>/SKILL.md`,不得接受 inline manifest、host path、Secret 或旧 `skillRefs`。 |
| `executionPolicy` | sandbox、network、timeout、secretScope 必须显式,不得由 HWLAB 扩大 AgentRun Secret 范围。 |
| `executionPolicy.secretScope.toolCredentials[]` | 需要 UniDesk SSH passthrough 时必须声明 `tool=unidesk-ssh``purpose=ssh-passthrough`、SecretRef `agentrun-v01-tool-unidesk-ssh`、projection env `UNIDESK_SSH_CLIENT_TOKEN`;不得把 token 放入 command payload 或 runner-job transientEnv。 |
| `traceSink` | 可指向 HWLAB trace adapter;为 `null` 时 HWLAB 仍可通过 AgentRun events 轮询。 |
`tenantId` / `projectId` 是 AgentRun manager 的 policy 边界,不是 HWLAB Workbench project。HWLAB adapter 可以把业务 project/workspace 写入 `metadata.hwlabProjectId``metadata.hwlabWorkspaceId``workspaceRef`,但不得覆盖 AgentRun `projectId=pikasTech/HWLAB`;否则 manager 必须按 `tenant-policy-denied` 拒绝。`providerProfile` 是 HWLAB 入口字段,进入 AgentRun 后必须映射为 `backendProfile`;同一个 HWLAB session 的后续 turn 应继承 session provider profile,不能被 stale workspace provider 覆盖。
Command 第一阶段要求 `type=turn``type=steer``turn` 保存用户原始 prompt、conversation metadata、profile 选择和 HWLAB trace correlation;稳定业务 prompt、skill 清单和工具入口不写入 command payload,而是通过 `ResourceBundleRef.kind="gitbundle"``bundles[]``promptRefs``requiredSkills` 装配。`steer` 保存运行中引导文本,并由 runner 在同 run active turn 期间转发到 backend。业务 cancel 仍走 run/command cancel API,不用 `steer` 伪装。不得把 cookie、session token、provider credential、HWPOD internal token、Secret value、历史消息或大段 skill manifest 写入 payload。
## 需要补齐的能力
### P0 trace/result 元语
AgentRun 标准 events 必须稳定到足以被 HWLAB 转换:
- `backend_status`profile、backendKind、protocol、attempt、resource/session 摘要,不包含 Secret 值。
- `assistant_message`:用户可见 assistant 文本,允许分片但必须能聚合为最终 reply。
- `tool_call`:工具名、状态、bounded 参数摘要和 redacted correlation。
- `command_output`stdout/stderr/diff 的 bounded summary、原始字节数、截断标记和 artifact/log 引用。
- `error``failureKind`、message、retryable、provider/infra/backend 分类和 redacted details。
- `terminal_status``completed``failed``blocked``cancelled`,是 completed 的唯一终态来源。
面向 HWLAB 的 result envelope 权威规则见 [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md#result-envelope)。HWLAB 至少应消费 `status``terminalStatus``reply/finalResponse``failureKind``blocker``lastSeq``eventCount``eventsCapped``nextAfterSeq``finalAssistantSeq``artifactSummary``runId``commandId``attemptId`。partial assistant 文本、transport close、idle timeout 或 stdout 存在都不能单独升级为 `completed`;当 `eventsCapped=true` 或 final assistant 标记截断时,HWLAB 应继续读取 AgentRun events/trace,而不是把当前摘要当作完整归档。
command terminal 与 run terminal 必须分离。普通 turn completed 只终结对应 command,并更新 SessionRef/thread 摘要;同一 run 保持可继续接收 command,已启动 runner Job 在 idle timeout 内继续 poll。只有显式 run cancel、runner 级不可恢复失败或未来 scheduler/retention 策略要求时,run 才进入 terminal。
### P0 cancel
AgentRun 需要提供 durable cancel 能力,建议形态为 `POST /api/v1/runs/:runId/cancel``POST /api/v1/commands/:commandId/cancel`。cancel 必须幂等;已 terminal 的对象返回当前终态。pending command 被 cancel 后不得再创建 runner Job。running runner 必须通过 poll、lease 或 heartbeat 观察 cancel,并传播到 backend interruptbackend 不支持 interrupt 时终止受控进程组。cancel 最终必须写入 event、command state 和 run status`failureKind` 使用 `cancelled`
### P1 SessionRef 持久化
`SessionRef` 需要从 `null/deferred` 升级为可选持久会话引用,支持 HWLAB `conversationId/sessionId/threadId` 到 AgentRun session identity 的映射。session 只能保存 backend thread/session/cache,不保存 API KEY、`auth.json``config.toml` 或完整 `CODEX_HOME`。session store 必须与 Secret projection、writable runtime home、Git workspace 分离。runner 启动时,只按 command `payload.threadId``SessionRef.threadId` 执行 `thread/resume`,没有标准 `threadId` 则执行 `thread/start`events、result 和 session record 都以 `threadId` 为唯一 thread identityprofile 隔离、TTL、GC 和跨 profile 污染防护必须可见。
### v0.1.1 Session state 真正持久化(per-session RWO PVC 直接挂载)
`P1 SessionRef 持久化` 把「持久化」从「metadata-only」升级为「真实持久化」:每个 session 绑一个 RWO PVCrunner Job 把 PVC **直接挂载**到 `${CODEX_HOME}/<codex_rollout_subdir>`codex app-server 自己管落盘,不引入 copy 钩子。
#### 关键边界
1. **不引入 copy 路径**PR #78 已回退 replacement 逻辑(v0.1 不接受 stale `thread/resume` 蒙混)。本节同样禁止「runner Started 后 copy/restore」「热补丁写入 PVC」「伪造 `thread/resume:completed`」等任何变体;只能 PVC 直接挂载,codex 自己读写。
2. **硬验收 = 「PVC 不删就随时 resume」**:跨 runner pod 重建 / 跨 runner Job 重建 / 7 天长跨度(场景 D)都必须 `thread/resume:completed`,不能用 `idleTimeoutMs` 拉成永驻或 runner 不退蒙混。
3. **profile 隔离 + 永不复用**:每个 session 一个 PVC,PVC 内容只服务当前 `backendProfile` 的 codex rolloutprofile 切换必须新 sessionId + 新 PVC。
4. **写后即一致**`codex-rollout-storage-mounted` 是 backend adapter 在 `initialize` 之后发出的事件,含 `pvcName` / `mountPath` / `codexRolloutSubdir`runner 通过 `POST /api/v1/sessions/:id/storage/refresh` 写回摘要。
5. **eviction 显式化**:只有 `AGENTRUN_SESSION_PVC_NAME` 已设 + codex 返回 `no rollout found for thread id` 时升级为 `session-store-evicted`;其他 `thread/resume` 失败按 T7 走 `thread-resume-failed`
#### 行为约束
- `POST /api/v1/runs/:runId/commands``storage_kind='evicted'` 时直接短路返回 `session-store-evicted`,不创建 runner Job。
- runner Job manifest 必须含 `volumes: [{ name: agentrun-sessions, persistentVolumeClaim: { claimName: ... } }]` 与对应 `volumeMounts`PVC `Phase=Bound` 才允许启动 turn。
- runner pod 被删除、Job 被重建或旧 runner lease 过期时,replacement runner 可以作为新的执行壳 claim 同一个 run 或 replacement run,但 resume 通过的判定仍必须是同一个 HWLAB session 映射到同一个 `SessionRef.sessionId``threadId``backendProfile` 和 PVC。不能把 replacement run/job 写成新业务 session,也不能用历史 prompt、messages 或 fake resume 代替 PVC 上的 Codex rollout。
- codex 写 `${CODEX_HOME}/<subdir>/YYYY/MM/DD/rollout-<timestamp>-<threadId>.jsonl`;其他路径不进入 PVC。
- GC 在 `expires_at` 过期且无 active run 时删 PVC;删 PVC 同时把 `storage_kind` 置为 `evicted`
- HWLAB Cloud Web WorkbenchPR D)收到 `session-store-evicted` 时走「同 conversationId + 新 sessionId + `threadId=null`」的 reset UX,不假装续接;新 session 走 `POST /api/v1/sessions` 重新创建 PVC。
### P1 ResourceBundleRef / bundle materialization
`ResourceBundleRef` 必须按 `kind="gitbundle"` 模型落地:输入只依赖 `repoUrl`、ref/branch 和 `bundles[]`runner 从 git mirror/repo 解析 actual commit 后形成内容身份。runner 只能 checkout 到允许 workspace 前缀,不能覆盖 `/app`、Secret projection、profile runtime home 或 session 目录。HWLAB canary 默认只复制 `tools -> tools``skills -> .agents/skills`;用户上传文件、inline seed、对象存储 artifact 和旧字段不进入 `v0.1`
### P1 Resource prompt/skill assembly
HWLAB 旧 Code Agent 的业务 prompt 和 skill 注入必须收敛为 `gitbundle` 子树和 `promptRefs`,而不是继续依赖 cloud-api 进程内硬编码 prompt、`/app/skills` 镜像目录、用户长 prompt、旧 seed 或 Codex 默认 skill registry。HWLAB dispatcher 创建 run 时应指定:
```json
{
"resourceBundleRef": {
"kind": "gitbundle",
"repoUrl": "git@github.com:pikasTech/HWLAB.git",
"bundles": [
{ "name": "hwlab-tools", "subpath": "tools", "target_path": "tools" },
{ "name": "hwlab-agent-skills", "subpath": "skills", "target_path": ".agents/skills" }
],
"promptRefs": [
{ "name": "hwlab-v02-runtime", "path": "internal/agent/prompts/hwlab-v02-runtime.md", "inject": "thread-start", "required": true }
],
"requiredSkills": [
{ "name": "dad-dev" }
]
}
}
```
这些 prompt 文件只写稳定规则,例如 HWLAB Cloud Workbench Code Agent 身份、HWPOD 四要素(target device、workspace、debug probe、io probe)、D601-F103-V2/Keil/build/download/UART 的标准 `hwpod` 路径、禁止旧 Device Pod/profile/device-pod-cli fallback、禁止 Cloud Web 业务 API 替代 runner 内 HWPOD CLI、禁止 session-token fallback、禁止长路径 wrapper,以及历史上下文只走 Codex stdio 原生 `thread/resume`。它们不写用户本轮任务、不写会话历史、不写 Secret 或一次性 issue 过程。
首轮新 thread 必须自动注入这些 prompt 和 skill facts,使 Web 简短 prompt 也能识别 HWLAB 标准能力。`requiredSkills` 声明的 skill 缺失时必须在 resource assembly 阶段 blockedfailureKind 为 `required-skill-unavailable`result/event 带 missing/available/hash/bytes 摘要。后续 turn/resume 不重复注入;若 resume 失败,按 AgentRun Codex stdio 规则失败,不拼接历史 prompt 模拟继续。
## 分阶段增强计划
| 阶段 | 目标 | 主要交付 | 验收重点 |
| --- | --- | --- | --- |
| 1 | 手动调度 API 固化 | `runner-jobs` request/response schema、idempotency、job identity、CLI 调同一 REST API | 重复 key 不重复创建;短返回;manager 重启后可查。 |
| 2 | trace/result 元语 | 标准 event 子集、terminal result envelope、bounded output metadata | HWLAB 可由 events 稳定生成 result/tracepartial 不误报 completed。 |
| 3 | cancel 闭环 | durable cancel API、runner cancel poll、backend interrupt/process group stop | pending/running/terminal 后 cancel 均幂等且可见。 |
| 4 | ResourceBundleRef materialization | Git-only checkout、workspace 前缀、requested ref/commit、actual commit/tree 摘要、failureKind | 从 repo/ref 解析 actual commit;不依赖 cloud-api 或 CI/CD rollout 的 artifact revision;不覆盖 Secret/session/runtime home。 |
| 5 | Resource prompt/skill assembly | `promptRefs` thread-start 注入、gitbundle skillDirs 发现、requiredSkills 校验、hash/bytes 可见 | 简短 HWLAB prompt 能看到业务 instruction 和 gitbundle skillsrequired skill 缺失 blockedresume 不重复注入;旧字段直接拒绝。 |
| 6 | SessionRef 持久化与 runner 多 turn | session record/store、thread resume、runner command loop、TTL/GC、profile 隔离 | 同一 conversation 连续两轮复用同一 run/runner Job;第二轮不重新 materialize bundle、不重复注入 initial prompt;不同 profile 不污染。 |
| 7 | HWLAB v0.2 canary | HWLAB dispatcher adapter、traceId 映射、result/trace 转换 | 普通自然语言最短 turn 真实 completed 且 reply 非空;HWPOD 仍由 HWLAB 授权。 |
## 测试规格
### T1 手动调度 API
阅读本文和 [spec-v01-agentrun-mgr.md](spec-v01-agentrun-mgr.md),然后在真实 `agentrun-v01` runtime 中用 RESTful API 创建 `tenantId=hwlab` 的 run、提交 `turn` command、调用 `POST /api/v1/runs/:runId/runner-jobs`。确认每个 API 返回 JSON、60 秒内返回、不等待完整 turn,并返回 job identity 与后续 poll 入口。
### T2 幂等和重复提交
阅读本文,然后用相同 `idempotencyKey` 重复调用 runner job API。相同 payload 必须返回同一 attempt/job;不同 payload 必须结构化失败,且不能创建第二个 runner Job。
### T3 trace/result 映射
阅读本文和 [spec-v01-backend-adapter.md](spec-v01-backend-adapter.md),然后执行真实 Codex stdio turn,确认 events 中存在可转换为 HWLAB result/trace 的 `backend_status`、assistant 或 error、`terminal_status`,且 bounded output metadata 足够判断截断和 artifact 引用。
### T4 cancel
阅读本文,然后分别验证 pending cancel、running cancel、重复 cancel 和 terminal 后 cancel。确认 command/run 终态、events 和 failureKind 均为 `cancelled` 或当前既有 terminal 状态,日志不泄露 Secret。
### T5 SessionRef 与 ResourceBundleRef
阅读本文和 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md),然后验证一次带 SessionRef 和 Git-only ResourceBundleRef 的 runner Job。确认 session 不含 credential 文件,bundle 从 repo/ref checkout 到允许 workspaceevent/result 能回答 session id、repo、requested ref/commit、actual commit 和 checkout 摘要。
### T6 同 run/runner 后续 turn
阅读本文和 [spec-v01-agentrun-runner.md](spec-v01-agentrun-runner.md),然后在同一 run 中先提交第一条 turn command 并启动一次 runner Job;第一条 command completed 后,在同一 run 中提交第二条 turn command。确认第二条由同一 runner Job 在 idle timeout 内处理,run 未因第一条 completed 而 terminalevents 中 `resource-bundle-materialized` 只出现一次,两个 command result 分别可查且 reply 不互相串联。
### T7 HWLAB prompt/skill 装配
阅读本文和 [spec-v01-runtime-assembly.md](spec-v01-runtime-assembly.md),然后用 HWLAB canary `ResourceBundleRef.kind="gitbundle"` 指定 `bundles[]``promptRefs``requiredSkills`。首轮 Web/CLI 简短 prompt 只写“编译 D601-F103-V2”或等价自然语言,确认 Codex turn 能看到 HWLAB runtime prompt、`.agents/skills` 中的 `hwpod-cli`/`hwpod-ctl`/`dad-dev` skill facts 和 `tools/hwpod` 命令;另跑一个只装配 tools 但声明 required dad-dev 的 payload,确认装配阶段 `required-skill-unavailable` blocked;第二轮 continuation 使用同一 thread resume,确认 `initialPromptInjected=false`,没有手工拼接历史;旧字段请求必须 schema-invalid。
### T8 DS 短 prompt 真实验收
阅读本文、[spec-v01-backend-codex.md](spec-v01-backend-codex.md) 和 HWLAB `spec-v02-hwlab-cli.md`,然后必须用正式 HWLAB CLI/Web 等价短连接入口提交真实 `backendProfile=deepseek` 的短 prompt 到 AgentRun runtime,不允许用长提示词把规则补给模型。验收至少包含三条 prompt:
1. “不调用工具的情况下,你可见的 skill 有哪些?”确认回复包含 gitbundle `.agents/skills` 中的 HWLAB skill,例如 `hwpod-cli``hwpod-ctl`,并且不只返回 Codex 默认 5 个系统 skill。
2. “你当前 HWLAB 初始规则里,D601-F103-V2 应该走哪个标准入口?请只回答入口和禁止路径。”确认回复能说出 `hwpod``hwpod-cli`/`hwpod-ctl`、assembled runtime env、禁止旧 Device Pod/profile/device-pod-cli fallback、禁止 session-token fallback 和禁止长路径 wrapper。
3. “编译 D601-F103-V2。”确认短 prompt 能按注入规则触发 `hwpod-cli -> hwpod-compiler-cli -> /v1/hwpod-node-ops -> hwpod-node` 正向链路,而不是要求用户补充长 prompt。
上述三条必须来自真实 DS/DeepSeek profile 的 Codex stdio `thread/start`/`turn/start` 和后续 `thread/resume` 事件;trace/result 必须显示 `initialPromptInjected=true` 的首轮、gitbundle skillDirs/requiredSkills 摘要、真实 provider profile、terminal status,以及 continuation 时 `initialPromptInjected=false`。如果回复只列出 Codex 默认系统 skill、不能识别 HWLAB 初始规则、缺失 required skill 没有 blocked,或需要用户长 prompt 才能触发 `hwpod`,验收失败。
## 实现状态
| 能力 | v0.1 状态 | 说明 |
| --- | --- | --- |
| 手动 runner Job API | 已实现 | `runner job` 通过 manager REST 创建 Kubernetes Job,支持 `idempotencyKey`、持久 runner job record、job identity、attempt/runner/jobName 返回和重复 payload 冲突保护。 |
| trace/result 元语 | 已实现最小合同 | 新增 run/command result envelope,聚合 terminal status、reply、failureKind、event cursor、artifact summary、attempt、SessionRef 和 ResourceBundleRef 摘要。 |
| cancel | 已实现最小闭环 | 已提供 run/command cancel APIpending cancel 会阻止新 runner Jobrunning runner 通过轮询触发 backend abort,终态写入 event、command state 和 run status。 |
| SessionRef | 已实现最小持久化 | run 可携带 `sessionRef`manager 保存 session/threadrunner 会按 threadId resumeresult envelope 暴露脱敏 session 摘要;TTL/GC 仍按后续运维策略细化。 |
| SessionRef | v0.1.1 已实现/已通过 HWLAB v0.2 原入口复测 | 在「metadata-only 最小持久化」基础上把 session 真实持久化:每个 session 绑 RWO PVC`agentrun-v01-session-<sessionId>`),runner Job 把 PVC 直接挂到 `${CODEX_HOME}/<codex_rollout_subdir>`codex app-server 自己落盘;HWLAB 原入口已验证 runner pod 删除后同 session/thread/PVC 可以恢复,仍禁止 fake 续接。 |
| ResourceBundleRef | 已实现 `kind="gitbundle"` materialization/promptRefs/tools/skillDirs/requiredSkills 装配 | run 可携带 `repoUrl + ref/branch + bundles[]`runner checkout 到 `AGENTRUN_WORKSPACE_ROOT` 下的隔离目录并记录 requested ref/commit、actual commit/tree/workspace/bundles 摘要;`tools/` PATH、`promptRefs` thread-start 注入、`.agents/skills` 目录发现和 required skill 校验已实现;上传文件、inline seed、inline skill manifest 和对象存储不进入 v0.1。 |
| 同 run/runner 多 turn | 已实现最小闭环 | runner Job 在 idle timeout 内持续 poll 同一 run 的后续 command;普通 turn completed 不终结 runbundle 只 materialize 一次,command result 按 commandId 独立聚合。 |
| HWLAB v0.2 canary | 已实现/已通过 HWLAB v0.2 原入口复测 | HWLAB dispatcher adapter 已调 AgentRun 手动调度 API,并能转换 result/traceMiniMax-M3 显式 session、provider profile 继承和 runner pod 删除后的同 session resume 已通过原入口 CLI 复测。 |