Files
pikasTech-unidesk/docs/reference/hwlab.md
T
2026-07-21 13:57:30 +02:00

552 lines
74 KiB
Markdown
Raw 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.
# HWLAB 指挥侧参考
本文中的 MDTODO/Project Management 是遗留页面名;恢复相关任务前按 `$unidesk-tasktree` 迁移。
本文定义 UniDesk 指挥官推进 `pikasTech/HWLAB` 时的固定入口、workspace、上线口径和常见误判边界。HWLAB 的项目内操作细则以 HWLAB 仓库自己的 `AGENTS.md``docs/reference/` 为准;本文只记录 UniDesk 侧如何进入和监督。
## 固定入口
- UniDesk 指挥侧 workspace`/root/unidesk`,固定使用 `master`,开始前执行 `git status``git pull --ff-only origin master`
- HWLAB 开发、部署和验证固定只使用 NC01。`config/hwlab-node-lanes.yaml` 是 node、lane、workspace、CI/CD repo、namespace、GitOps path、公网入口和 Secret sourceRef 的配置真相,当前默认目标必须是 `NC01/v03`
- HWLAB 固定主 workspace 是 `NC01:/root/hwlab-v03`,固定跟踪 `v0.3`。源码修改、PR 准备、repo 内验证、GitOps render 和 rollout 修复都必须在该固定 workspace 下创建任务级 `.worktree/<task>`master server 本地 `/root/HWLAB`、一次性 clone、runner clone、pod 内副本或非 NC01 workspace 只能做只读对照,不能承担开发 source truth。
- HWLAB v0.3 delivery authority
-`config/hwlab-node-lanes.yaml``config/platform-infra/gitea.yaml`
`config/platform-infra/pipelines-as-code.yaml` 组合确认;
- NC01/v03 精确解析为 PaC consumer 后,
目标 branch 的 GitHub PR merge 是唯一交付触发;
- 完整自动链为 GitHub webhook -> Gitea controlled mirror -> immutable snapshot
-> Gitea webhook -> PaC -> Tekton -> GitOps/Argo -> runtime
- 默认 help、status 与 Next 只给只读 status/history
- 自动链故障必须修 owning YAML、controller 或源码,
不得用 trigger、refresh、mirror sync/flush、人工 PipelineRun 或直接 Gitea push 补齐;
- 调试或临时恢复确需运行面 patch 时走 `$unidesk-daddev` P2
- patch 不得改变交付 authority 或冒充终态;
- 结论必须写回 owning YAML/源码并由正常 PR 自动交付;
- 临时 patch 随后撤销或由声明式交付覆盖。
- HWLAB production release delivery authority
- development 与 production 复用同一个
`mirrors/pikasTech-HWLAB` Gitea 物理仓库;
- owning YAML 必须分别声明 `hwlab-nc01-v03`
`hwlab-nc01-production` source identity
- production identity 固定选择 `release`、独立 immutable snapshot prefix
`release-gitops`
- production PaC consumer 必须精确引用 production repository identity
- delivery authority parser 必须精确匹配 target、owner/name 与 branch
缺少 production identity 时 fail-closed,禁止回退到 `v0.3`
- 进入任何 HWLAB 工作前,按 `NC01/v03` 解析 route/workspace/sourceBranch/kubeRoute/runtime namespace 做预检、快进和验证:工作面是 `NC01:/root/hwlab-v03`、source branch 是 `v0.3`、k3s route 是 `NC01:k3s`、runtime namespace 是 `hwlab-v03`、公网入口由 YAML 的 NC01 target 声明。
- NC01 的 node-local registry 是 k3s workload/PVC,不是 host Docker registry。`hwlab nodes control-plane infra status --node NC01 --lane v03` 应显示 registry workload ready、PVC bound 和 endpoint ready;必须通过受控 `runtime-image preload``infra tools-image build/status` 补齐 BuildKit、runtime/base 和 tools image,再把 HWLAB/AgentRun status 与 web-probe smoke 作为可用性证据。
- HWLAB 项目内长期规则入口仍以目标 repo 的 `AGENTS.md` 为准。进入已解析的目标 workspace 后,必须重新读取该 workspace 的规则文件;不能只凭主 server 的压缩上下文继续操作。
- 每次开始源码修改、PR 准备或 repo 内验证前,先用 YAML-first 只读入口 `bun scripts/cli.ts hwlab nodes control-plane source-workspace status --node NC01 --lane v03` 读取 clean、branch、remote、HEAD 和依赖状态。NC01/v03 已解析为 PaC consumerconfirmed `source-workspace sync` 不得成为默认准备步骤或 source delivery recovery;它只允许在 owning YAML 精确解析为 `legacy-manual` 后从 `legacy-cicd` scoped help 使用。若 PaC workspace 返回 `source-workspace-not-ready``remoteUpToDate=false`、ahead/behind/diverged 或 HEAD 与声明 remote/base 不一致,应先修 workspace 自动管理机制、owning YAML 或受控 worktree 基线,再从目标 branch 的 remote base 创建任务 `.worktree/<task>`,不得人工推进 source branch。运行面 `status` healthy、PaC snapshot 对齐和 GitOps/Argo healthy 只能证明 runtime source authority,不代表 host 固定 workspace 已可用于源码工作。
- k3s 操作必须使用 YAML 解析出的 route 语法,例如 `trans NC01:k3s ...`。第一个 route token 必须定位分布式目标,后续 token 才是 operation。
- 非 NC01 workspace、`/root/HWLAB``/workspace/hwlab``/tmp/hwlab-*`、无关 runner clone、master-server checkout 或未由 YAML 选中的 workspace 都不能作为当前 HWLAB 开发 source truthCI/CD source authority 只看 NC01 YAML `sourceAuthority` 的受控 source snapshot。
BK7258、苗总项目、飞思创、脚本/中间件适配和 MicroPython 材料的常用项目目录是 `D518:D:\Work\HWLabOA\Project Management\[EPIC002][MIAO][PRJ001][BK7258]\`。进入该目录后先读取 `D002001001-SUMMARY.md`,再按摘要定位具体规格、任务书或阶段材料;不要把该资料目录当成 HWLAB 源码 workspace。
HWLAB 用户反馈、CLI、Cloud Web、AgentRun、device-pod、公开 API 或运行面工作流 issue,关闭前都必须在 issue/CLI 明确选中的 node/lane 通过用户入口或原入口完成真实验证。源码检查、测试、PR 合并、PipelineRun 或 Secret 存在性只能作为支持证据,不能单独满足关闭条件。`HWLAB_API_KEY` 必须按选中的 node/lane 和目标 HWLAB repo 规则解析,输出只允许 source path、presence、redacted prefix 或 fingerprint,不得打印完整 key。
## 关键 GitHub 入口
- UniDesk 总看板:`pikasTech/unidesk#20`
- HWLAB 总看板:`pikasTech/HWLAB#7`
- HWLAB 上位会议/冻结规则:`pikasTech/HWLAB#78`,当前 P0 是 M3 虚拟硬件可信闭环。
- HWLAB Cloud Workbench 用户界面:`pikasTech/HWLAB#99`
- HWLAB 用户反馈入口:`pikasTech/HWLAB#108`
- HWLAB 手动发布复盘与自动化收敛:`pikasTech/HWLAB#61`
`pikasTech/unidesk#20` 只记录 UniDesk 侧 commander、Code Queue、CLI 和 infra governance。HWLAB 用户反馈、Cloud Workbench、DEV-LIVE、M3 闭环和其他产品事项必须写入 `pikasTech/HWLAB` 的 issue;如果需要在 #20 出现,只能作为 UniDesk 侧调度、CLI guard、infra blocker 或验收治理 lane 的上下文,而不能作为 HWLAB 产品 row。
## 规格真相与仓库 Reference 边界
HWLAB 需求规格的唯一长期正文在 UniDesk OA `project-management/PJ2026-01/specs/`。L0 总规格、硬件池、Agent编排、HarnessRL、客户端、用户管理、平台运维及其 L2/L3 都从这里索引;HWLAB repo 的 issue、PR、runtime reference 和阶段验证 issue 只能承载执行讨论、证据和操作入口,不能重新定义需求。
HWLAB v0.2/v0.3 仓库内 `docs/reference/spec-*`,以及已收编的 `cloud-workbench.md``code-agent-chat-readiness.md``g14-gitops-cicd.md` / `node-gitops-cicd.md``dev-runtime-boundary.md``gateway-outbound-demo.md``MVP-e2e-acceptance.md``architecture.md` 只保留到 UniDesk OA 的交叉引用或历史 stub。repo-local runbook 可以继续说明命令、路径、lane 和调试入口,但不得把公开能力、CLI/API 语义、测试大纲、Gateway 主动出站或 AgentRun 接入要求写成第二份规格正文。
公开入口、FRP/Caddy/域名和 Web/API 可达性需求以 [PJ2026-010604 公开入口](../../project-management/PJ2026-01/specs/PJ2026-010604-public-entry.md) 为权威;Prometheus、日志、trace、health、status 和运维监控需求以 [PJ2026-010605 可观测监控](../../project-management/PJ2026-01/specs/PJ2026-010605-observability-monitoring.md) 为权威。运行配置数值仍以 UniDesk YAML 和目标 HWLAB repo 的受控配置为准,长期 reference 只记录解析与验证方法。
重大规划型 issue 必须执行 P0 SPEC-firstP0 阶段先在 UniDesk OA `project-management/PJ2026-01/specs/` 维护对应 SPEC,确认 SPEC 编号、上级/关联规格、架构图、数据流图、关键时序图和代码引用规则,再进入后续实现。该 issue 范围内新增或修改的源码文件头部必须标注遵循的 SPEC 编号、短名和实现引用版本;自动生成、vendored、纯配置、锁文件或不能承载注释头的二进制产物可例外,但对应生成器、渲染器或配置入口必须能追溯到 SPEC。issue 正文和评论只承载执行计划、讨论和证据,不替代长期 SPEC。
## 概念展示与执行可见性
- HWLAB 概念展示必须从 UniDesk OA 的总规格、Agent 编排、客户端和硬件池边界出发,不从既有 Web 页面布局反推产品定义。
- 展示页用于解释稳定产品能力和研发闭环,不承担第二份需求规格。
- 页面中的任务、工具动作、硬件事实、证据和裁决必须能追溯到 OA 规格中的对应能力。
- 一次性演示故事、截图、端口、提交和运行记录只保留在 issue 或项目过程材料中。
- AGENT 必须是研发过程的主语,不能把固定 CI/CD 流水线或代码差异冒充 AGENT 参与过程。
- 每个关键阶段应展示观察事实、可审计假设、决策摘要、工具调用、输出事实和下一步。
- 不展示原始思维链,不用拟人化长篇解释替代可验证的行动与证据。
- 代码差异是 AGENT 行动的结果,只在对应修改节点或检查器中展开。
- 执行过程优先使用可交互的空间执行图表达。
- AGENT 的观察、假设、计划、裁决和复诊应作为独立节点。
- Keil、CMSIS-DAP、UART、ioProbe 等能力应作为 AGENT 选择调用的工具或硬件节点。
- 节点与连线必须投影排队、运行、通过、失败、阻断和跳过状态。
- 通过和失败必须形成真实分支;物理验证失败后应生成新假设,并明确回环到下一轮诊断入口。
- 节点检查器应提供有界输入、决策摘要、工具调用、输出事实、证据引用和必要的代码差异。
- 真实硬件和数字孪生只负责提供物理现场与测量事实,视觉权重不得压过 AGENT 主舞台。
- 板卡、调试器、探针、线缆和仪器应保持可辨识的真实硬件关系。
- 空间波形、设备线框、深度网格和测量 HUD 可以增强混合现实表达,但不能遮挡设备身份或伪造测量结果。
- AGENT 调用 Flash、UART 或物理测量时,执行节点应与对应设备产生同步状态响应。
- 证据链必须把任务、Agent Trace、Patch、Build、Flash、UART、Physical 和 Verdict 关联起来。
- 物理测量通过前不得展示 Aggregate PASS 或允许封存回归 Case。
- 物理测量失败时必须拒绝封存,展示 Aggregate BLOCKED,并暴露下一诊断目标。
- 浏览器中的动画、标签和演示状态只能表达预先声明的故事或真实后端事实,不能把本地状态冒充运行面证据。
- 纯前端概念展示可以使用独立静态目录和轻量 HTTP 服务,但必须保持展示资产与生产运行面解耦。
- 静态页面不得增加后端接口、伪造 API 响应或引入不必要的框架依赖。
- 公网展示必须验证 HTML、关键图片和交互资产均可从外部网络读取。
- 需要持续托管时应使用受监督、可重启的服务管理器;一次性 shell 子进程或普通 `nohup` 不能作为长期在线证据。
- 验收至少覆盖桌面、紧凑桌面和移动端,无文档级横向溢出、无关键内容重叠、无浏览器控制台错误,并支持 `prefers-reduced-motion`
## DEV 入口
- 当前入口必须从 `config/hwlab-node-control-plane.yaml#publicExposures.NC01` 读取,不从长期文档硬编码推断。
- 当前 lane 入口:
- development `NC01/v03` 前端/API 入口为 `https://lab-dev.hwpod.com`
- production release lane `NC01/production` 的正式入口为 `https://lab.hwpod.com`
- HWPOD L1 Native 是进程独立、界面复用完整 Cloud Web Shell 的运行面:
- API、Temporal worker、Web 和固定端口必须从 owning YAML 读取:
- `config/hwlab-node-lanes.yaml#lanes.<lane>.targets.<node>.nativeDevelopment.hwpod`
- 启停和状态统一使用 `hwlab nodes native-development hwpod api|worker|web`
- 聚合状态统一使用
`bun scripts/cli.ts hwlab nodes native-development hwpod status --node <node> --lane <lane>`
- L1 API、worker 和 Web 是 NC01 host 上的 native 进程:
- 任何情况下都不依赖 CI/CD、GitOps、Argo、Kubernetes 或集群 rollout
- 正常启动、首次拉起、配置变更、合并后复测和故障处理均适用;
- L1 Web 必须复用完整 Cloud Web 的 `AppShell`、Router、总根导航和
`/hwpods/devices` 页面,禁止另建 standalone SPA 或复制一套 HWPOD 页面;
- 可见根导航和首屏路径由
`nativeDevelopment.hwpod.web.accessProfile` 声明,runtime config 从同一 owning YAML
渲染并注入,禁止前端或启动脚本硬编码默认值;
- topology 首屏由 native HWPOD API 从 YAML-first/PostgreSQL spec registry 构建,
禁止代理 deployment Cloud API 作为 L1 首屏数据源;
- CLI `--over-api` 直接调用 owning YAML 固定端口上的 native API
- 同 host 回归使用 YAML probe host
- 禁止改走 Cloud API 或 Kubernetes Service
- L1 只验收本机 API、worker、Web 小回环:
- 公网入口与 public-edge 是独立可选检查;
- 不得成为 L1 启动、执行或完成条件;
- 浏览器验收统一使用
`bun scripts/cli.ts web-probe native-readiness --node <node> --lane <lane> --profile hwpod`
- 节点接入页专项验收使用同一受控入口的 `hwpod-onboarding` profile
- 固定访问 `/hwpods/onboarding`
-`/v1/hwlab-node` 纳入关键失败响应;
- 要求 AppShell 主内容不滚动,只允许 `.bounded-workspace-main` 内部滚动;
- HWPOD Web 无业务登录页时,由 owning YAML profile 声明 `authentication: none`,不得改用临时 Playwright 脚本或弱化浏览器错误断言。
- native API 已接受操作且 Temporal workflow 已创建时,后续公共
`/v1/hwpod-node-ops` 失败必须分类为外部 Cloud API 或 node 运行面故障,
不得误判为 L1 API、worker 或 Temporal 未启动。
- HWPOD L0 与 L1 的 host 边界:
- L0 `--local` 直接执行当前 host 的 function 和文件系统,不提供跨 host 路由;
- 跨 host 的 Windows spec 在 API host 做 L0 时,只比较 compiler/plan 合同;
- 真实 Windows workspace、Keil build 和硬件操作必须通过 API host 上的 L1
native API 路由到目标 HWPOD-NODE
- L0 workspace 和 bounded build 使用当前 host 的 disposable fixture 验证。
- HWPOD Windows UART backend
- backend 由 owning HWPOD spec 的 YAML 字段选择,默认使用 `serial-monitor`
- 单个目标节点可显式选择 `pyserial`,禁止在代码中增加全局自动回退;
- compiler 必须将 backend 对称写入 `open``read``write``close` 四类 node plan
- `pyserial` 节点在操作序列间持有同一连接,回归无论成功或失败都以 `close` 收尾。
- HWPOD runtime spec 的持久化边界:
- L0、L1 和 L2 development 共用 owning YAML 声明的 development host PostgreSQL
- L3 production 使用 production owning YAML 声明的 host PostgreSQL
- database、role、Secret `sourceRef`、schema 和 table 只能从 owning YAML 解析,
当前实现使用 `hwpod.runtime_specs`,禁止在代码或命令中复制连接信息;
- YAML-first 内置 spec 只在读取时与 runtime 记录合并,不写入 runtime table
且 create、update、delete 均返回冻结错误;
- filesystem JSON registry 属于 `legacy-retire`,不得保留 fallback、双写、
启动导入、overlay 或第二 authority
- PostgreSQL 配置或连接缺失时,L0-L3 必须 fail closed,禁止降级到内存或文件;
- L0/L1 spec CRUD 和操作对比统一使用
`bun scripts/cli.ts hwlab nodes native-development hwpod cli ... --node <node> --lane <lane>`
- `--local` 表示 L0 native function`--over-api` 表示 owning YAML 固定端口的
L1 native API,两者不得同时使用。
- Windows HWPOD-NODE 的连接入口和制品入口属于不同合同:
- WebSocket 注册入口来自节点 YAML 的桌面连接配置;
- 更新元数据和 Python 制品下载入口来自同一节点 YAML graph 的 artifact 配置;
- HTTP health 或制品下载成功不能证明 WebSocket Upgrade 可用;
- 恢复节点时必须以 `hwlab nodes hwpod-node status` 同时证明交互进程、配置指纹和 cloud registration。
- 两个产品入口分别声明内部 NodePort `32009``32010`,公网 hostname、upstream 和 healthPath 由同一产品 exposure 对象唯一声明。
- NC01 共享 `public-edge` 只允许通过 `configRef/path` 消费产品 exposure,不得复制 hostname、upstream 或形成第二 authority。
- HWLAB Cloud Web/Workbench 的最终 web-probe 证据必须来自 PR 合并并经 CI/CD/GitOps/Argo 部署后的公网入口;本地 fake-server、localhost dist、临时 `--url` 和源码/API preflight 只能证明开发面,不得作为关闭用户入口 issue 或声明上线成功的替代证据。所有浏览器操作只经 `web-probe`,禁止直接调用 Playwright;公网 web-probe 发现问题时继续修复 PR 或运行面,再重新部署复测。
- HWLAB 与 AgentRun 的环境绑定必须按 owning YAML 显式隔离:
- development `NC01/v03` 只调用 `config/agentrun.yaml#controlPlane.lanes.nc01-v02`
- production `NC01/production` 只调用 `config/agentrun.yaml#controlPlane.lanes.nc01-release`
- production 不得依赖 `lanes.v03` 继承出的 manager URL、runner namespace 或 Secret namespace。
- HWLAB 调 AgentRun 的服务间 API key 不属于统一管理员登录密码:
- 唯一来源由 `config/hwlab-node-lanes.yaml#lanes.<lane>.targets.<node>.codeAgentRuntime.apiKeySourceRef/apiKeySourceKey` 声明;
- 目标 Secret 名和 key 由同一 `codeAgentRuntime` 声明,并通过 `hwlab nodes secret ensure` 受控分发;
- 禁止回退到 `/root/.config/hwlab-<lane>/master-server-admin-api-key.env` 等 lane 级隐式来源,也不得从运行面 Secret 反解。
- HWLAB Code Agent 的 AgentRun 工具 Secret 投影规则:
- 工具 Secret 必须由选中 target 的 `codeAgentRuntime.toolSecretNames` 显式声明,应用和 renderer 不能回退到其他 lane
- PaC source artifact 的实际投影路径是 repo-owned native runtime GitOps postprocess/verify helper,新增或修改运行时配置字段时必须先在 L0 验证 native helper 的 render 与 verify
- L0 通过后必须到 development lane 核对 Deployment/Pod env 和原 Workbench 终态,只修改 TypeScript renderer、只看到 PipelineRun 成功或只看到 Secret 存在,都不能证明该字段已进入 runtime。
- 旧公网端口不是浏览器验收入口;内部 k3s service 仍可使用 `6667` 作为服务端口。
- 不要把 master 上的其他 UniDesk frontend/backend-core 路径误判为 HWLAB 前端。
## NC01 v0.3 User-Billing 管理闭环
NC01 `v0.3` 已按 `pikasTech/HWLAB#1176` 收敛到 Sub2API-style 的最小多用户运营基线。该基线包括 app-local 注册登录、用户自服务 API key/profile/password、admin users 管理、credit adjust、Code Agent reservation/terminal billing、`/v1/billing/summary`、plan/entitlement/quota/concurrency/RPM、admin usage/ledger filter/export、manual credit audit、redeem/manual recharge,以及 subscription/payment 的 provider-agnostic `unconfigured` 占位。审计细节、PR、PipelineRun、public smoke 和 closeout 证据以 #1176 及其 R1-R6 子 issue 为准;长期参考只记录完成态边界和复验入口。
该能力的业务 authority 仍是 HWLAB `hwlab-user-billing`。Cloud API 只做代理和同源 Web session/API key 鉴权,不维护第二套 user、credit、usage、ledger、redeem、subscription 或 payment 状态;Cloud Web 只消费 Cloud API 暴露的正式路径,不直连 user-billing 内部端口。
NC01 `v0.3` 的 DB 执行面不再硬编码为外置 PostgreSQL,而由 UniDesk YAML `config/hwlab-node-lanes.yaml` 中选中 target 的 `runtimeStore.postgres.mode` 决定。mode 为 `local-k3s` 时,Cloud API、user-billing、workbench-runtime、project-management 和 OpenFGA 消费 `hwlab-v03` namespace 内的本地 `hwlab-v03-postgres`;复验和关闭证据应来自 `hwlab nodes control-plane plan|status``hwlab nodes secret status`、运行面 workload/Secret 摘要和 `external bridge/secrets not-required`,而不是要求本地 PostgreSQL 缺失。`externalPostgres` 与 PK01 platform DB YAML 必须继续保留为 `platform-service` mode 的候选配置;把 mode 切回 `platform-service` 时应重新启用 bridge/Secret 路径,但不得因为当前使用 `local-k3s` 删除外置配置。
复验 NC01 `v0.3` user-billing 管理闭环时遵循以下口径:
- 入口必须从 `config/hwlab-node-control-plane.yaml#publicExposures.NC01.v03` 读取;
- 当前 development 入口为 `https://lab-dev.hwpod.com`
- 最小 smoke 应覆盖:
- `/health/live` revision 指向目标 source commit
- admin Web session 登录和 admin 创建、查询、停用 redeem code
- 普通用户注册或登录后兑换 code,重复兑换 one-time code 不重复入账;
- admin redemptions 和 ledger 能查到同一 ledger/redemption
- subscription/payment summary 在没有真实 provider 配置时明确返回 `unconfigured`
- `/billing``/admin/billing` 页面加载且部署 bundle 包含对应 endpoint
- 验证输出只能记录对象 id、prefix、状态、计数和 redacted presence,不能打印 bootstrap admin password、raw redeem code、API key secret、DB DSN 或 provider token。
真实 payment provider、外部支付回调、订单结算和增长活动策略不是 #1176 完成态的一部分。只有当 `config/hwlab-node-lanes.yaml` 或 HWLAB 自有受控 YAML 明确声明 provider/sourceRef,并且 Secret sync、回调入口、ledger/order 状态机和 public end-to-end smoke 都齐备时,才可以把 NC01 `v0.3` 从 provider-agnostic placeholder 扩展到真实支付;不得通过硬编码 provider、手写 k8s Secret、临时 SQL 或 Cloud API 平行账本绕过 user-billing authority。
## HWLAB 测试账号 YAML 归属
HWLAB node/lane 测试账号、bootstrap admin API key 观测、普通测试用户固定 API key、workbench 绑定、user-billing DB sync 输入和 sourceRef/targetKey 映射属于 UniDesk 指挥侧运维真相,必须写入 UniDesk `config/hwlab-test-accounts.yaml`,并通过 `bun scripts/cli.ts hwlab nodes test-accounts status|sync --node <node> --lane <lane>` 受控读取和同步。HWLAB 仓库可以保留应用代码、user-billing schema/migration 和业务 API,但不能另建一份账号 YAML 真相,也不能用运行面 Secret、pod env、日志或 DB 结果反推 owner-only key source。
`config/hwlab-test-accounts.yaml` 中的相对 `sourceRef` 以该文件的 `sourceRoot` 为根解析,属于 UniDesk 指挥侧 owner-only source,不要求也不应要求同名文件存在于 NC01 目标 host。目标 host 或 runtime pod 中没有该 env 文件不能判定为 key 缺失;应先用 UniDesk 受控 CLI 确认 source 与 target fingerprint,再把选中身份的 `HWLAB_API_KEY` 仅作为一次性进程环境注入目标 HWLAB CLI。不要为了复验把测试用户 key 持久化复制到目标 workspace、shell 启动文件、issue、日志或 Git tracked 文档。
该入口的输出只能记录 logicalId、role、permissions、workbench、sourceRef、sourceKey、targetKey、对象 id、byte count、prefix、fingerprint、presence、matchesSourceFingerprint 和 mutation 摘要;不得打印完整 `HWLAB_API_KEY`、完整 `DATABASE_URL`、base64 payload 或可复制凭据。NC01 `v0.3` T1/T2 类验证如果需要 admin/test 两套身份,先用此 UniDesk YAML/CLI 准备账号,再在目标 HWLAB workspace 使用原 `hwlab-cli` 切换 `HWLAB_API_KEY` 做真实入口复验。
`test-accounts status|sync` 中涉及 DB 的读写也必须跟随选中 node/lane 的 DB authority。NC01 `v0.3` 处于 `local-k3s` mode 时,工具不得从指挥机直接连接保留下来的外置 DB sourceRef;只有 mode 明确为 `platform-service` 时才使用外置 DB 路径。若旧 test-accounts DB 探测仍因外置 sourceRef DNS 或网络失败而报错,应登记为 CLI mode-awareness follow-up,不得把它当成 access-control 预置用户或根导航 ACL 的失败证据;这类 ACL 关闭证据应来自受控 preset-user Web/API 验证和 redacted Secret 状态。
身份、权限、workbench、trace/result 和 usage/billing 复验优先使用目标 HWLAB repo 的 typed CLI`client request` 只能作为缺 typed wrapper 时的有界探测或后端路径确认。若 raw request 覆盖了真实用户验收所需的字段,关闭前必须把缺失的 typed CLI 包装登记到对应 HWLAB issue,不能把 raw request 静默当成长期正式用户入口。
### Code Agent TraceResult 展示证据
Code Agent trace 的长期 API 和 Web 行为规格以 UniDesk OA 为权威:API 资源形态见 [PJ2026-010403 API契约](../../project-management/PJ2026-01/specs/PJ2026-010403-api-contract.md) 的 `GET /v1/agent/traces/{traceId}`Web 自动补齐和分片显示见 [PJ2026-010401 Web工作台](../../project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md) 的 Trace阅读要求。UniDesk 指挥侧只记录验证入口和误判边界,不在本参考重新定义 trace endpoint、分页字段、游标语义或自动折叠策略。
实时 Trace 展示必须把 turn 状态和 trace event 分页视为两条独立读路径。`/v1/agent/turns/:traceId` 只回答 running/terminal/final/error 等 turn 状态;`/v1/agent/traces/:traceId` 只负责按下游读 cursor 拉取已经持久化的 trace events。Web 在运行中不能等 turn terminal 后才 hydrate trace,也不能让 compact turn snapshot 覆盖已经拉到的 trace rows。后端刷新 AgentRun 上游失败时,trace API 仍应返回本地 trace store 中已有的分页快照,并把 refresh failure 作为诊断字段暴露;已有事件不得被硬 502 遮住。关闭“运行中 Trace 加载不出”类 issue 时,应证明 running 期间 trace API 与 Web DOM 都能看到已写入事件,而不是只展示最终完成后的 timeline。
下面三段 projection/read-model 规则只适用于 YAML 独立启用 `transactionalProjector``projectionOutboxRelay``projectionRealtime` 的能力组合。它们不是 NC01/v03 纯 Kafka 产品实时链路的默认权威,也不得用来要求 `directPublish + liveKafkaSse` 模式补 snapshot、sync、replay、gap-fill、finalizer 或 polling;当前 capability 值必须从选中 lane YAML 和运行时 status 读取。
Workbench projection/read model 是持久化投影,不是 GET 侧隐式修复路径。Cloud API 重启、内存 finalizer 丢失或投影 worker 中断后,恢复只能由受控后台 projector/resumer 从 durable session、durable trace 和 AgentRun source cursor 继续推进;`/v1/agent/turns/:traceId``/v1/agent/traces/:traceId` 和 Web hydrate 不得为了让一次读取看起来正确而隐藏写入或 remap command。排查“AgentRun 已完成但 Workbench 仍 running/pending”时,先比较 AgentRun command result/session terminal、AgentRun raw event 最大 `sourceSeq`、HWLAB durable trace 最大 `sourceSeq` 和 session/turn 投影状态;若 AgentRun 已 terminal 且 HWLAB trace 落后,应归为 HWLAB projection resume gap,并用 YAML-first 配置的 projector/resumer 修复与验收。
Workbench 投影相关问题的禁用模式以 HWLAB issue 历史为判定边界。`pikasTech/HWLAB#1585` 已证明 GET/read-side 补 AgentRun result 会把恢复能力藏进一次读取,Cloud API 重启、rollout 或内存 finalizer 丢失后仍会让 Workbench 长期停在旧 `lastProjectedSeq`;因此“事后修补/0repair”不能替代 durable projector/resumer。`pikasTech/HWLAB#1596` 已证明 turn/card completed 与 TraceEventPage 不一致会直接变成用户可见的“已完成但暂无可读 Trace”;因此不能让 AgentRun raw result、session summary、turn snapshot、trace tail 或 DOM 互相竞争,再用优先级规则仲裁显示状态。`pikasTech/HWLAB#1690` 进一步固定 Trace 视觉顺序权威:`projectedSeq` 必须由 durable projection 幂等分配,局部 `event.seq``sourceSeq`、输入顺序和 renderer 文本匹配不能参与视觉位置仲裁;历史 collision 应暴露为 projection blocker,而不是交给读侧重排。读侧也不得从 event `completed`、message text、elapsed timeout 或 final result cache 推测 lifecycle;这些只能作为诊断输入,最终事实必须由唯一 Workbench projection 写出。具体 Web/CLI renderer 和 web-probe 验收口径统一见 `$unidesk-webdev`
TraceEventPage 自身的分页契约修复可以在同一持久化快照内做稳定排序和输出 cursor 归一化,但不得改变 lifecycle、补写 projector 状态或引入第二事实源。修复完成后的关闭证据必须同时覆盖同一 session/trace 的 turn、trace events、range/monotonic cursor 和 DOM Trace 可读性,避免只凭 completed card 或单个 API 通过误关。
Code Agent trace/result 展示类问题的 typed CLI 关闭证据以 `hwlab-cli client agent result <traceId>``hwlab-cli client agent trace <traceId> --render web` 和必要的 `hwlab-cli client agent inspect --trace-id <traceId>` 为准,具体操作说明见 `$hwlab-code-agent` skill。三者的默认 JSON 都应暴露 `traceResultSummary`,其中 `ids``toolCalls``agentMessages``finalResponse``diagnostics``counts``upstreamGaps` 是给用户和审计者阅读的稳定摘要;不要要求关闭者从 raw `body``runnerTrace.events``rows``terminalEvidence` 人工拼事实。
`result``trace --render web` 必须能直接证明 final assistant response、实际工具调用及状态、关键 trace/session/conversation/run/command/runner ID 和 runner/provider/lane 诊断。`inspect` 用于确认 trace 所属 session/conversation/thread、恢复上下文和下一步入口;它可以佐证 ID 和上下文,但不能单独替代 final response 或 Web renderer 行。验证必须打到 issue/CLI 选中的同一 node/lane public origin 或等价 Cloud Web/Cloud API dispatcher,不能用临时 AgentRun manager 调用、手写 raw request 或旧 lane trace 代替。
失败详情类问题必须优先核对 `client agent result <traceId>` 的顶层 `error``agentRun``trace --render web` 只证明 rows/timeline 渲染,可能不携带 terminal result 的 `error``agentRun.runId``commandId``runnerId``jobName``namespace``terminalStatus`;Cloud Web 恢复会话时必须从 result 补齐这些诊断,再渲染详情弹窗。空字段不得渲染成大面积“未观测”占位;用户第一眼应看到错误码、错误类别和错误消息,有值的 AgentRun provenance 才进入状态摘要。
AgentRun terminal `failed``blocked``canceled` 也是最终结果,不是“没有 final response”。当 `/v1/agent/chat/result/:traceId``/v1/agent/turns/:traceId` 返回顶层 `error.message``blocker.summary` 或 terminal failure message 时,HWLAB Cloud API 必须把可读错误生成为 `finalResponse.text``traceSummary.finalAssistantRow`Cloud Web 的 final response 展示区必须直接显示该错误文本。TraceTimeline 可以同时展示失败事件,但不能让用户只能从 trace rows 里找错误;也不能用“没有返回可展示的 final response”覆盖已有 terminal error。
`traceResultSummary.valuesPrinted=false` 只是脱敏声明,不等于免检。关闭前仍应扫描输出中是否出现完整 `HWLAB_API_KEY``hwl_live_*`、Authorization Bearer header、DB DSN、Secret payload 或 provider token。若 `upstreamGaps` 出现 `prompt_not_returned_by_upstream`,表示上游 trace/result payload 没有返回可脱敏展示的 prompt metadata;客户端不得发明 prompt 真相,应把该缺口拆到 Agent 编排或 trace payload issue,并说明它是否阻塞当前展示项。
### Workbench 浏览器回归专项
Workbench 浏览器回归需求以 UniDesk OA [PJ2026-010401 Web工作台](../../project-management/PJ2026-01/specs/PJ2026-010401-web-workbench.md) 为权威;具体 Web 开发、受控 fake-server、fixture 采集脱敏、移动端断言、截图 artifact 和线上 web-probe 闭环统一见 `$unidesk-webdev`。HWLAB repo 只保留入口边界:专项代码位于 `web/hwlab-cloud-web`,命令必须在 issue/CLI 选中的目标 node/lane workspace 或独立 worktree 上运行,不得在 master server 本地跑浏览器或仓库级前端 check。
- L1 Native 端口冲突处理:
- 端口被同一 L1 服务的旧进程占用时,只能通过项目 CLI 停止或重启该服务;
- 端口被其他服务占用时,禁止停止、接管或复用其他服务;
- 必须先确认空闲端口,再修改 `config/hwlab-node-lanes.yaml#lanes.<lane>.targets.<node>.nativeDevelopment.workbench` 中本服务的端口;
- API、Worker、Web 的启动和状态继续由同一 YAML 与 `hwlab nodes native-development workbench` 解析;
- 禁止用命令行参数、临时环境变量或代码 fallback 形成第二端口真相;
- 端口退让后从 owning YAML 的 native probe host 和固定端口执行
`web-probe native-readiness`,确认页面、DOM、交互和浏览器错误均通过。
- L1 Native 暴露边界:
- `nativeDevelopment.<application>.publicExposure` 只描述独立公共入口;
- 公网域名、TLS、public-edge 和固定公网入口不进入 L1 启动、执行、回归和
完成条件;
- L1 不调查、等待或操作公共面的 CI/CD、GitOps、Argo、Kubernetes、镜像或
rollout
- 只有用户明确要求 L2 或独立公共面运维时,才进入对应专项流程。
- Workbench L1 API 与 Kafka SSE 验收:
- API 进程存活入口固定为 `/health/live`
- API 依赖就绪入口固定为 `/health/ready`
- `/health` 不是 Workbench L1 health 路由,禁止通过试探 404 判断服务异常;
- 实时验收先启动 `hwlab-cli workbench events inspect --over-api --wait-for user,backend,assistant,terminal,final`,再向同一 session/trace 提交一个新 turn
- 已有 session 的刷新验收必须先检查 connected contract
- `kafkaRefreshReplay=true` 时要求 `deliverySemantics=kafka-retention-then-live``replay=true``liveOnly=false`
- capability 已启用但仍返回 `live-only` 时,优先定位 L1 native API 是否仍保留独立 live-only SSE adapter
- 此时 retention 查询尚未开始,不得先归因 Kafka 扫描超时或历史事件缺失;
- 已有 session 出现 rail、turn card、Final Response 或回放提示不一致时,最短分层固定为:
- 先用 `hwlab-cli workbench events inspect --over-api` 检查同一 session/trace 的 connected contract、核心事件族和 terminal
- 服务端完整时,只执行一次 `web-probe observe command <observer> --type validateExistingSessionRefresh --profile <yaml-profile> --session-id <session>`,核对刷新前后 DOM identity、业务帧数、live handoff 和 forbidden request 数;
- 禁止先扫描大型 artifact、重复重启 L1、增加 snapshot/polling,或用 session rail 的相对时间推测运行耗时;
- Kafka retention 回放队列必须在后台标签页继续推进:
- 禁止用 `requestAnimationFrame` 作为 SSE ingress/reducer 分批处理的唯一调度源,因为后台标签页降频会把有限回放放大成分钟级状态分裂;
- 零延迟合作式调度使用不依赖绘制帧的任务队列,并保留有界 chunk、事件顺序、稳定 event identity 去重和同一 reducer
- L0 必须模拟 rAF 不回调,批量送入 retention 业务帧和最后的 `workbench.connected`,并在有界时间内证明全部帧按序排空;
- 该规则只修正客户端调度,不允许引入第二投影、REST 补洞、sealed guard 或终态仲裁;
- Conversation 与 Trace 等流式容器的贴底状态必须区分内容增长和用户滚动:
- 退出 following
- 只有 `scrollTop` 实际向上移动才能退出;
- 内容先增长、贴底帧尚未执行时出现的临时 bottom distance 不得关闭 following
- 程序性贴底:
- 释放后若内容继续增长,仍处于 following 时补一次合并对齐;
- 恢复 session 历史位置后必须按恢复位置显式同步 following;
- 观察器:
- `MutationObserver` 可以响应字符与子树变化来调度贴底;
- 只在滚动容器直属子节点集合变化时重建 `ResizeObserver` 目标;
- 禁止每个流式字符都 disconnect、遍历并重新 observe
- L0 至少覆盖:
- 高度先增长但 `scrollTop` 未下降时仍保持 following
- 用户真实上滚后暂停,回到底部后恢复;
- L1 使用真实长回复或 retention 回放:
- 检查最终内容、终态和 Composer 同时可见;
- 外层 Conversation 与内层 Trace 必须复用同一贴底状态机,禁止分别维护阈值、竞态修复或轮询补偿;
- API readiness 连续超时而进程仍存活时,先读取受控 API 日志:
- 若周期性 Kafka session index 全量重建与超时窗口重合:
- 归类为后端事件循环或索引维护问题;
- 不得继续重试 WebProbe、GC、前端贴底修复或浏览器刷新;
- 若涉及索引维护策略、增量更新或执行隔离:
- 必须进入独立架构 issue
- 前端任务只保留已完成的 L0 和浏览器证据;
- 明确最新 patch 后尚未覆盖的 L1 边界;
- 通过条件是 `connected=true``missingSemantics=[]``terminalStatuses` 出现明确终态;
- `eventCount``--min-events` 不能证明 assistant、terminal 或 final,不得通过反复猜事件数量重开 inspect;
- 业务交互优先使用项目正式 CLI 的 `--over-api` 完成 session/turn 与产品 SSE 终态对账,并保留精确 sessionId、traceId
- 页面验证随后使用 `web-probe screenshot` 打开 owning YAML 选择的固定公网入口及该 session 深链,只观察用户可见的消息、终态和语义化错误;
- 不得通过硬编码 DOM selector、长等待或临时 Playwright 脚本重复驱动页面业务;这类 WebProbe 异常只能作为工具可见性证据,不能覆盖同一 dispatcher 的 CLI 终态或截图中已可见的业务结果;
- CLI 和浏览器必须保持同一产品 dispatcher、鉴权与 Kafka SSE 路径;禁止用 AgentRun direct manager、snapshot、result polling 或其他第二业务路径替代。
- `event-timeout` 必须直接读取 `observedSemantics``missingSemantics` 分层定位;
- AgentRun 冷启动仍未终态时,报告上游运行状态,不得补读 snapshot、read-model、AgentRun `/events``/result` 冒充产品 SSE 验收。
- `workbench.connected` 已到达且 `filters.sessionId` 与当前会话一致时,delivery semantics、capabilities、contract version 和 authority metadata 的缺失或漂移只记录 `blocking=false` warning
- 上述内部元数据 warning 不得清空 realtime ready、禁用 Composer、覆盖业务错误或产生 `workbench_live_realtime_contract_invalid` 一类阻塞状态;
- EventSource 未连接、连接进入 error/closed/blocked,或 connected session scope 与当前会话不一致时仍可阻断,并必须返回与 transport 或 session scope 对应的 typed error
- Provider catalog 成功且当前选择明确为 `configured=false` 时,前端选择首个 configured profile 并持久化;
- Provider catalog 请求失败或没有 configured profile 时保留当前选择,不禁用 Composer、不增加前置门禁;真实提交若被 provider 拒绝,按原业务错误投影。
- Workbench L1 runner 源码与多轮复用:
- API、Worker 和 Web 可以从 YAML 选中的固定 L1 workspace 热加载;
- runner Git bundle 的 source commit 必须解析自 owning YAML 声明的
`sourceWorkspace.git.remoteName``sourceBranch`
- 禁止把固定 workspace 的本地独有或未推送 `HEAD` 作为 runner fetch ref
因为 Gitea 无法获取该对象;
- runtime workspace 与 runner source 的 provenance 漂移只记录
非阻塞 warning,不得阻塞核心业务;
- runner source Git 对象不存在或无法获取仍是阻塞错误;
- 多轮回归使用
`agentrun events run/<runId> --aggregate reuse --native-development`
一次证明一个 run、一个 runner Job、首轮一次源码获取和物化、后续 turn
的 app-server/thread resume 复用,以及全部 command 终态;
- AgentRun 复用摘要只证明执行资源复用,Workbench 用户终态仍以
Kafka retention replay 与纯 SSE 投影为唯一权威。
- Workbench Cloud 内部统一密钥认证:
- 认证 authority
- Workbench API 发往 Cloud API 的 `Authorization` 与 Cloud API internal dispatch 校验必须消费同一个 YAML SecretRef
- 不得复制 Secret、引入服务私有密钥或增加第二认证 authority;
- Header 规范化:
- 裸统一密钥需要按协议转换为 Bearer header 时,发送端与校验端必须调用同一个共享规范化函数;
- 禁止两端各自拼接或只修改其中一侧;
- 最小 L0
- 同时证明裸值规范化、已有 Bearer 值不重复加前缀,以及 adapter 产出的 header 能通过 Cloud API 校验;
- 只验证 adapter mock 请求不能关闭认证问题;
- L2 失败检查:
- 先核对目标 revision 是否已自动滚动;
- 再核对两端 SecretRef 的对象、key、presence 与 fingerprint
- 禁止读取值、修改 Secret 内容、增加 fallback 或用人工 rollout 掩盖未交付 revision。
- Workbench L0 native smoke
- 使用 `bun run workbench:native:smoke` 一次验证 local、`--over-api`、独立 API 和 Vite HMR
- smoke 使用 disposable loopback 空闲端口,不停止、接管或复用正在运行的 L1 服务;
- API idle timeout 由 smoke fixture 显式注入,不依赖代码默认值或运行面反解。
MDTODO/Project Management Web 的重写或布局类 issue 以 UniDesk OA 对应 SPEC 为权威;关闭时必须用选中 node/lane 的 public origin 和 `$unidesk-webdev``observe command` 证据证明默认任务正文、Rxx 树、Source/File 选择、报告侧栏和报告全屏状态。`observe` 同时采集 control/observer 页面时,显式 command result、control URL 和截图是用户动作证据;被动 observer 周期刷新或 stop 后根路由空态只能作为对照,不能覆盖用户入口截图和 command JSON。
Workbench session lifecycle、空 session 后台回收、archived/deleted deep link 和 GET 纯读投影的判定口径统一见 `$unidesk-webdev`。指挥侧关闭这类 issue 时只记录选中 node/lane、受控配置来源、后台 mutation/GC 证据、list/detail/messages/deep link 摘要和截图 SHATTL、interval、batch 等可调数值以选中 lane 的受控配置为准,不在本参考维护第二份数值真相。
### Web Live DOM Probe 验收
`scripts/web-live-dom-probe.mjs` 和 UniDesk `web-probe run|script` 是 Cloud Web 原入口 DOM 验收底座;正式 CLI 入口是 `bun scripts/cli.ts web-probe ...`,旧 `hwlab nodes web-probe` 已移除。登录 sourceRef、同源 page helper、URL 构造、readiness、Trace 采样、浏览器噪声分类和 artifact 规则统一见 `$unidesk-webdev`;本文件不复制使用细则,避免与 UniDesk WebDev 操作面分叉。只修改该 helper 时属于无服务交付,按目标 HWLAB repo `AGENTS.md` 选择直接提交或 PR,关闭证据写明 `rollout=not-applicable`
Cloud Web runtime config 或 YAML-first UI policy 类修复关闭前,除源码/fake-server 证据外,还必须证明选中 node/lane 的配置已进入部署链路:YAML 字段、GitOps render/env、Deployment 或 Pod env、public origin DOM/API 证据要分别记录。缓存、Pod 滚动和 browser helper 返回包装对象的细节由 `$unidesk-webdev` 规定;本文件只要求这些层次不能混为一个结论。
### Cloud Web Workbench Prompt 浏览器闭环
Workbench prompt、TraceTimeline、final response、详情弹窗、session 切换、deep link 和运行态一致性问题的浏览器证据要求统一见 `$unidesk-webdev`typed CLI 交叉验证仍见 `$hwlab-code-agent`。这里不再复制 selector、fresh context、turn authority 或登录排障细则,避免与 WebDev 操作面产生多路径。
### OpenCode 独立 UI 入口
HWLAB 接入 OpenCode 时,默认采用独立 `opencode-server` Pod 和独立 public hostname,通过 Cloud Web 的 `/opencode` 导航与 iframe 集成;不要把 OpenCode UI 与 HWLAB Cloud Web 合并到同一个 Pod,也不要让 public OpenCode hostname 直接暴露 OpenCode Basic Auth。Cloud Web 继续拥有 HWLAB 登录态和同源 sessionOpenCode public hostname 应先经过 Cloud Web 鉴权代理;未登录访问 OpenCode host 的健康或 API 路径时,预期是 Cloud Web 的鉴权失败响应,而不是浏览器 Basic Auth 弹窗。
OpenCode provider/model、public hostname、SecretRef 和 extra FRP proxy 都必须从 issue/CLI 选中的 node/lane YAML 与目标 HWLAB repo render 读取;长期参考不硬编码当前模型或端口。关闭 OpenCode 集成、provider profile 或微前端入口类 issue 时,最小浏览器证据优先使用选中 node/lane 的 `web-probe opencode-smoke`:登录 HWLAB public origin,打开 `/opencode`,定位 iframe/direct OpenCode origin,打开 project/composer,点击可见 submit,确认最终 assistant 文本和 EventSource 终态事件。只有需要额外核对 `/global/health``/config` 或底层 `/session/:id/message` payload 时,才补 `web-probe script` 做 bounded API drill-down。只看到 `opencode-server` Pod ready、Caddy hostname 200 或 Secret 存在,不能替代浏览器入口 smoke。
排查 OpenCode 对话长时间停在 `Thinking` 或无 assistant 文本时,先拆分 provider 完成、Cloud Web 代理长连接和 UI live state 三层事实。provider 侧必须按 `opencode-provider-proxy` service 明确查 OTel,确认 `/v1/chat/completions` 状态、耗时、content chunk、reasoning-only drop 和 done lineCloud Web 侧必须能看到 `/global/event``opencode.proxy.stream.start` span,以及 `opencode.proxy.sse.directory_rewrite_enabled``from=/workspace``to=/``ticket_accepted=true` 等属性。关闭证据必须包含 `web-probe opencode-smoke` 或等价浏览器 DOM 的最终 assistant 文本、没有残留 `Thinking`,以及 EventSource 收到 `message.part.updated``step-finish``session.idle` 等事件;provider 200 或 `/session/:id/message` 返回 terminal 不能单独证明 UI 已收敛。OpenCode composer smoke 应优先点击可见的 `[data-action='prompt-submit']` 提交按钮;只依赖 Enter 在 contenteditable 状态下不稳定。
## HWLAB FRP 维护
HWLAB 公网 FRP server 由 master server 上的 `hwlab-frps-dev` 容器承担,容器使用 host network,并把 `/opt/hwlab-frp/frps.dev.toml` 只读挂载到 `/etc/frp/frps.toml`。这个 server 侧 allowlist 是 UniDesk 指挥侧维护对象,不属于 NC01 k3s GitOps desired stateNC01 侧 `frpc` ConfigMap/Deployment 只负责各 runtime namespace 的客户端 tunnel。
HWLAB NC01 公网入口统一由单一共享 HTTPS 边缘承载。排障时按以下顺序对齐事实:
- 读取 `config/hwlab-node-control-plane.yaml#publicExposures.NC01.<lane>`,确认 hostname、expectedA、upstream、healthPath 和 NodePort Service
- 读取目标 runtime GitOps 中的 `node-public-service.yaml`,确认 namespace、selector、port、targetPort 与 nodePort 来自同一 exposure
- 读取共享 `public-edge` owning YAML 中指向该 exposure 的 `configRef/path`,不得从运行面 Caddyfile 反推产品配置;
- 合并后只观察正常 PaC/GitOps/Argo 自动链和共享 edge 状态,不人工创建 PipelineRun、Argo sync、Caddy patch 或第二监听器。
当前固定映射为:
- `NC01/v03`NodePort `32009`,公网 `https://lab-dev.hwpod.com`
- `NC01/production`NodePort `32010`,公网 `https://lab.hwpod.com`
共享 edge schema 或站点引用尚未合并时,产品 PR 只声明产品 exposure 与通用 Service renderer,并明确记录合并依赖;禁止增加裸 IP、PK01、FRP、HTTP/SSE fallback 或代码默认值。
## 门禁最小化与扩容治理
不要滑向不必要的复杂门禁是 HWLAB 指挥侧通用原则。旧节点退役、runtime lane 扩容、CI/CD 迁移、运行面热修和文档治理都应优先靠固定边界、清晰命名、唯一真相源、标准入口和长期参考文档收敛,不要把每个设计约定、运行策略或回滚手册都做成新的 preflight、guard、gate 或报告生成器。
旧 DEV/main/非 NC01 特例门禁如果阻碍 NC01 node/lane 路径,默认处理是从当前调用链删除,而不是做兼容迁移、fallback、legacy mode、双路径绕行或在旧门禁上叠加例外。新增门禁只能覆盖明确高价值风险,且必须最小、低噪声、容易删除;资源配额、RBAC 命名、清理策略、回滚顺序、人工同步策略等默认是设计约定或 runbook,不是 CI/CD 通过条件。
NC01 runtime lane 的硬边界必须从 `config/hwlab-node-lanes.yaml` 的 NC01 target 解析:source branch、CI/CD source repo、GitOps branch/path、runtime namespace、publicExposure、service 列表和 workspace 都以 YAML 为准。其他事项应先作为决策表或 runbook 固化,只有被证明无法靠边界和标准入口自然收敛时,才允许加最小检查。
## 最小 Device Agent/Gateway 桥接模型
最小打通目标是只新增桥接面,不改动既有 HWLAB 应用、GitOps、FRP、CD 或前端路径:
- 在选中 node/lane runtime namespace 手动建立一个 standalone `device-agent-71-freq` Pod/Deployment 和 ClusterIP Service。它暴露设备语义入口,例如 `/health``/skills``/workspace/*``/run`,并把真实硬件调用转成 HWLAB cloud-api 的 gateway operation,而不是在 Pod 内直接访问 Windows 硬件。
- 在明确登记的外部 Windows 硬件网关上启动 `hwlab-gateway`,作为 71-FREQ/ConStart/Keil/串口资源的 host bridge。gateway 通过公网或受控网络出站连接 NC01 lane 的 cloud-api,注册稳定的 `gatewaySessionId``resourceId``capabilityId`
- `hwlab-gateway` 是 Windows 长驻 outbound poll 进程,不能用一次性 trans/tran 会话直接挂载;这种启动方式可能继承输出句柄或被 provider 按子进程树等待,导致 trans/tran 卡住并阻塞后续 provider session。临时实验也应通过 Windows Task Scheduler、Windows Service、NSSM、PM2 service 或等价 detached launcher 启动,`trans` 只负责触发和读取状态;通用规则见 `docs/reference/windows-passthrough.md`
- Windows gateway 的最小配置必须来自 profile 或环境变量,核心字段是 `HWLAB_GATEWAY_CLOUD_URL=<active-runtime-lane-cloud-api>``HWLAB_GATEWAY_ID``HWLAB_GATEWAY_SESSION_ID``HWLAB_GATEWAY_RESOURCE_ID``HWLAB_GATEWAY_CMD_CAPABILITY_ID``HWLAB_GATEWAY_CMD_EXEC_ENABLED=1``HWLAB_GATEWAY_DEMO_OPEN=1``HWLAB_GATEWAY_MAX_INFLIGHT``HWLAB_GATEWAY_CMD_TIMEOUT_MS`。这些值用于声明能力和执行边界,不得散落在 device-agent 代码里。
- device-agent 访问集群内 cloud-api 时优先使用选中 runtime lane 的 Service DNSNode 实现访问 `6667` 时遵循 Node/Lane 运行面口径里的 bad-port 规则,不为 device-agent 单独维护另一套例外。
- 71-FREQ 的 device profile 必须配置化保存,至少包含工程根目录、Keil project/target、默认串口波特率、gateway/resource/capability 选择器和 workspace 根。不得把 `F:\Work\ConStart`、工程名、串口号或 probe UID 硬编码进 device-agent 镜像。
- 最小验收顺序是:NC01 lane cloud-api `/health/live` ready;外部 Windows gateway 能访问该 lane 的 public `/health/live``hwlab-gateway` 注册后 cloud-api 能看到 gateway session/resource/capability`device-agent-71-freq` `/health``/skills` 返回 ready;通过 device-agent 发起一条只读或 `echo` 级 gateway shell operation,并返回 operation/evidence/trace 摘要。
- 这个模型只证明 `device-agent Pod -> NC01 lane cloud-api -> external hwlab-gateway -> Windows host` 的控制链路。Keil 编译下载、串口日志抓取和 workspace CRUD 可以作为后续 device-agent skill 子命令逐步接入;未接入前不能把 device-agent 标成完整 71-FREQ 硬件代理。
## Master Server 校验边界
- master server 是 UniDesk/HWLAB 的生产入口且资源紧张;它只能承担轻量源码编辑、Git 操作、日志/健康观察、JSON CLI 指挥和受控 CD 审阅,不能承担正式校验执行面。
- 禁止在 master server 上运行 HWLAB 或 UniDesk 的仓库级 `check`/`test`/smoke 命令,包括但不限于 `bun scripts/cli.ts check``node --test``node web/hwlab-cloud-web/scripts/check.mjs``node scripts/dev-cloud-workbench-smoke.mjs`、Playwright/browser layout smoke,以及其他会长时间占用 CPU/内存、启动浏览器或遍历大仓库的校验流程。
- 需要正式验证时,固定切到 issue/CLI 选中的 node/lane workspace、k3s/Tekton、HWLAB repo-owned CI 或其他获批外部执行面;master server 只负责发起、观察和记录,不负责实际跑 check。
- 如果为了排障必须从 master server 生成命令或查看源码,后续验证命令也必须显式改到目标 node/lane 路径执行,例如 `trans NC01:/root/hwlab-v03 sh -- ...``trans NC01:k3s ...`,而不是直接在 `/root/unidesk` 或 master server 上本地运行。
## Node/Lane 运行面口径
HWLAB 当前真实 runtime 固定为 NC01 `v0.3`。只读观察使用 YAML 解析出的 kube route、namespace 和 public endpoint
```sh
trans NC01:k3s kubectl -n hwlab-v03 get deploy,svc,pod -o wide
```
HWLAB node/lane 的 PipelineRun 失败定位优先使用 `bun scripts/cli.ts hwlab nodes control-plane status --node <node> --lane <lane> --pipeline-run <name>`、PaC `status|history` 和返回的 bounded TaskRun/Pod logs 下钻,而不是手工拼查询。PaC 或 `unknown` authority 的失败态不得给 trigger、refresh、mirror sync/flush 或 rerun;应由只读证据定位 owning YAML、controller 或源码缺陷,再用修复 PR 合并产生的新自动事件验收。默认状态不得打印完整 pod 日志、Secret、DSN、API key 或其他可复制凭据。
- Cloud Web 启动阶段依赖安装或静态资产构建卡住时:
- 先区分 startup probe 预算耗尽与依赖下载无进展;
- 在目标 Pod 内一次核对有效 package registry、proxy 和目标 registry 可达性;
- registry 可用但镜像默认配置失效时,只在选中服务和 lane 的 owning YAML 中声明 service-level registry 覆盖;
- 禁止修改共享镜像配置、其他业务 Pod、共享代理或继续提高 probe 预算来掩盖下载停滞;
- service-level 覆盖只用于恢复当前声明式交付,长期目标仍是把锁文件依赖和静态资产构建进镜像,运行时不再执行依赖安装或前端构建;
- 长期改造涉及制品边界变化时必须由 TaskTree 独立跟踪,不能夹带在运行面恢复 PR 中。
`hwlab-cloud-api``hwlab-edge-proxy` 在集群内可使用 `6667` Service 端口;对外入口以 NC01 target 的 publicExposure/public URL 为准。Node/Bun/undici 的 Fetch 实现会按 Fetch bad-port 规则拒绝 `6667`,因此任何需要代理、转发、探测或服务间访问该端口的 Node 实现都必须使用 `node:http`/`node:https`、已有 repo-owned HTTP helper,或改用不触发 bad-port 的受控代理端口;不要把 Fetch bad-port 造成的 `fetch failed` / 502 误判为 Service DNS、PVC、数据库或 trace 数据缺失。NC01 target 以 `config/hwlab-node-lanes.yaml``nodes.NC01``lanes.v03.targets.NC01` 为准。
NC01 k3s 是 HWLAB 当前正式控制面。任何 HWLAB k3s 操作都必须显式使用 UniDesk route `NC01:k3s` 或等价的 NC01 kubeconfig,不能使用裸默认 kubeconfig
```sh
trans NC01:k3s kubectl -n hwlab-v03 get deploy,svc,pod -o wide
```
不要直接相信默认 `kubectl` context。默认 context 可能指向非 NC01 环境,并可能给出误导性的 workload 状态。
NC01 `v0.3` publicExposure 以 `config/hwlab-node-lanes.yaml` 和 PK01 Caddy/FRP target 为准。
## Code Agent 模型通道
- HWLAB 与 AgentRun 的线上契约、版本和 commit 对齐遵循
[DevOps Hygiene](devops-hygiene.md#prohibited-deployment-truth)
- 只允许产生非阻塞 warning 和诊断证据;
- 不得因此拒绝 Code Agent admission、dispatch 或实时事件流;
- 可安全归一化的旧请求形态必须先归一化再继续业务。
HWLAB 的 DeepSeek/Codex 兼容性属于 HWLAB runtime 规则,长期真相在 HWLAB 仓库 `AGENTS.md` 和目标 lane 对应的 `docs/reference/`。UniDesk 指挥侧只保留监督边界:诊断这类问题时,先在选中 node/lane 的目标 Pod 或临时 passthrough Pod 内闭环证明 Codex stdio、Responses 请求、tool call/tool result 顺序、模型目录和真实 provider 返回,再考虑 GitOps/CI/CD;不要用完整 CI/CD 作为每轮协议试错工具。
DeepSeek profile 应优先使用成熟 Responses/Anthropic 协议桥接方案,例如 Moon Bridge,来保留 prompt cache、tool-result 顺序和模型目录语义。HWLAB repo-owned compatibility bridge 只能承担薄层职责,例如 Codex zstd request body 解码、非 `function` tool 过滤、`/v1/models` 或 readiness 适配;不要在 UniDesk 或 HWLAB 中手写完整 Responses-to-Chat 转换器替代成熟桥接层。Secret 值、provider token 和完整模型请求正文不得写入 UniDesk 文档、issue 或日志。
HWLAB 与 AgentRun 协同修复必须按 `docs/reference/agentrun.md` 的职责边界执行:AgentRun 拥有的共享执行、trace/result、backend event、runner 和 CLI 能力缺失时改 AgentRunHWLAB 拥有的鉴权、Cloud Web/CLI 对外 API、adapter 消费、业务和前端问题改 HWLAB。不得用一侧的观测、fallback 或兼容逻辑迁就另一侧未实现或未修复的能力。
HWLAB v0.3 订阅 AgentRun Kafka event 的权威输入是 `agentrun.event.v1`,内存 direct mapper 的权威输出是 `hwlab.event.v1`;三 topic 的 ownership 统一见 `docs/reference/platform-infra.md#kafka-event-bus-boundary``directPublish` 使用固定独立 group 消费 AgentRun event、调用生产 mapper 并直接发布完整 HWLAB envelope`liveKafkaSse` 使用另一个固定 group 的进程级 shared consumer,再向当前 SSE subscriber fanout,同一 Kafka record 不为每个浏览器重复消费。两者不读取数据库、projection inbox/checkpoint/outbox 或 snapshot,不得回写 `agentrun.event.v1`,也不得把 `codex-stdio.raw.v1` 混入 HWLAB product schema。transactional projector、projection outbox relay 和 projection realtime 是三个可独立组合的 capability,关闭时必须保持不启动、不就绪门禁、不参与产品链路。
### 纯 Kafka 页面刷新与实时交接合同
- Workbench 的产品权威链固定为:
- `codex-stdio.raw.v1 -> agentrun.event.v1 -> HWLAB direct mapper -> hwlab.event.v1 retention -> bounded refresh bootstrap -> shared live fanout -> SSE -> Web ingress/reducer`
- 用户输入必须先成为 `agentrun.event.v1` 的正式 `user_message`,再一对一映射为 `hwlab.event.v1` 的正式 user event
- 用户消息、assistant、tool、terminal 和 final 不得另建数据库、HTTP history 或浏览器本地事实源。
- 页面刷新只允许从 retained `hwlab.event.v1` 恢复已授权的 session/trace
- 服务端先建立 shared live handoff 保护,再读取目标分区 end offset 作为 barrier
- barrier 内由有界 retention bootstrap 输出,barrier 后由 shared live fanout 输出;
- overlap 只按 Kafka topic/partition/offset 与稳定 event identity 收敛;
- messageId 必须由正式 trace/item identity 派生,禁止按正文、时间戳、数组位置或 DOM 内容去重与重排。
- refresh bootstrap 是短生命周期读取器:
- 到达 barrier 后必须退出,不得把每个浏览器连接变成新的长期 Kafka consumer
- 实时阶段继续复用进程级 shared consumer/fanout
- 同一 Workbench session 只建立一个产品 EventSource,刷新与重连不得复制业务 subscriber。
- retention replay 的页面可见性采用单一事务边界:
- 返回已有 session 或 SSE 重连时,connected 前的 Kafka business frame 可以在后台追赶,但不得逐帧发布历史前缀到当前可见 session bucket
- 追赶期间必须保留切走前的稳定消息、Trace、terminal 和最近更新时间,错误与基础设施诊断仍应立即可见;
- replay 提交必须同时满足以下条件:
- 同一 session scope 的 `workbench.connected` 已证明 retention 到 live handoff 完成;
- 待处理 frame 在同一浏览器任务内交给原生产 reducer,并且只发布一次最终投影;
- scope 被替换、连接失败或 handoff 合同无效时必须丢弃未完成事务,禁止提交部分历史前缀;
- 该事务只控制同一 Kafka+SSE reducer 的可见时机,不得演变为 snapshot、HTTP history、时间戳仲裁、第二 reducer 或第二状态权威。
- 单一路径禁止用状态优先级伪装:
- “陈旧快照不得覆盖 sealed final”、snapshot freshness、final authority priority 等 guard 都意味着多条业务写入路径仍在竞争;
- 应删除 HTTP session/message/turn/result、submit response 和本地缓存对业务状态的写入;
- retention replay 与 live frame 必须只通过同一 SSE ingress、queue、reducer、row model 和 card
- HTTP 只负责命令接纳、鉴权和非业务辅助数据,不参与消息、终态或 Final Response 投影。
- `kafkaRefreshReplay``liveKafkaSse` 是可组合、可独立开关的 capability
- topic、group、扫描预算、等待预算、live buffer 上限和失败策略只从选中 node/lane 的 owning YAML/runtime config 读取;
- replay 与 live frame 必须进入同一 decode、queue、reducer、row model 和 card
- barrier 不可获得、扫描或 buffer 超限、授权 scope 不完整时必须 typed fail-closed,不得回退 projector/read model/snapshot/sync/gap-fill/finalizer、polling 或 HTTP history。
- 关闭此类问题时先用 repo-owned CLI 复用生产 bootstrap/codec/reducer,披露 scanned、matched、applied、barrier、buffered、deduplicated、terminal 与 stable identity
- CLI 通过后,使用 semantic internal WebProbe 在同一已有 session 上执行只刷新、不提交新输入的验收;
- 刷新前后 user/agent identity 和可见顺序必须一致,retention 与产品 SSE envelope 多重集必须精确相等;
- 涉及 session 切换时:
- 必须在真实运行中 session 上执行切走再切回;
- 必须证明 handoff 前可见最近更新时间不回退;
- 必须证明 handoff 后继续接收 live event
- OTel 应只出现 direct publish、shared fanout receive 和 product SSE writeprojector/projection write span 必须为 0。
纯 Kafka 调试分成三个可复用入口,不以临时脚本作为长期路径。AgentRun 使用 `./scripts/agentrun kafka regenerate agentrun --session-id <ses_agentrun_...> --trace-id <trc_...>`,从 stdio Kafka/JSONL 调用生产 reducer,只输出 `agentrun.event.debug.v1` 并明示 partial reconstructionHWLAB 使用 `hwlab-cli kafka regenerate hwlab --from kafka|jsonl --session-id <id> --trace-id <id>` 调用生产 mapper,只输出 `hwlab.event.debug.v1`。原 Workbench 页面的管理员 YAML 调试开关只在显式点击后按当前 traceId 建立唯一 debug group,清空隔离面板并复用生产 reducer/card;正式浏览器验收使用 `web-probe observe command <observer> --type validateWorkbenchKafkaDebugReplay`。typed command 继承 observer 的 YAML `internal|public` origin,不接受 URL/IP 来选择内外网。
产品实时链路的最小关闭证据必须来自一个真实 Workbench turn,并同时证明:AgentRun→HWLAB direct publish 数与 HWLAB shared fanout receive 数一致;产品 SSE 收到 terminal/final responseOTel 覆盖 `hwlab-cloud-api``agentrun-manager``agentrun-runner` 且 error=0projection write/projector span 为 0。单步 debug 关闭证据另需证明 stdio frame→AgentRun debug→HWLAB debug→Workbench applied 的逐级 count、顺序、lineage 和 terminal,不得把 debug topic/group 冒充产品 topic/group。KafkaJS 2.2.4 `TimeoutNegativeWarning` 已由 `pikasTech/unidesk#1704` 跟踪空 pending queue 的 1ms 自循环;它不是数据丢失证据,也不得通过 suppress warning、全局 timer patch 或退回产品 polling 路径掩盖。
### Code Agent Provider Profile 配置与验收
本小节只适用于 NC01 `v0.3` provider profile。配置必须先按 `config/hwlab-node-lanes.yaml` 解析 NC01 运行面,再读取目标 HWLAB repo 的对应规则;AgentRun 内部 Secret 物化和 backend adapter 设计仍以 AgentRun 仓库自身为准。
Provider profile 有两条不同职责的正式路径,不能混用。HWLAB 产品侧动态 profile 通过已部署的 NC01 HWLAB Cloud API/CLI 管理,入口是 `hwlab-cli client provider-profiles`。AgentRun runtime lane 自身的 provider credential、`config.toml``auth.json`、runner egress/NO_PROXY 和 GitOps 物化由 UniDesk `config/agentrun.yaml` 拥有,入口是 `bun scripts/cli.ts agentrun provider-profile plan|status|apply|validate --node NC01 --lane nc01-v02 --profile <profile>`;该路径支持在线配置 profile,不要求 image rebuild 或 PipelineRun。不要把手工 patch AgentRun Secret、直接调用 AgentRun manager、临时 runner job、修改 `~/.codex/config.toml` 或修改 `/root/.codex/config.toml` 当作正式配置路径或关闭证据。
HWLAB Provider Profiles 的真实测试按钮只证明 Cloud Web -> Cloud API -> AgentRun validation 链路可用,不替代 AgentRun 发布状态。若成功文本缺少 `requestedModel``responseModel`、validation id、run id 或 command id,先用 AgentRun control-plane status、`cicd status --node NC01` 和 PaC history 核对 source/runtime;若自动链未对齐,修 owning YAML、controller 或源码并等待新 PR merge 自动推进。不得用 trigger、refresh、PaC closeout、重建 HWLAB 容器、修改 operator `config.toml` 或重复下发 Secret 掩盖 manager 版本漂移。
当明确需要把当前 operator 的 Codex 凭据作为 AgentRun 动态 profile 提供给 HWLAB Code Agent 或 CaseRun 使用时,固定操作手册在 `$hwlab-code-agent``AgentRun 动态 sub2api profile` 小节。该场景是 AgentRun runtime profile 投影,不替代上面的 HWLAB provider-profiles 正式配置路径;默认 profile 名为 `sub2api`、模型为 `gpt-5.5`,优先使用 NC01 k3s 内受控 Sub2API ClusterIP 作为 `base_url`,并只记录 SecretRef、keyPresence、hash suffix 和原入口验收结果。
`config.toml` 和 Codex `auth.json` 是两个独立输入。`set-config <profile> --config-stdin` 写入 profile 的 Codex 配置文本;`set-auth-json <profile> --auth-json-stdin` 写入完整 Codex auth JSON object,并在 AgentRun runtime Secret 中作为 `auth.json` key 保留。`set-key --key-stdin` 只用于单值 API key profile;当输入本来就是 Codex `auth.json` 时,不得再把它压扁、拆成单个 key、改走 legacy API-key-only 路径或保留旧断言阻塞提交。遇到 CLI 不支持完整 auth JSON、输出不可见或缺少 hash/keyPresence 等证据时,先补 HWLAB CLI/API 和 AgentRun provider-profile 能力,再继续试机。
标准命令形态如下,文件名可按实际 profile 替换:
```bash
bun tools/hwlab-cli/bin/hwlab-cli.ts client provider-profiles set-config <profile> --config-stdin --no-session < config.toml
bun tools/hwlab-cli/bin/hwlab-cli.ts client provider-profiles set-auth-json <profile> --auth-json-stdin --no-session < auth.json
bun tools/hwlab-cli/bin/hwlab-cli.ts client provider-profiles list --no-session
```
AgentRun `v0.1` 运行面物化对象是 `agentrun-v01` namespace 中的 `agentrun-v01-provider-<profile>` Secret。基础 Codex profile 的稳定 key 是 `config.toml``auth.json`;需要本地模型目录的 profile 还必须按 AgentRun backend profile 声明补齐额外 key,例如 `dsflash-go` 必须同时具备 `model-catalog.json`,且 `config.toml` 中的 `model_catalog_json` 指向 runner 内 profile-local catalog 路径。文档、issue、trace 和 CLI 输出只能记录 profile 名、SecretRef、key 是否存在、字节数、redacted hash/hash suffix、resourceVersion 和验证结果;不得记录 Secret value、完整 `auth.json`、provider token、完整 API key 或可复用凭据命令。
profile 配置后的最小真实验收是通过同一 HWLAB Cloud API/Web dispatcher 路径创建 Code Agent session 并完成一轮真实 turn。CLI 路径使用 `client agent session create --provider-profile <profile>`,再 `client agent send --session-id <sessionId> --provider-profile <profile>`,最后用 `client agent result <traceId>``client agent trace <traceId> --render web` 确认终端状态和最终 assistant 文本。Web 路径使用 UniDesk `web-probe observe start --node NC01 --lane v03 --target-path /workbench`,再 `observe command --type newSession``observe command --type sendPrompt --provider <profile>`;证据必须包含新 session id、`providerSelection` 选中目标 profile、`/v1/agent/chat` 返回 202、traceId、terminal turn 和 Final Response。只看到 Secret 存在、AgentRun canary 通过、PipelineRun 成功或源码测试通过,都不能替代这一真实入口验收。对 profile-sensitive CaseRun 或 provider 修复,关闭证据还必须来自原入口 `case run`/Web 等价路径,结果中应同时显示 `requestedProviderProfile``resolvedBackendProfile`、AgentRun `backendProfile`、模型名和终端状态;涉及 ds-flash/Moon Bridge 时,还要确认 `deepseek-v4-flash`、1M context/model catalog 元数据生效,且归档中不再出现 `responses/compact 404``404 page not found`。当前 HY 凭据对的稳定 profile 名是 `hy`;复测时使用同一标准入口,不在任何长期文档或 issue 中记录凭据内容。
CaseRun 的 case/registry/aggregate、评价、回放、训练反馈和硬件证据闭环需求以 UniDesk OA 的 [PJ2026-0103 HarnessRL](../../project-management/PJ2026-01/specs/PJ2026-0103-harness-rl.md) 为权威;HWPOD 服务、AI 网关和 HWLAB 到 AgentRun 的装配接入分别以 [PJ2026-010103 HWPOD服务](../../project-management/PJ2026-01/specs/PJ2026-010103-hwpod-service.md)、[PJ2026-010104 AI网关](../../project-management/PJ2026-01/specs/PJ2026-010104-ai-gateway.md) 和 [PJ2026-010205 HWLAB接入](../../project-management/PJ2026-01/specs/PJ2026-010205-hwlab-dispatch.md) 为权威。HWLAB 仓库内 `docs/reference/spec-hwpod-harness.md` 只保留历史交叉引用 stubUniDesk 指挥侧按 issue/CLI 选中的 node/lane 重新读取 HWLAB `AGENTS.md`、使用目标 workspace 原入口验证,并在关闭 issue 时记录 runId、traceId、provider profile、registry commit 和负向检索摘要。不要在 UniDesk reference 里复写 CaseRun prompt 细则或 `.agents/skills` 装配实现。
CaseRun skill 交付边界按 `docs/reference/agentrun.md#agentrun--hwlab-协同职责边界` 判定:专用 skill 通过 AgentRun `gitbundle` 装配给 Code Agentsubject repo 不能携带 `.agents/skills` 副本。关闭要求涉及 skill 的 case 时,除 runId、traceId、provider profile 和 registry commit 外,还应记录 `resourceBundlePolicy`、实际装配的 skill 名、AgentRun/CaseRun 归档中的 skill 读取证据,以及 subject repo diff 或 artifact 中没有新增 `.agents/skills` 的负向检索结果。
CaseRun `summary.md``result.json` 和 registry aggregate 是阅读索引,不是替代原始执行证据的判定器。若 summary 中的 build/download/UART 字段与 AgentRun trace、HWPOD command output、Keil job、下载日志或 UART 输出不一致,应先用原始 trace rows、terminal command id、硬件命令输出和串口证据收口当前用户问题,同时把 summary/aggregate 语义缺口提到拥有该契约的仓库继续修复。NC01 硬件 case 的 UART 证据必须来自当前 run 的串口输出;`serial-monitor` 服务或 Windows wrapper 不可用时,应先恢复或登记基础设施问题,不能把串口不可见误判为 subject 源码失败。
### NC01 v0.3 Web CaseRun 最小闭环
NC01 `v03` 的 Web CaseRun 最小闭环是 Cloud Web -> Cloud Web proxy -> Cloud API `/v1/caserun*` -> built-in/configured case repo -> HWPOD node ops -> Python UI node/HWPOD -> Keil。验证 Web 用户入口时使用 UniDesk `bun scripts/cli.ts web-probe script --node NC01 --lane v03`,让 `config/hwlab-node-lanes.yaml` 中的 `webProbe.defaultOrigin` 选择内部 IP 或公网入口;只有显式临时覆盖时才传 `--url`,不要把内部 IP 写进脚本或 issue 作为长期默认。
Web-probe short script 仍受 60s 级别的交互超时约束;长 CaseRun 应拆成“启动 run”和“轮询 run”两段,或使用 observe/专用归档入口承接下载、UART 和 Arm2D stage D 这类长证据链。Cloud Web 对 GET `/v1/*` 可以走通用代理,但 POST/写操作必须在 Cloud Web route policy 中显式登记认证代理路由;新增 CaseRun 写入口时不能只验证 Cloud API 直连。
当前 Web smoke case 使用 NC01 lane 声明的 Keil compile-only 能力。关闭 NC01/v03 Web CaseRun compile-only 问题时,证据至少记录 runId、registry aggregate sha256、Keil jobId、`hwpodExitCode``.hex`/`.axf` artifact、YAML 选中的 origin、HWLAB source revision 和 GitOps revision。compile-only 不要求下载日志、UART 输出或 AgentRun traceId;需要这些硬件证据时按 `pikasTech/HWLAB#2120``pikasTech/HWLAB#2121` 继续扩展,不要把 compile-only smoke 误判为完整下载/UART/Arm2D 闭环。