docs: document v02 deploy yaml source

This commit is contained in:
Codex Agent
2026-06-08 13:21:26 +08:00
parent 435ce50f11
commit a5f38a95ee
6 changed files with 31 additions and 6 deletions
+1
View File
@@ -32,6 +32,7 @@ HWLAB 是硬件实验室运行面和控制面项目。本文是 agent、指挥
- G14 是当前 HWLAB DEV/PROD 原生 k8s 与 GitOps 运行面真相;`hwlab-dev``hwlab-prod` 均由 G14 k3s、Tekton、`G14-gitops` 和 Argo CD 管理,详见 [docs/reference/g14-gitops-cicd.md](docs/reference/g14-gitops-cicd.md)。
- `v0.2` 是 G14 上的加法扩容线:固定分支 `v0.2`、固定开发 workspace `G14:/root/hwlab-v02`、固定 CI/CD source repo `G14:/root/hwlab-v02-cicd.git`、固定 namespace `hwlab-v02`、FRP 入口 `74.48.78.17:19666/19667`;创建和集成 `v0.2` 不得删除、重命名、复用或改义现有 `G14`/`G14-gitops` 分支、`hwlab-dev`/`hwlab-prod` namespace 或 17666/17667、18666/18667 入口。
- `v0.2` 的人写 deploy/runtime 配置单一出处是 `deploy/deploy.yaml``deploy/deploy.json` 不再是 v0.2 兼容源。所有脚本、renderer、planner 和 CLI 必须通过 `scripts/src/structured-config.mjs` / `scripts/src/deploy-config.mjs` 这类格式无关读写层消费配置,禁止把 YAML parser import 或 ad hoc YAML 解析散落到业务脚本里。
- G14 GitOps render 若改变 Argo Application、AppProject、runtime path 或 DEV/PROD 拆分目录,必须同步应用 `deploy/gitops/g14/argocd/project.yaml``application-dev.yaml``application-prod.yaml`;只推 `G14-gitops` 分支不等于 Argo 已切到新 path。
- D601 HWLAB DEV、D601 `dev-cd-apply``ci-publish` 和旧 `main` JS 脚本式 CI/CD 已退出 G14 发布入口;G14 Tekton 只能通过 `scripts/g14-artifact-publish.mjs` 作为集群内 build/push helper,再由 GitOps/Argo CD rollout,新开发、发布、验收、文档和运行面实验不得把 D601 或 legacy 脚本 CD 当作当前 HWLAB runtime source-of-truth。
- G14 k3s 操作必须通过 UniDesk route `G14:k3s` 执行;不得用 D601 kubeconfig、Docker Desktop Kubernetes、master server 本地 check/build 或旧 JS CD 结果作为 G14 DEV/PROD 通过证据。
+4 -1
View File
@@ -1,6 +1,6 @@
# Deploy Contract
`deploy.schema.json` defines the future `deploy/deploy.yaml` manifest shape.
`deploy.schema.json` defines the current `deploy/deploy.yaml` manifest shape.
The MVP acceptance environment is DEV only. Public endpoint source fields live in
`deploy/deploy.yaml`: frontend `http://74.48.78.17:16666` and API/edge
@@ -12,6 +12,9 @@ for future compatibility, but PROD deployment is not part of MVP acceptance.
- `deploy/deploy.yaml` is the DEV-only deploy manifest.
- `deploy/deploy.yaml` is the single source for `health.path`,
`publicEndpoints`, `frp.proxies`, and `k3s.serviceMappings`.
- Config parsing and writing must go through `scripts/src/structured-config.mjs`
and `scripts/src/deploy-config.mjs`; do not add direct YAML parser calls in
planners, renderers, smoke scripts, or CLIs.
- `deploy/k8s/base` contains parseable k3s resource skeletons for the frozen
HWLAB service IDs rendered from the manifest contract.
- `deploy/k8s/dev` contains the DEV health contract and endpoint metadata.
+2 -1
View File
@@ -75,7 +75,8 @@ npm run g14:gitops:render -- --source-revision <sourceCommit>
HWLAB 是 monorepoG14 CI/CD 加速必须按组件输入判断构建和滚动;v0.2 env-reuse 的组件边界与环境配方直接来自 `deploy/deploy.yaml`,运行态镜像身份来自 per-service artifact catalog`CI.json` 已删除,不再作为任何 planner 输入。
- `scripts/g14-ci-plan.mjs` 是只读 planner,默认读取当前 workspace 的 `deploy/deploy.yaml``deploy/artifact-catalog.dev.json``deploy/deploy.yaml` 内的 lane service declarations/env recipe,输出 `affectedServices``reusedServices``componentCommitId``componentInputHash``dockerfileHash``baseImageDigest``buildArgsHash` 和原因;它不得修改 deploy、catalog 或 GitOps 文件。Tekton `prepare-source` 会先从 `G14-gitops` 注入上一轮发布态 catalogsource 分支里的 catalog 只作为 seed contract
- `deploy/deploy.yaml` 是当前 v0.2 人写 deploy/runtime 配置单一出处;`deploy/deploy.json` 不得作为 v0.2 兼容源恢复。配置读写由 `scripts/src/structured-config.mjs``scripts/src/deploy-config.mjs` 统一承载,只有该格式无关层直接处理 YAML/JSON 解析;planner、renderer、smoke、artifact helper 和 CLI 只调用读写 helper
- `scripts/g14-ci-plan.mjs` 是只读 planner,默认读取当前 workspace 的 `deploy/deploy.yaml``deploy/artifact-catalog.dev.json`,以及 `deploy/deploy.yaml` 内的 lane service declarations/env recipe,输出 `affectedServices``reusedServices``componentCommitId``componentInputHash``dockerfileHash``baseImageDigest``buildArgsHash` 和原因;它不得修改 deploy、catalog 或 GitOps 文件。Tekton `prepare-source` 会先从 `G14-gitops` 注入上一轮发布态 catalogsource 分支里的 catalog 只作为 seed contract。
- v0.2 服务清单来自显式 `--services``deploy.lanes.v02.envReuseServices`;旧 `deploy.services[]``deploy.k3s.serviceMappings[]``internal/protocol.SERVICE_IDS` 推导路径不再作为 planner 输入。
- v0.2 env-reuse 组件边界和环境镜像配方以 `deploy/deploy.yaml``lanes.v02.serviceDeclarations``lanes.v02.envRecipe``lanes.v02.bootConfig` 为单一配置点;新增服务或调整 `componentPaths``runtimeKind``entrypoint`、Bun 版本、系统包、launcher 路径和 HWPOD alias 时先改 deploy 配置与 schema/测试,不再把这些属性硬编码进 planner 或 artifact publish helper。
- `hwpod` 是 runner 内的稳定 HWPOD task 入口,由 `tools/hwpod-cli.ts``tools/hwpod-compiler-cli.ts``tools/hwpod-ctl.ts``tools/hwpod-node.ts``skills/hwpod-cli/``skills/hwpod-ctl/` 组成。修改这些路径时,G14/v0.2 CI 必须至少触发携带 `/usr/local/bin/hwpod` 的 runtime skills/env-reuse 重新装配或 rollout,不能全量复用旧 artifact 后只报告 PipelineRun 成功。
+2 -2
View File
@@ -301,7 +301,7 @@ CaseRun 的 trace 处理必须遵循 [spec-v02-code-agent-trace.md](spec-v02-cod
`hwlab-cli case aggregate <caseId> --run-id <runId> --case-repo /root/hwlab-case-registry` 是已完成 CaseRun 的无服务二次整理入口。它只读取 case registry repo 里已有的 `runs/<caseId>/<runId>/` 产物,不启动新的 CaseRun、不访问硬件、不触发 CI/CD、不追加自动评价或 pass/fail 判定;默认输出 `runs/<caseId>/<runId>/aggregate.md`,并只把这个 markdown 文件 `git add``git commit``git push` 回 case registry repo。若 registry 里存在其他并行 dirty 产物,`case aggregate` 不得顺带提交它们。
`aggregate.md` 是该 run 的主阅读入口,必须按固定顺序聚合运行环境信息、HWPOD 信息、Code Agent 信息、输入 Prompt、低噪声 Trace、final response 和最后 diff。Trace 正文优先使用 `agent-messages.json` 里的共享 Web/CLI renderer rows;普通 message row 直接加粗展示正文,不做折叠,只有工具调用 row 使用 `- <details>/<summary>` 列表项折叠,且 `<summary>` 本身就是折叠按钮,文案写成单行截短的 `已运行 <command>`。工具调用展开体必须保留或恢复 `stdout:``stderr:``exitCode=`结构换行,不能把详情挤成一行。缺少 rows 时才嵌入已有 `agent-trace.md`。聚合文件可以列原始产物索引和 trace/result/inspect 命令作为审计线索,但不能把 `summary.md``result.json``final-response.md` 或原 trace 文件继续声明为对外主阅读路径。
`aggregate.md` 是该 run 的主阅读入口,必须按固定顺序聚合运行环境信息、HWPOD 信息、Code Agent 信息、输入 Prompt、低噪声 Trace、final response 和最后 diff。Trace 正文优先使用共享 Web/CLI renderer rows;普通 message row 直接加粗展示正文,不做折叠,只有工具调用 row 使用 `- <details>/<summary>` 列表项折叠,且 `<summary>` 本身就是折叠按钮,文案写成单行截短的 `已运行 <command>`。工具调用展开体和新生成的 `agent-trace.json` 必须保留完整 command/stdout/stderr/exitCode 正文和结构换行,不能把详情挤成一行,也不能在 full trace 产物里做 preview 截断;只允许 summary 截短。缺少 rows 时才嵌入已有 `agent-trace.md`。聚合文件可以列原始产物索引和 trace/result/inspect 命令作为审计线索,但不能把 `summary.md``result.json``final-response.md` 或原 trace 文件继续声明为对外主阅读路径。
验收已完成 case04 时,使用现有 registry 产物回放,不重新启动 CaseRun:
@@ -328,6 +328,6 @@ hwlab-cli case aggregate d601-f103-v2-arm2d-integration \
7. 长任务验收必须覆盖 `hwlab-cli case run start <caseId>` 立即返回,以及 `status/result/logs <runId>` 在 worker 运行中能短查询到阶段、耗时、stdout/stderr byte count、trace/session/conversation/thread 信息和下一条轮询命令;不得把同步命令长时间无输出视为有效 CaseRun 操作体验。
8. `d601-f103-v2-compile` 的真实流程只覆盖当前编排链路和 compile-only smoke;操作员直接运行 `hwpod-cli build`、直接调用 Keil 或只检查 node 侧 job,不等同于 CaseRun 流程跑完。
9. agent-task CaseRun 的真实流程必须证明 `case run` 已创建或调用 Code Agent / AgentRun session,任务 prompt 已进入 agent 上下文,CaseRun 已采集隔离 subject worktree diff,并已继续执行编译、下载或 I/O smoke 等后续动作;当前版本只记录这些事实,不自动判断 agent 是否完成任务。
10. 已完成 run 的 registry 聚合验收必须通过 `hwlab-cli case aggregate <caseId> --run-id <runId>` 输出单个 `aggregate.md`,并证明该文件包含运行环境、HWPOD、Code Agent、Prompt、低噪声 Trace、final response 和最后 diffTrace 中 message 加粗直接展示,工具调用用 `- <details>` 缩进和单行截短的 `已运行 <command>` summary 折叠,展开后保留详情换行。该命令不得启动新 CaseRun,也不得提交当前聚合 markdown 以外的并行 registry dirty 文件。
10. 已完成 run 的 registry 聚合验收必须通过 `hwlab-cli case aggregate <caseId> --run-id <runId>` 输出单个 `aggregate.md`,并证明该文件包含运行环境、HWPOD、Code Agent、Prompt、低噪声 Trace、final response 和最后 diffTrace 中 message 加粗直接展示,工具调用用 `- <details>` 缩进和单行截短的 `已运行 <command>` summary 折叠,展开后保留完整详情换行。新归档的 `agent-trace.json` 也必须保存 full trace row body,不得把 tool detail 压缩为 preview。该命令不得启动新 CaseRun,也不得提交当前聚合 markdown 以外的并行 registry dirty 文件。
11. Windows subject worktree 文本编辑必须能在 CRLF 文件中通过 `workspace.apply-patch` 完成源码修改,返回 before/after SHA 与 diff 摘要,并保留原文件 CRLF;`workspace.apply-patch` 在 context 不匹配时必须返回可诊断 payload,而不是只给 `context not found`
12. Keil `debug.download` 自动装配必须输出结构化 `cmd.run` argv 步骤;带空格的 `probeName` 在 plan 中必须是单独 argv 元素。`io.uart.read` 必须通过节点本地 `serial-monitor` 读取正在监控的 COM/baud;未监控或工具失败时必须返回包含 `ioProbe`/COM 口/baud/monitor status/启动建议的结构化 blocker details。
+22
View File
@@ -1743,6 +1743,28 @@ test("hwlab-cli Web trace render reports suppressed noise and command tool detai
assert.match(text, / HWPOD/u);
});
test("hwlab-cli Web trace render keeps full tool details with --full", async () => {
const longStdout = `first line\n${"X".repeat(1500)}\nlast line`;
const command = "/bin/sh -lc 'cat > /tmp/main_patch.txt << '\''PATCHMAIN'\''\n*** Begin Patch\n*** Update File: projects/01_baseline/User/main.c\n@@\n+d601_arm2d_demo_task();\n*** End Patch\nPATCHMAIN\nhwpod workspace apply-patch --spec .hwlab/hwpod-spec.yaml --reason demo'";
const result = await runHwlabCli(["client", "agent", "trace", "trc_render_full", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web", "--full"], {
fetchImpl: async () => new Response(JSON.stringify({
status: "completed",
traceId: "trc_render_full",
events: [
{ traceId: "trc_render_full", seq: 1, label: "item/commandExecution:completed", type: "tool_call", toolName: "commandExecution", status: "completed", itemId: "call_full", command, exitCode: 0, stdoutSummary: longStdout, createdAt: "2026-06-01T13:00:02.000Z" }
]
}), { status: 200 })
});
assert.equal(result.exitCode, 0);
const row = result.payload.body.rows[0];
assert.match(row.body, /cat > \/tmp\/main_patch\.txt << '+PATCHMAIN'+/u);
assert.match(row.body, /\n\*\*\* Begin Patch/u);
assert.match(row.body, /stdout:\nfirst line\nX{1500}\nlast line/u);
assert.match(row.body, /exitCode=0/u);
assert.doesNotMatch(row.body, /last line\.\.\./u);
});
test("hwlab-cli Web trace render keeps AgentRun middle assistant message", async () => {
const result = await runHwlabCli(["client", "agent", "trace", "trc_render_agentrun_middle", "--base-url", "http://web.test", "--cookie", "hwlab_session=session-a", "--render", "web"], {
fetchImpl: async () => new Response(JSON.stringify({
-2
View File
@@ -2200,9 +2200,7 @@ async function archivedAgentTraceBody(run: PreparedCaseRun, evidence: any) {
async function materializeFullAgentTraceWithExistingCli(context: CaseContext, run: PreparedCaseRun, evidence: any, initialTrace: any) {
const body = initialTrace?.body && typeof initialTrace.body === "object" ? initialTrace.body : initialTrace;
const existingRows = arrayOfObjects(body?.rows ?? body?.renderedRows ?? body?.traceRows);
const existingEvents = arrayOfObjects(body?.events ?? body?.trace?.events ?? body?.sourceEvents);
if (existingRows.length > 0) return initialTrace;
if (existingEvents.length > 0) return initialTrace;
if (!run.agent) return initialTrace;
const traceId = text(body?.traceId ?? initialTrace?.traceId ?? evidence.agent?.traceId ?? run.agent?.traceId);