fix: 收敛 PaC 自动交付提示

This commit is contained in:
Codex
2026-07-11 11:42:16 +02:00
parent b12dd85375
commit b607c3a996
55 changed files with 2953 additions and 1472 deletions
+11 -18
View File
@@ -42,7 +42,7 @@ NC01:/root/agentrun-v02/.worktree/{pr_branch}
## 文档落库规则
AgentRun 仓库内长期参考、`spec-v01-*` `architecture.md` 交叉引用 stub 变更不创建 PR。完成本地审查后,必须直接提交并推送到对应目标分支,例如 `origin/v0.2`。需求规格正文变更落到 UniDesk OA 的 `project-management/PJ2026-01/specs/`,不要在 AgentRun repo 另维护一份正文。过程计划、阶段证据、验收结果和阻塞点写入对应 GitHub issue 评论区,不能用文档 PR 代替直接落库
AgentRun 源码仓目标 branch 的任何变更都必须通过 GitHub PR 合并,包括长期参考、`spec-v01-*` `architecture.md` 交叉引用 stub。不得以“仅文档”为由直接 push 目标 branch,因为目标 branch 的 PR merge 是 migrated source delivery 的唯一正式触发。若确有不触发交付的文档专用 branch,必须由 owning YAML 与 delivery authority 显式声明,不能把它当作默认例外。需求规格正文变更落到 UniDesk OA 的 `project-management/PJ2026-01/specs/`,不要在 AgentRun repo 另维护一份正文。过程计划、阶段证据、验收结果和阻塞点写入对应 GitHub issue 评论区,不能用文档提交替代正式 issue 证据
## 部署目标
@@ -62,14 +62,12 @@ trans NC01:k3s kubectl get pods -n agentrun-v02
## 受控 CI/CD 入口
AgentRun 控制面写操作必须通过 UniDesk 高层 CLI 执行。无 `--node/--lane` 的控制面命令解析 `config/agentrun.yaml.controlPlane.default`,当前固定为 `NC01/nc01-v02`;显式 `--node <node> --lane <lane>` 也只应选择 NC01 lane。新增或迁移 lane 必须从 `config/agentrun.yaml` 解析目标,不得从 AgentRun service repo 的 `deploy.json` 读取部署真相
AgentRun 目标必须从 `config/agentrun.yaml` 解析,不得从 service repo 的 `deploy.json` 或 URL 片段推断。Delivery authority 还必须与 `config/platform-infra/gitea.yaml``config/platform-infra/pipelines-as-code.yaml` 的 consumer/repository 组合校验。当前默认 `NC01/nc01-v02` 是 PaC migrated consumer;目标 source branch 的 GitHub PR merge 是唯一正式交付触发
```bash
bun scripts/cli.ts agentrun control-plane status
bun scripts/cli.ts agentrun control-plane trigger-current --dry-run
bun scripts/cli.ts agentrun control-plane trigger-current --confirm
bun scripts/cli.ts agentrun control-plane refresh --dry-run
bun scripts/cli.ts agentrun control-plane refresh --confirm
bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01 --consumer agentrun-nc01-v02
bun scripts/cli.ts platform-infra pipelines-as-code history --target NC01 --consumer agentrun-nc01-v02 --limit 10
bun scripts/cli.ts agentrun control-plane cleanup-runners --node NC01 --lane nc01-v02 --dry-run
bun scripts/cli.ts agentrun control-plane cleanup-runners --node NC01 --lane nc01-v02 --confirm
bun scripts/cli.ts agentrun control-plane cleanup-runs --min-age-minutes 30 --limit 200 --dry-run
@@ -78,7 +76,7 @@ bun scripts/cli.ts agentrun control-plane cleanup-released-pvs --limit 200 --dry
bun scripts/cli.ts agentrun control-plane cleanup-released-pvs --limit 200 --confirm
```
YAML-only lane 的标准入口是
平台配置维护与 source delivery 是独立职责。`apply``secret-sync``restart` 只用于 YAML 声明的控制面、Secret 或配置维护,不得作为 PR 合并后的交付恢复
```bash
bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-v02
@@ -88,24 +86,19 @@ bun scripts/cli.ts agentrun control-plane secret-sync --node NC01 --lane nc01-v0
bun scripts/cli.ts agentrun control-plane secret-sync --node NC01 --lane nc01-v02 --confirm
bun scripts/cli.ts agentrun control-plane restart --node NC01 --lane nc01-v02 --dry-run
bun scripts/cli.ts agentrun control-plane restart --node NC01 --lane nc01-v02 --confirm
bun scripts/cli.ts agentrun control-plane trigger-current --node NC01 --lane nc01-v02 --dry-run
bun scripts/cli.ts agentrun control-plane trigger-current --node NC01 --lane nc01-v02 --confirm
bun scripts/cli.ts agentrun control-plane status --node NC01 --lane nc01-v02 --full
bun scripts/cli.ts platform-infra gitea mirror status --target NC01
bun scripts/cli.ts platform-infra pipelines-as-code status --target NC01
bun scripts/cli.ts agentrun control-plane platform-maintenance --help
```
`status` 只读观察 YAML 选中 lane 的 source authority、对应 PipelineRun、GitOps latest、Argo Application、runtime workload、manager source commit 和 git mirror/Gitea 摘要,并报告 Argo revision 是否对齐该 lane 的 GitOps latest。未迁移的 legacy `v0.2` lane source authority 只来自 k8s git-mirror snapshot:受控 sync 先为 branch tip 创建 `refs/unidesk/snapshots/agentrun-yaml-lane/<branch>/<commit>``status` / `trigger-current` / build 只读取该 snapshot 和 `sourceStageRef`,不得把 host source workspace、本地 fetch/pull、可变 branch ref 或 Pipeline 直连 GitHub 当 authoritative source。默认输出是 compact commander 视图:`target` 只保留 node/lane/source/runtime/CI/GitOps/git-mirror/database 摘要,关键结论在 `summary``alignment`,成功 probe 的 stdout/stderr tail、完整 YAML target原始 `source``runtime``gitMirror` payload 默认省略;需要完整展开时使用返回的 `disclosure.fullCommand` 或显式加 `--full`,需要原始调试视图时加 `--raw``status` 额外支持 `--pipeline-run <name>``--source-commit <sha>` 定点查询;`--pipeline-run` 会读取 PipelineRun `revision` 参数作为 pinned source commit,并在 `alignment.branchDrift` / `summary.branchDrift` 中同时披露当前 snapshot tip、目标 source commit、PipelineRun source commit、是否已被当前 snapshot supersede 以及 `triggerLatest` 下一步。`status` 会向 stderr 输出 `agentrun.control-plane.status.progress` 阶段事件,覆盖 `source``runtime``git-mirror`,避免长时间聚合时无可见进展。legacy `trigger-current` 会先执行 k8s git-mirror sync 并以 snapshot commit 创建 commit-pinned PipelineRun;同名 PipelineRun 正在运行或已经成功时必须拒绝重复触发,只允许在失败态或不存在时创建。该命令只提交 CI/CD 工作,不等待完整 PipelineRun 或 rollout 完成,后续用 `job status``status --pipeline-run <name>` 轮询。`refresh` 只对 YAML 声明的 Argo Application 执行 hard refresh,用于 GitOps promotion 已完成但 Argo 仍停留旧 revision 时的受控同步入口;它不直接 patch runtime workload。
`status` 只读观察 YAML 选中 lane 的 authority、PipelineRun、GitOps、Argo、runtime、manager source commit 与 Gitea 摘要。默认输出是 compact commander 视图,完整 target原始 probe payload 只在 `--full|--raw` 展开。PaC consumer 的正常、失败和超时状态都只能给 `status``history` 与只读下钻;不得返回 `trigger-current``refresh`、mirror `sync|flush` 或人工 PipelineRun。authority 零匹配、多匹配或配置错误必须返回 `unknown``mutation=false` 并 fail-closed。
NC01 `agentrun-nc01-v02` PaC lane 的正式触发链路是:GitHub PR 合并到 `pikasTech/agentrun@v0.2` -> GitHub webhook bridge 同步到 Gitea controlled mirror immutable snapshot refs -> Gitea repository push webhook 触发 Pipelines-as-Code -> Tekton PipelineRun -> GitOps/Argo -> k8s runtime。该 lane 不使用 branch-follower、Gitea Actions、act_runner 或自维护 `trigger-current` 作为正式 CI 触发器;对应 source authority、repo visibility、public exposure 和 mirror credentials 属于 `config/platform-infra/gitea.yaml`PaC controller/Repository/webhook/Tekton 参数属于 `config/platform-infra/pipelines-as-code.yaml`。Gitea 的公开 Web UI 是 `https://gitea.pikapython.com`,但 CI、Argo 和 runtime 内部读取必须使用 `gitea-http.devops-infra.svc.cluster.local:3000` 的 ClusterIP URL,避免公网回环和 legacy git-mirror commit 缺失
NC01 `agentrun-nc01-v02` PaC lane 的正式链路是:GitHub PR merge -> GitHub webhook -> Gitea controlled mirror -> immutable snapshot -> Gitea webhook -> Pipelines-as-Code -> Tekton -> GitOps/Argo -> k8s runtime。该 lane 不使用 branch-follower、Gitea Actions、act_runner 或自维护 trigger fallback。Gitea authority 属于 `config/platform-infra/gitea.yaml`PaC consumer 属于 `config/platform-infra/pipelines-as-code.yaml`CI、Argo 和 runtime 内部读取必须使用 YAML 声明的 ClusterIP URL
AgentRun PaC lane 的 closeout 以 `bun scripts/cli.ts cicd status --node <NODE>` `bun scripts/cli.ts platform-infra pipelines-as-code status --target <NODE> --consumer agentrun-<node>-v02` 为首选状态入口。默认输出必须能直接看到 webhook 是否存在、最新 PipelineRun/TaskRun duration、image status、env identity、digest、GitOps commit、Argo revision/health、manager Deployment readiness 和 runtime source/env annotation。env reuse 的通过证据是 `IMAGE_STATUS=reused`、同一 env identity 和稳定 digest;首次 cache miss 可以作为冷启动事实记录,但不能替代后续 env-reuse 秒级收口。若 GitHub PR 合并没有新 PipelineRun应先排查 `platform-infra gitea mirror webhook status|test``platform-infra pipelines-as-code history`,不得改用 `trigger-current`、直接创建 PipelineRun、手工 push Gitea 或裸 `kubectl` 作为正式补触发。若 webhook delivery 返回成功但 GitHub head、Gitea branch、snapshot 或 latest PipelineRun source 不一致,先用 `platform-infra gitea mirror webhook status --target <NODE>` 披露差异,再用受控 `platform-infra gitea mirror sync --target <NODE> --confirm` 修复 mirror/snapshot,并把自动跟进缺口归并到 pikasTech/unidesk#1622;不要重复触发旧 `trigger-current` 掩盖 source authority 分叉
AgentRun PaC lane 的默认观察入口是 `bun scripts/cli.ts cicd status --node <NODE>` `bun scripts/cli.ts platform-infra pipelines-as-code status|history --target <NODE> --consumer agentrun-<node>-v02`。输出应显示 webhook、PipelineRun/TaskRun duration、image、env identity、digest、GitOps、Argo 和 runtime provenance。GitHub PR 合并没有新 PipelineRun GitHub head、Gitea branch、snapshot PipelineRun source 不一致时,只用 webhook status 与 PaC history 定位,然后修 owning YAML、bridge/controller 或源码;禁止 mirror sync、直接 Gitea push、人工 PipelineRun 或旧 trigger fallback 补齐当前交付
YAML-only lane 的 `trigger-current --confirm` 是受控长流程入口;k8s git-mirror snapshot sync、image build、GitOps publish、git-mirror flush 和 PipelineRun 创建必须拆成短提交与状态轮询,不得把 clone、build、push 或长时间 polling 放一个顶层 `trans` 长连接。`trigger-current` 返回异步 job 时,先用 `bun scripts/cli.ts job status <jobId> --tail-bytes 12000` 观察 `agentrun-yaml-lane-trigger` progress,再用 `agentrun control-plane status --node <node> --lane <lane> --pipeline-run <name>` 观察 Tekton、GitOps 和 Argo 对齐。后台步骤的 `status``ok` 必须共同判定`status=succeeded``ok=false` 是终态失败,不能继续轮询到超时。GitOps publish 必须使用隔离临时 clone/worktree,不能切换或污染任何固定 source workspace`v0.2` 历史失败 publish 若留下 dirty/detached/GitOps branch 状态,不得通过 host workspace repair 恢复 source authority,只清理已知发布残留并从 git-mirror snapshot 重新触发
只有 owning YAML 明确解析为 `legacy-manual` 的 lane 才能进入旧 trigger/mirror 实现,且命令只在 `agentrun control-plane legacy-cicd --help``agentrun git-mirror legacy-ops --help` 中可发现。Legacy 长流程必须拆成短提交与状态轮询,不得把 clone、build、push 或长 polling 放一个顶层 `trans`。PaC 或 `unknown` authority 在任何远端调用或异步 job 创建前都必须结构化拒绝`mutation=false`
AgentRun YAML-only lane 发布收口必须以当前 k8s git-mirror snapshot tip 为准。`trigger-current` 期间若 lane source branch 被并行 PR 推进,`status --pipeline-run <name>` 会通过 `branchDrift.sourceBranchAdvanced=true` / `targetSupersededByCurrentBranch=true` 标记该 PipelineRun 已不是当前 snapshot tipcloseout 必须确认最新 snapshot commit 包含本次修复,再按最新 snapshot 重新 `trigger-current`,最后用最新 PipelineRun 的 `status` 证明 `aligned=true``blockers=[]``argoSyncedToGitops=true``managerSourceMatchesExpected=true`。不要用已经被更新 source supersede 的中间 PipelineRun 作为最终 closeout。
YAML-only lane 的 `trigger-current` 会先确保目标 branch 已同步到 k8s git-mirror snapshot,再从 UniDesk YAML 声明的 image build、GitOps branch/path、runtime namespace、Secret、数据库和 manager env 渲染 artifact catalog 与 GitOps desired state。该路径会删除新 lane source branch 中的 `deploy/deploy.json`,因为部署真相已经迁入 UniDesk YAML;旧 `v0.2` branch 中历史文件只作为迁移前遗留产物存在,不能作为新 lane 的事实来源。Secret export 格式或外部数据库连接参数变化时,先用 `platform-db postgres export-secrets --confirm` 物化本地 Secret source,再用 `agentrun control-plane secret-sync --node <node> --lane <lane> --confirm` 下发,最后用 `agentrun control-plane restart --node <node> --lane <lane> --confirm` 让 manager Deployment 通过 rollout 读取新 Secret;不要手工删除 Pod 或直接 patch Secret。
平台 Secret/config 维护继续使用 YAML `sourceRef``secret-sync` `restart`,并允许独立于 delivery authority 执行;它们不得创建 PipelineRun,也不得被描述成 source delivery recovery。不要手工删除 Pod 或直接 patch Secret。
Provider credential Secret 的 `auth.json``config.toml` 也必须按 NC01 lane 的 YAML `sourceRef` 下发,不能把指挥机全局 Codex 配置当成运行真相。lane 的模型、provider、endpoint 和 Codex runtime options 需要由 UniDesk YAML 拥有时,使用 `sourceMode: codex-config` 和同一 Secret spec 下的 `codexConfig` 渲染 `config.toml`;不要通过修改 `/root/.codex/config.toml``~/.codex/config.toml` 来改变 HWLAB/AgentRun 运行面模型。
+22 -267
View File
@@ -1,276 +1,31 @@
# G14 Provider Node
# G14 旧交付运行手册(已退役)
G14 is a configured HWLAB runtime-lane node and a UniDesk provider node for staging other infrastructure workloads. It is not the global HWLAB default; issue/CLI lane+node selection and `config/hwlab-node-lanes.yaml` decide whether a task targets G14, D601 or another node. Legacy HWLAB G14 DEV/PROD (`G14`/`G14-gitops`, `hwlab-dev`/`hwlab-prod`, ports 17666/17667 and 18666/18667, Argo Applications `hwlab-g14-dev`/`hwlab-g14-prod`) is retired and must not be used as current source, release, validation, rollback or support truth. G14's UniDesk provider id is `G14`; the local UniDesk worktree is `/root/unidesk`, and the native k3s kubeconfig is `/etc/rancher/k3s/k3s.yaml`.
这里只退役本文件承载的旧 delivery runbook;不表示 G14 节点、硬件或所有 legacy lane 已退役。本文件不再是 G14/HWLAB 交付或运行面的权威入口。旧内容混合了手工 `trigger-current`、mirror sync/flush、PipelineRun 清理、直接运行面恢复与历史 `v0.2` 路径,继续保留会与当前 YAML-first 和 PaC 自动交付规则形成第二真相。
G14's long-lived k3s control bridge is `k3sctl-adapter-g14`, a UniDesk direct service outside the k3s fault domain. It listens on the G14 host loopback port `127.0.0.1:4266` and is registered separately from the D601 `k3sctl-adapter`, so G14 infrastructure services can be built and tested without taking over user services that still run on D601.
当前工作必须按以下权威入口执行:
G14's node-level PostgreSQL platform database is host-native infrastructure, not a k3s workload or `devops-infra` namespace service. It is documented in `docs/reference/g14-platform-db.md`; HWLAB v0.3 uses it through the namespace-local `g14-platform-postgres` bridge and must not keep its old repo-owned PostgreSQL StatefulSet/PVC after migration.
- HWLAB 固定运行面、工作区、原入口验收与凭据边界:`docs/reference/hwlab.md`
- Gitea source authority、PaC/Tekton、GitOps/Argo 与自动交付边界:`docs/reference/platform-infra.md#gitea-与-pipelines-as-code-边界`
- 跨节点现场调查、持久化和原入口复测:`$dad-dev`
- CI/CD 只读观察、scoped help 与 legacy 判定:`$unidesk-cicd` 及其按需 reference。
- 固定开发机、Master 构建限制和任务 worktree`docs/reference/dev-environment.md`
For Code Queue and non-HWLAB CI/CD migration preparation, G14 uses native k3s labels `unidesk.ai/node-id=G14` and `unidesk.ai/provider-id=G14`. The G14 Code Queue manifests `src/components/microservices/k3sctl-adapter/k3s/code-queue.g14.k8s.yaml` and `src/components/microservices/k3sctl-adapter/k3s/code-queue.g14.k3s.json` are candidate staging artifacts only until an explicit production cutover is approved. Non-HWLAB production Code Queue, CI/CD and user-service execution must remain on D601 while D601 is carrying those services.
## 自动交付硬边界
## Legacy DEV/PROD Retirement
PaC migrated consumer 的唯一正式交付触发是目标 source branch 的正常 GitHub PR merge
Legacy G14 DEV/PROD is a retired runtime, not a stable baseline. The controlled UniDesk entry is:
```bash
bun scripts/cli.ts hwlab g14 retirement status
bun scripts/cli.ts hwlab g14 retirement plan
bun scripts/cli.ts hwlab g14 retirement execute --confirm
```text
GitHub PR merge
-> GitHub webhook
-> durable inbox
-> Gitea authority branch + immutable snapshot
-> Gitea webhook
-> PaC
-> Tekton
-> GitOps/Argo
-> runtime
```
`status` reports the legacy Argo Applications, legacy namespaces, bounded resource previews, protected v0.2/v0.3 Applications and namespaces, and the local marker at `.state/hwlab-g14/legacy-g14-retirement.json`. `plan` is dry-run only and lists the exact destructive targets. `execute --confirm` deletes only `argocd/hwlab-g14-dev`, `argocd/hwlab-g14-prod`, namespaces `hwlab-dev`, `hwlab-prod`, and active local `hwlab_g14_pr_monitor` jobs; it must not touch `hwlab-g14-v02`, `hwlab-node-v03`, `hwlab-v02`, or `hwlab-v03`. The legacy base=`G14` PR monitor is blocked by this retirement contract even in a fresh checkout; the marker is execution evidence, not the source of the policy.
合并后不得由主代理、子代理或操作者执行 `apply``trigger-current`、mirror sync/flush、人工 PipelineRun、直接 Gitea push、运行面 patch、hook test 或其他补跑。链路失败时必须修复 owning YAML、controller 或源码,并通过后续正常 PR merge 产生的新自动事件验收。
The old `/root/hwlab` workspace on branch `G14` is no longer a default source truth. Use it only for explicit legacy archaeology or a user-authorized retirement follow-up, after fast-forwarding and reading the HWLAB repo rules. Current source, render, CI/CD and validation work must use the workspace resolved from the selected node/lane, such as `G14:/root/hwlab-v02` for G14 v0.2 or `D601:/home/ubuntu/workspace/hwlab-v03` for D601 v0.3.
The standard entry forms are:
```bash
trans G14:/root/hwlab sh -- 'git fetch origin G14 && git pull --ff-only origin G14 && git status --short --branch && git remote -v'
trans G14:/root/hwlab apply-patch < patch.diff
trans G14:k3s kubectl get pods -n hwlab-v02
```
`G14:k3s` is the only supported k3s route form. Do not use `ssh G14 k3s ...`; the first token must locate the distributed target, and the following tokens must be the operation.
If `/root/hwlab` has unrelated local changes when this sync starts, first determine whether they can be quickly merged with the latest `origin/G14`. Merge them immediately when they are mergeable; do not default to stash, discard or a behind worktree. Only when the changes cannot be automatically merged should they be isolated and the operation stopped for human decision. A behind fixed workspace is not a valid basis for precheck, new worktree creation, render, polling, deployment or runtime validation.
The retired G14 DEV/PROD boundary is:
- Legacy public endpoints `http://74.48.78.17:17666/`, `http://74.48.78.17:17667/health/live`, `http://74.48.78.17:18666/`, and `http://74.48.78.17:18667/health/live` are not current validation targets.
- Keep HWLAB Services as `ClusterIP` unless a repo-owned G14 GitOps rule explicitly exposes them. Public exposure should stay in the approved G14 edge/proxy path, not ad hoc NodePort or local port-forward.
- Use runtime-lane local PostgreSQL and runtime-lane Secrets for cloud-api durable runtime tests. Do not copy D601 database credentials.
- Use only G14-local Codex auth material and k8s Secrets authorized for HWLAB on G14; do not copy D601 or production auth material by hand.
- Set `HWLAB_CLOUD_API_PORT=6667` explicitly in the G14 cloud-api Deployment. Kubernetes otherwise injects a `HWLAB_CLOUD_API_PORT=tcp://...` Service environment variable that breaks the Node port parser.
- `HWLAB_PUBLIC_ENDPOINT` and health/live evidence must describe the active runtime lane endpoint, not retired G14 DEV/PROD or old D601 production endpoints.
- Do not run HWLAB repository `check`, Playwright/browser smoke, image builds or other heavy validation on the master server. For G14 targets, run those through the target G14 runtime lane workspace, G14 k3s/Tekton, or another explicitly approved external execution plane; for non-G14 targets, use the selected node/lane execution plane from `config/hwlab-node-lanes.yaml`.
- Manual device-agent experiments for real hardware must be standalone resources in the active runtime lane namespace and must not patch existing HWLAB Deployments, Services, ArgoCD Applications, FRP, CD desired-state or public frontend routing unless a separate HWLAB change authorizes it.
- A D601 Windows `hwlab-gateway` may connect outbound to an active G14 runtime lane cloud-api as an external host bridge for Keil/serial/workspace access. In that G14 device-agent model, the Windows bridge is not the runtime target; D601 can still be a first-class HWLAB runtime node when issue/CLI explicitly selects a D601 node-scoped lane.
Healthy G14 HWLAB runtime means the active runtime lane's main Deployments and StatefulSets are Ready, `cloud-api` and `edge-proxy` return `/health/live` with `status=ok`, durable runtime checks pass, and the public lane endpoints report the expected revision. For a device-agent smoke, health also requires the standalone device-agent Service to answer in-cluster and the D601 Windows gateway session/resource/capability to be visible through the active G14 cloud-api.
## HWLAB v0.2 Expansion Line
HWLAB `v0.2` is the supported G14 runtime lane for the v0.2 branch. It must not recreate, depend on, or roll back to the retired legacy `G14` DEV/PROD runtime. Legacy `G14`/`G14-gitops`, `hwlab-dev`/`hwlab-prod`, DEV public ports `17666/17667`, and PROD public ports `18666/18667` are not the stability baseline.
The fixed `v0.2` source branch is `v0.2`, forked from the current `G14` branch after the G14 long-term reference docs record this decision. The fixed G14 development workspace for that branch is:
```bash
trans G14:/root/hwlab-v02 sh -- 'git status --short --branch && git remote -v'
```
`/root/hwlab-v02` is the long-lived `v0.2` development workspace, not a scratch clone or CI/CD source selector. It must track `origin/v0.2` with `origin git@github.com:pikasTech/HWLAB.git`; local dirty state, stale `HEAD`, and untracked `.worktree/` only affect human development. Do not reuse retired `/root/hwlab` or `/root/hwlab/.worktree/*` as the `v0.2` fixed workspace.
`v0.2` CI/CD source selection is isolated in the dedicated bare repo `G14:/root/hwlab-v02-cicd.git`. UniDesk control-plane commands must fetch `origin/v0.2` into that repo and render from a commit-pinned detached worktree; they must not read the source commit from `/root/hwlab-v02` checkout state.
The fixed `v0.2` runtime namespace is `hwlab-v02`. The intended public FRP allocation is:
- Cloud Web browser entry: `http://74.48.78.17:19666/`.
- API/edge entry and live health: `http://74.48.78.17:19667/health/live`.
Master-side FRP server maintenance for HWLAB public ports is documented in `docs/reference/hwlab.md#hwlab-frp-维护`; keep the detailed allowlist, restart boundary and verification sequence there instead of duplicating another runbook in this G14 node reference.
The `v0.2` CI/CD integration owns a dedicated CI/CD source repo, `devops-infra` git mirror/relay, GitOps desired-state lane, Argo CD Application, namespace resources, artifact catalog, and a `deploy/deploy.yaml` lane config only when they target `v0.2`/`hwlab-v02` explicitly. Do not add or revive a legacy `G14` branch poller, DEV/PROD Argo Applications, DEV/PROD runtime paths, or existing namespace resources to bootstrap `v0.2`.
For current G14/v0.2, `deploy/deploy.yaml` is the single human-authored deploy/runtime config source. `deploy/deploy.json` is not a v0.2 compatibility source and must not be recreated for this lane. YAML and JSON parsing are centralized in the HWLAB repo's format-agnostic config layer: `scripts/src/structured-config.mjs` handles file format parsing/writing, while `scripts/src/deploy-config.mjs` owns deploy-config defaults and shape. Renderers, planners, smoke scripts and CLIs should consume that layer through `readStructuredFile` / `writeStructuredFile` / `readDeployConfig` or equivalent helpers; do not scatter direct YAML parser imports or ad hoc `readFile` + `YAML.parse` calls. Legacy D601 HWLAB CD still has its own `deploy/deploy.json` desired-state documented in `docs/reference/hwlab.md`; that legacy path is not the G14/v0.2 authority.
On the UniDesk control-plane side, HWLAB runtime lane expansion is sourced from `/root/unidesk/config/hwlab-node-lanes.yaml`. That YAML owns `nodes`, `lanes`, `networkProfiles`, `downloadProfiles`, target overrides and public entrypoints for the UniDesk trigger/status/apply layer. The base `lanes.v03` entry can point at `nodes.G14`, while `lanes.v03.targets.D601` owns the D601 v0.3 workspace, CI/CD repo, runtime path and `https://hwlab.pikapython.com` public exposure. Proxy URLs, `NO_PROXY`, git/npm/pip/docker/curl retry/download defaults and Docker build proxy settings live under profile objects. G14 must remain a node config value, not a hardcoded code path such as `g14Proxy` or `g14GitOps`; future lanes and node targets should be added by adding YAML entries and then consuming the generated spec. `hyueapi.com` and `.hyueapi.com` are required `NO_PROXY` entries and must remain present in effective runtime and Docker build proxy environments.
Runtime lane base images are also node/lane YAML truth. `baseImage` is the node-local registry artifact consumed by Tekton/BuildKit, while `baseImageSource` is the public source image used to seed it. For G14 v0.3, inspect and seed this dependency through `bun scripts/cli.ts hwlab nodes control-plane runtime-image status --node G14 --lane v03` and `bun scripts/cli.ts hwlab nodes control-plane runtime-image preload --node G14 --lane v03 --confirm`; `runtime-image build` is only a preload alias until a separate image-build workflow is explicitly designed. Do not hand-run `docker tag`, `docker push` or raw registry shell as the long-term control path. If the base image status is ready but a later PipelineRun fails in a service-specific build task, track that as a separate CI/service issue instead of reopening the base-image preload path.
The `devops-infra` git mirror/relay remains manual and CLI-controlled, not CronJob-driven. The standard `v0.2` delivery trigger is `bun scripts/cli.ts hwlab g14 monitor-prs --lane v02`: it watches base=`v0.2` PRs, waits for GitHub preflight/CI readiness, auto-merges only ready and non-conflicting PRs, then drives the same controlled CD path and comments pending/blocked/succeeded/failed/timeout state back to the PR. The `v0.3` delivery trigger is `bun scripts/cli.ts hwlab g14 monitor-prs --lane v03`: it watches base=`v0.3` PRs, uses the runtime lane control-plane to trigger CD, verifies PipelineRun/Argo/`hwlab-v03` runtime public probes/Git mirror flush, comments PR progress, and creates or updates failure issues for failed checks, conflicts, merge blockers, CD failures and timeouts. The lower-level `bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02|v03 --confirm` remains the manual recovery or diagnosis entry; it must fetch the lane CI/CD bare repo, resolve the current source branch commit, check the mirror source ref before creating the PipelineRun, run one bounded manual `git-mirror sync` Job when the mirror is stale, and only continue after the mirror ref matches the current source commit. Use `hwlab g14 git-mirror sync --confirm` directly only for explicit mirror maintenance or diagnosis.
After a `v0.2` PipelineRun completes, treat runtime rollout and remote GitOps persistence as two separate checks. `hwlab g14 control-plane status --lane v02` is the runtime check: it must show the expected source commit, PipelineRun completed, Argo `Synced/Healthy`, public 19666/19667 probes passing, and Cloud Web asset probes such as `/app.js` readable. `hwlab g14 git-mirror status` is the persistence check: `cache.summary.pendingFlush` must be false and `cache.summary.githubInSync` true before declaring GitOps fully flushed back to GitHub. The PR monitor performs this flush automatically for its own merged PRs and records the result in the PR comment. Manual operators should run `bun scripts/cli.ts hwlab g14 git-mirror flush --confirm` and poll the returned job with `bun scripts/cli.ts job status <jobId> --tail-bytes 12000` only when they used lower-level manual trigger/status paths or when the monitor reports a flush failure; do not replace this with raw `kubectl`, native `git push`, or a long SSH wait.
For D601/node-scoped runtime lanes, `hwlab nodes git-mirror status --node <node> --lane <lane>` reports GitHub refs from the mirror cache's `refs/mirror-stage/...`, not from a live GitHub API request. The status output must keep that source visible through `refSources.githubFieldsAreMirrorStageCache=true`. A `flush --wait` run can push the GitOps ref successfully and still hit a transient GitHub SSH error such as `kex_exchange_identification` during follow-up fetch/verification; this condition is reported as `partialSuccess=push-succeeded-fetch-failed`. Current CLI should first perform a controlled sync/recheck and mark `partialSuccessRecovered=true` when `pendingFlush=false` and `githubInSync=true`; only unrecovered cases should point operators at `hwlab nodes git-mirror sync --node <node> --lane <lane> --confirm --wait`. Do not keep repeating blind flush attempts; refresh `refs/mirror-stage/...` with `sync --wait`, then recheck status for `localGitops=githubGitops`, `pendingFlush=false`, and `githubInSync=true`.
If `gitops-promote` fails because the git mirror control plane drifted, refs are inconsistent, or publish/flush did not complete, recover through the controlled mirror path: `hwlab g14 git-mirror apply --confirm` to reinstall the current hook/ConfigMap, `hwlab g14 git-mirror sync --confirm --wait` to realign source and GitOps refs, then a targeted `control-plane cleanup-runs --pipeline-run <failed-run> --confirm` before retriggering the same lane. The old branch/path allowlist gate has been removed; do not restore it, patch the hook inside the pod, delete PipelineRuns with raw kubectl, or bypass `git-mirror flush`. Closeout still requires the target PipelineRun status, Argo health, public probes, and `git-mirror status` with `pendingFlush=false`.
When closing an issue against a specific completed `v0.2` PipelineRun, use targeted status instead of the latest-head status if `origin/v0.2` has already advanced through a parallel task:
```bash
bun scripts/cli.ts hwlab g14 control-plane status --lane v02 --pipeline-run hwlab-v02-ci-poll-<short-sha>
bun scripts/cli.ts hwlab g14 control-plane status --lane v02 --source-commit <full-sha>
```
Targeted status must expose `statusTarget.mode` and `targetValidation`. `targetValidation.state=passed` means the requested PipelineRun/source commit reached a succeeded PipelineRun, Argo `Synced/Healthy`, public web/API probes, flushed Git mirror, and matching runtime source commits for the services listed in that run's `planArtifacts.rolloutServices`; services listed in `planArtifacts.reusedServices` remain visible as runtime/provenance evidence but must not be forced to the target source commit. `targetValidation.state=superseded` means the requested PipelineRun succeeded but no longer owns runtime: either it was replaced by a newer succeeded `v0.2` PipelineRun, or latest-only promotion observed that `origin/v0.2` had advanced before GitOps/runtime writeback and closed the historical run as no-op. This is valid closure evidence for the requested run when the newer commit is on the same branch lineage. In both states, `commitAlignment.staleReasons` may still mention later `origin/v0.2` or CI/CD source head movement; that is parallel-head context, not a failure of the requested run. `falseGreenGuard` is a current-runtime guard and should report not-applicable/superseded for such historical targets instead of turning later runtime movement into a false failure. Default status without a target remains strict for the latest source head.
For HWLAB user-feedback, CLI, Cloud Web, AgentRun, device-pod, public API, or runtime workflow issues, source-level validation is not enough to close the issue. Source checks, `git diff --check`, targeted build checks, PR merge metadata, and source commit rollout evidence are supporting evidence only. The issue may be closed only after the affected user entry or original entry has been exercised against the target runtime. For CLI issues, that means running the relevant `hwlab-cli` or UniDesk-controlled CLI command from the G14 `v0.2` workspace or approved execution plane against the intended lane/URL/namespace and proving the observed behavior, not just proving the helper code compiles. For Cloud Web or public API issues, use the public endpoint or a bounded API/asset smoke that reaches the deployed runtime. For AgentRun or device-pod issues, capture the trace/session/thread/run/job/device evidence that proves the specific continuation or hardware workflow reached the live backend.
For Cloud Web Workbench and Code Agent issues, the closeout validation must use the same dispatch entry as the browser flow, or a CLI command that calls that same Cloud Web/Cloud API dispatcher path. A hand-written `dispatchHwlabAgentRun()` canary, direct AgentRun manager command, or runner job created outside the Web dispatcher is only infrastructure evidence; it cannot prove that the browser path requested the current AgentRun runtime assembly, tool credentials, transient env, conversation/session/thread binding, or runtime lane. The current HWLAB v0.2 resource assembly requirement is `ResourceBundleRef.kind="gitbundle"` with `bundles[]` materializing repo `tools/` and `skills/`; the authority for those fields is UniDesk OA [Runtime装配](../../project-management/PJ2026-01/specs/PJ2026-010202-runtime-assembly.md) and [HWLAB接入](../../project-management/PJ2026-01/specs/PJ2026-010205-hwlab-dispatch.md). If no CLI can exercise the Web-equivalent path, improve the CLI first and keep the issue open until the Web-equivalent CLI or browser trace proves the deployed behavior.
Provider profile configuration and credential write rules for Code Agent are owned by `docs/reference/hwlab.md#code-agent-provider-profile-配置与验收`. This G14 reference only defines runtime lane and closeout evidence; do not duplicate profile Secret, `config.toml`, `auth.json` or CLI credential semantics here.
For Cloud Web Workbench Code Agent response or trace-rendering bugs, the minimum Web-equivalent CLI proof is a fresh `hwlab-cli client agent send --wait` against the deployed public Web origin, followed by `hwlab-cli client agent trace <traceId> --render web` against the same origin. The submit proof must show the browser dispatcher family, normally `POST /v1/agent/chat`, result polling through `/v1/agent/chat/result/<traceId>`, `continuation.webEquivalent=true`, `shortConnection=true`, and explicit `sessionId` / `conversationId` / `threadId` binding when those values affect the bug. The result proof must show the final assistant text from `assistantText` or `reply.content`; placeholder status text, result summaries, terminal status messages, and AgentRun completion boilerplate are not acceptable substitutes for the assistant final response.
For persisted final-response display regressions, a fresh turn alone is not enough when the user report identifies an existing conversation, session, or trace. Re-read the original record on the deployed `v0.2` runtime with locked lane env and the correct `projectId`; the default session list project may differ from the affected Workbench project. The minimum proof is `client session list --project-id <projectId> --limit <N> --full`, `client session inspect <conversationId> --full`, and `client agent result <traceId> --full`. Passing evidence must show that list and inspect surface the same latest agent `traceId` as `lastTraceId`, the latest agent text matches the terminal result `reply.content` or equivalent final assistant text, and known fallback text such as `Code Agent 仍在处理,可以继续 steer 或等待 trace 完成。` is absent from list, inspect, and result output. When the repair is lazy-on-read, run the read path again or capture the exposed repair source/updated marker so the evidence proves persisted conversation state was repaired, not merely synthesized for one response. `client agent trace <traceId> --render web` remains required for trace-rendering bugs; for persisted conversation-display bugs it is supporting evidence unless it returns rendered assistant rows from the same original trace.
The `--render web` proof must inspect the rendered body, not only the raw event count. Passing evidence should include `body.render=web`, the shared renderer identity when exposed, `status=completed`, rendered/returned row counts, noise/omitted counts when available, at least one rendered assistant row containing the final assistant text, and an explicit absence check for known non-user boilerplate such as `AgentRun terminal status completed`, `AgentRun result is ready`, and `Code Agent 仍在处理`. If the trace API returns `status=missing`, `sourceEventCount=0`, or no rows for a historical issue trace, treat that trace as expired or unavailable; do not use it as closure evidence. Generate a fresh equivalent turn on the current v0.2 runtime and validate that trace instead.
CLI/Web-equivalent trace evidence does not replace browser UI evidence for visual, layout, copy-to-clipboard, collapsed-panel or removed-control bugs. G14/v0.2 browser or DOM evidence must still hit the deployed public endpoint for the target lane, but Playwright/web-probe/fake-server/screenshot operation details are owned by `$unidesk-webdev`; this G14 reference only records lane and rollout evidence.
The closing comment for these issues must be semantic natural language before it lists evidence: state what the user-visible problem was, what changed, where it rolled out, and what original entry was rechecked. It must include the actual command or entry path, target lane or endpoint, relevant trace/session/thread/PipelineRun/run/device ids, and the pass/fail result. If the original entry cannot be verified because rollout has not happened, credentials are unavailable, the target runtime is down, or the required CLI capability is missing, keep the issue open and record the blocker. Do not close the issue on the strength of PR merge, targeted tests, or "will be verified after rollout" wording. If an issue was closed before this real CLI/user-entry validation, reopen it and add a correction comment before continuing.
For HWLAB v0.2 Code Agent context-loss or multi-turn continuity issues, the minimum closeout is a real `hwlab-cli client agent` two-turn E2E from `G14:/root/hwlab-v02` or another approved G14 execution plane with locked runtime namespace/lane env. Submit the first turn, poll its result to completed, submit the second turn with the same explicit `conversationId`/`sessionId`/`threadId`, then capture `trace`/`inspect` evidence. Passing evidence must show the second turn used prior-turn context, and should include context attachment or run reuse labels such as `conversation-context:attached`, `agentrun:run:reused`, `agentrun:runner-job:reused`, plus the relevant run/command ids. Long verification evidence belongs in a separate `gh issue comment create --body-file` comment; lifecycle close comments stay short, as defined in `docs/reference/cli.md`.
`/health/live` revision is owned by `hwlab-cloud-api`; it can legitimately differ from the source commit for a Cloud Web-only change. Do not call that difference a failed Cloud Web rollout when `webAssets.checks.htmlOk`, `webAssets.checks.appJsOk`, CSS probes, Argo health, and `hwlab-cloud-web` Deployment readiness have passed. For Cloud Web behavior changes, the public JS asset probe or a bounded browser/DOM check is stronger evidence than cloud-api `apiRevision`.
Do not turn `v0.2` expansion governance into a stack of broad compatibility gates. The stable control points are branch, dedicated CI/CD source repo, git mirror/relay refs, GitOps branch, namespace, runtime path, Argo Application, FRP ports and generated-output ownership. Legacy DEV/D601/main preflights that block the `v0.2` lane should be removed from that lane, not patched with fallback or legacy modes. Naming, RBAC scope, cleanup policy, resource quota and rollback order are design decisions or runbook entries unless they protect a concrete high-value risk that cannot be enforced by the fixed boundaries above.
## v0.2 Source Workflow
The generic P2/P3/P4 flow is owned by `$dad-dev`; this section fixes the G14/v0.2 source route, branch and lane. `v0.2` now has two source workflows:
- `direct-lightweight`: CaseRun, case registry aggregation, trace rendering, short-connection CLI/helper, docs/reference, schema-free config reader/writer, and deploy-config cleanup that does not change cloud-api, web, gateway, GitOps, k3s runtime or other long-running services. Use the fixed workspace directly, run the relevant CLI/render/test validation on G14, commit to `v0.2`, and push `origin v0.2`. Do not open a PR, trigger CI/CD, rollout, or reintroduce legacy gates for this class.
- `pr-rollout` / service workflow: cloud-api, Cloud Web UI, gateway, AgentRun dispatch integration, device-pod runtime, GitOps/Tekton/Argo, k3s manifests, Secrets/RBAC, public endpoint behavior, or any change that must reach live runtime. Use a task-scoped worktree on a feature branch, merge through a PR, then use the controlled v0.2 CD/status path.
Direct-lightweight precheck:
```bash
trans G14:/root/hwlab-v02 sh -- 'git fetch origin v0.2 && git pull --ff-only origin v0.2 && git status --short --branch'
```
Service workflow setup:
```bash
trans G14:/root/hwlab-v02 sh -- 'git worktree add .worktree/<task> -b fix/issue<N>-<short-name> origin/v0.2'
```
The fixed repo at `/root/hwlab-v02` is not a scratch area for service/runtime work, but it is the direct-lightweight source workspace. When a direct-lightweight task sees parallel dirty state in the fixed repo, inspect and include or separate it according to the current user instruction and project Git rules; never discard it silently. Worktree branches for service workflow should follow the `fix/issue<N>-<short-name>` naming so PR titles and merge commits stay scannable. GitHub PR writes, merge, rollout trigger and final original-entry validation follow `$dad-dev` plus the UniDesk CLI control rules in `AGENTS.md`.
### Recovery From an Unapproved Direct Commit To v0.2
Direct-lightweight commits are allowed and do not need recovery. A direct commit on `v0.2` only needs recovery when it changed service/runtime/GitOps/CI/CD/public behavior that should have used the PR/rollout workflow. The recovery is bounded and audit-friendly, but it is also a `git push --force-with-lease` against the protected branch, so it is only acceptable when the unapproved direct commit is the only new content on `v0.2` since the last merged PR:
1. Confirm no parallel worktree was in flight and the commit is the only delta. `trans G14:/root/hwlab-v02 sh -- 'git log origin/v0.2..HEAD'` and `git log HEAD..origin/v0.2` must show the direct commit as a single fast-forward candidate.
2. Capture the commit identity and patch for the recovery record:
```bash
trans G14:/root/hwlab-v02 sh -- 'git show <direct-commit-sha> > /tmp/v0.2-recovery.patch'
```
3. Roll the fixed repo back to the previous merged PR head. Use `git reset --hard <previous-pr-sha>`; this preserves any autostash (e.g. from a parallel `git checkout` snapshot in another worktree) on the stash list and does not touch the other worktree's working tree.
4. In the pre-existing worktree (e.g. `.worktree/<task>` on `fix/issue<N>-<short-name>`) bring the branch up to the previous PR head with `trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'git reset --hard <previous-pr-sha>'`, then `git cherry-pick <direct-commit-sha>` to replay the direct commit on the feature branch. If the worktree branch was already a clean clone of `origin/v0.2` at the previous PR head, the reset is a no-op.
5. Push the feature branch and force-push `v0.2` back to the rolled-back head with `--force-with-lease` (refuses to clobber a concurrent push):
```bash
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'git push -u origin fix/issue<N>-<short-name>'
trans G14:/root/hwlab-v02 sh -- 'git push --force-with-lease origin v0.2'
```
6. Open the PR through UniDesk CLI, squash-merge, then `git pull --ff-only origin v0.2` to bring the fixed repo back in sync. The previous PR's merge commit will not be in the new PR's history; the new PR's diff equals the original direct commit's diff, so the PR trail still contains the exact same bytes.
7. `bun scripts/cli.ts hwlab g14 control-plane status --lane v02` will read the new merge commit; the previously-staged PipelineRun for the direct commit was created on the v0.2 head and `trigger-current` will delete + recreate it for the post-merge head, so no manual PipelineRun cleanup is required.
The recovery is auditable: the original `git show` patch and the `cherry-pick` SHA both land in the PR diff, so the issue/PR trail still contains the exact same bytes that were first committed directly. Recurring unapproved service/runtime direct commits on `v0.2` are a workflow regression and must be called out in the relevant issue or PR; direct-lightweight commits are not a regression.
### v0.2 Cloud Web Runtime Layout Validation
Cloud Web layout, status-panel, collapsed-control, modal, Markdown renderer, scroll-owner and Workbench DOM issues on `v0.2` need deployed browser evidence after rollout; source checks and control-plane status are supporting evidence. Use `$unidesk-webdev` for the actual Playwright/web-probe shape, bounded screenshot artifact rules, layout metrics and no-skip dependency policy. G14/v0.2 closeout still records the lane endpoint, rollout provenance, `/health/live`, `/v1/live-builds` service row and the specific user-visible DOM assertion.
### v0.2 Cloud Web Button/JS Sync Rule (HWLAB #748)
When a `v0.2` Cloud Web fix removes a button from `index.html` or a field from the `el` literal in `web/hwlab-cloud-web/app.ts`, every `el.<removed-field>.addEventListener(...)` (or `.requestSubmit()` / `.showModal()` / etc.) binding must be removed from the matching `init*` function in the same commit. The static `web:check` does not catch this orphan listener class because the TypeScript build is `Bun.build` transpile-only (no `tsc --noEmit`), and the runtime crash only surfaces as `Cannot read properties of undefined (reading 'addEventListener')` on first init. The minimal closeout checks for the v0.2 lane are:
```bash
# 1. Web assets rebuild and the orphan is gone from the dist
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'cd web/hwlab-cloud-web && bun run build'
trans G14:/root/hwlab-v02/.worktree/<task> sh -- "grep -c '<removed-field>' web/hwlab-cloud-web/dist/app.js" # must be 0
trans G14:/root/hwlab-v02/.worktree/<task> sh -- "grep -c 'id=\"<removed-id>\"' web/hwlab-cloud-web/index.html" # must be 0
# 2. Live 19666/19667 confirms the deployed bundle is the new build
curl -fsS http://74.48.78.17:19666/ | grep -c '<removed-id>' # must be 0
curl -fsS http://74.48.78.17:19666/app.js | grep -c '<removed-field>' # must be 0
bun scripts/cli.ts hwlab g14 control-plane status --lane v02 # webAssets.checks.appJsOk = true, sourceCommit = merge commit
```
While the PR is open, the author can also run a one-liner to surface any orphan `el.<field>.addEventListener` whose field is not declared in the `el` literal of `app.ts`:
```bash
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'awk "/^const el = /,/^};/" web/hwlab-cloud-web/app.ts | tr -d "," | awk "{print \$1}" | grep -E "^[a-zA-Z]" | sort -u > /tmp/el-fields.txt; grep -nEo "el\\.([A-Za-z_$][A-Za-z0-9_$]*)\\.addEventListener" web/hwlab-cloud-web/*.ts | while read m; do field=$(echo "$m" | sed -E "s/.*el\\.([A-Za-z_$][A-Za-z0-9_$]*)\\.addEventListener.*/\\1/"); if ! grep -q "^$field$" /tmp/el-fields.txt; then echo "ORPHAN: el.$field.addEventListener"; fi; done'
```
Document the explicit `grep` / curl evidence in the issue closeout comment. Tightening the `el` literal with proper TypeScript types is tracked separately and must not be done as part of a runtime fix PR.
## Node-Local VPN Proxy
G14 has a node-local VPN/proxy stack for infrastructure bootstrap and recovery downloads:
- Primary mixed HTTP/SOCKS proxy: `127.0.0.1:10808`.
- Backup Hysteria2 HTTP proxy: `127.0.0.1:11809`.
- Backup Hysteria2 SOCKS5 proxy: `127.0.0.1:11808`.
- Operator-only local details remain on G14 under `/root/docs/vpn-proxy-ops.md`; subscription URLs, node credentials and GUI database contents must not be copied into the UniDesk repository.
The G14 host persists this proxy configuration in these local files:
- `/etc/profile.d/unidesk-g14-proxy.sh` exports `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, lowercase aliases and `NO_PROXY` for new login shells. Set `UNIDESK_G14_DISABLE_PROXY=1` before shell startup to opt out.
- `/root/.npmrc` pins npm `proxy`, `https-proxy`, `noproxy` and retry settings for root-side bootstrap commands.
- `/root/.gitconfig` pins root Git HTTP/HTTPS proxy settings.
- `/root/.docker/config.json` pins Docker client proxy settings for commands and build contexts that honor Docker client proxy configuration.
- `/etc/systemd/system/docker.service.d/proxy.conf` pins Docker daemon pull proxy settings. Updating this drop-in requires `systemctl daemon-reload` and a Docker restart before the active daemon sees the new `NO_PROXY`; do not restart Docker while G14 provider-gateway, k3s bootstrap or image builds are in flight unless that interruption is intentional.
- `/etc/systemd/system/k3s.service.env` pins k3s/containerd image-pull proxy settings. If Pod events show image pulls failing through a stale loopback proxy such as `127.0.0.1:10808` while Hysteria is the active listener, update this file through `trans G14:/etc/systemd/system apply-patch`, run `systemctl daemon-reload`, restart k3s, then verify node readiness and the target image pull. Do not hand-import images as the long-term fix for a stale k3s proxy env.
The `NO_PROXY` list must include localhost, the main server, private LAN ranges, k3s pod/service CIDRs, Kubernetes service domains and the loopback registry so that k3s, `127.0.0.1:5000`, Kubernetes API access and UniDesk control paths do not route through the VPN proxy.
The primary proxy can be used for G14 target-side image bootstrap when Docker Hub, npm, GitHub or Playwright downloads are unreliable through direct network or provider-gateway WS egress. For Docker build steps that use `127.0.0.1`, build with host networking so the build container reaches the host proxy:
```bash
docker build --network host \
--build-arg HTTP_PROXY=http://127.0.0.1:10808 \
--build-arg HTTPS_PROXY=http://127.0.0.1:10808 \
--build-arg ALL_PROXY=socks5h://127.0.0.1:10808 \
--build-arg http_proxy=http://127.0.0.1:10808 \
--build-arg https_proxy=http://127.0.0.1:10808 \
--build-arg all_proxy=socks5h://127.0.0.1:10808 \
...
```
`127.0.0.1:10808` is a G14 host loopback endpoint. Inside an ordinary k3s Pod, `127.0.0.1` is the Pod network namespace, not the node proxy. Do not set long-lived workload proxy env to `http://127.0.0.1:10808` unless that workload is intentionally `hostNetwork` and the port conflict/DEV-PROD blast radius has been reviewed. Temporary hostNetwork debug Pods may use the node-local proxy only for bounded bootstrap proof or cache prewarm; they must not become GitOps desired state just to make external downloads work.
The backup proxy uses `HTTP_PROXY=http://127.0.0.1:11809`, `HTTPS_PROXY=http://127.0.0.1:11809` and `ALL_PROXY=socks5h://127.0.0.1:11808`.
This proxy is not a replacement for UniDesk runtime egress. k3s workloads such as Code Queue must still use the cataloged `g14-provider-egress-proxy` Kubernetes Service and `g14-tcp-egress-gateway` for normal runtime access to PostgreSQL, OA Event Flow and external APIs. The node-local VPN proxy is allowed only for G14 host-side bootstrap, k3s image pulls, image build, cache prewarm or recovery steps, and those steps should record the proxy choice in issue or deployment evidence.
Runtime egress readiness is not proven by Service DNS alone. Before closing HWLAB, AgentRun, Code Queue, CI/CD or user-service issues that require GitHub/codeload/npm/pip/API access through `g14-provider-egress-proxy`, validate that the Deployment is Ready and the Service has at least one ready endpoint. If the Service resolves but `curl` through `http://g14-provider-egress-proxy.unidesk.svc.cluster.local:18789` fails immediately with connection refused, inspect `unidesk/g14-provider-egress-proxy` and `unidesk/g14-tcp-egress-gateway` pods for `ImagePullBackOff`, `ContainerStatusUnknown` or notReady endpoints. A pod trying to pull `unidesk-code-queue:g14` from Docker Hub as `docker.io/library/unidesk-code-queue:g14` is an invalid runtime egress deployment state; restore the controlled image source/import path instead of redirecting long-lived workload proxy env to the node-local bootstrap proxy.
## v0.2 device-pod cloud-api architecture
`v0.2` device-pod integration is the cloud-api → executor → D601 Windows `device-host-cli.mjs` chain under `internal/cloud/access-control.ts`, `cmd/hwlab-device-pod/main.ts` and the host-side `F:\Work\ConStart\tools\device-host-cli.mjs`. PR #765 (selector cheat sheet + fail-fast) and PR #778 (output.text propagation + evidence selector + read-only sub-action `--reason` exemption) are the two anchor PRs; PR #779 tracks the still-open host-side ops work. Earlier work used raw `MUTATING_INTENTS.has(intent) && !reason` and a single-pass `textOr(output.text, …)` extractor; both are obsolete and must not be re-introduced.
### Intent / sub-action / reason matrix
`DEVICE_JOB_INTENTS` (cloud-api) enumerates the full supported surface; `MUTATING_INTENTS` is the strict subset whose default sub-action is mutating. Only `workspace.build` and `debug.download` carry a structured sub-action (`start` / `status` / `output` / `wait` / `cancel` / `evidence`) and are listed in `DEVICE_JOB_ACTIONABLE_INTENTS`; for those two, `_deviceJobRequiresReason(intent, args, reason)` returns `false` when `reason` is provided OR when `args.action` is in `DEVICE_JOB_READ_ONLY_SUB_ACTIONS`. Any other mutating intent (`workspace.apply-patch`, `workspace.put`, `debug.reset`, `io.uart.write`, `io.uart.jsonrpc`, `io.uart.read-after-launch-flash`, etc.) still always requires a non-empty `reason`. Adding a new actionable mutating intent requires extending both `MUTATING_INTENTS` and `DEVICE_JOB_ACTIONABLE_INTENTS` together; adding a new read-only sub-action requires only the `DEVICE_JOB_READ_ONLY_SUB_ACTIONS` set.
The `evidence` sub-action on `workspace.evidence` / `debug.evidence` is a first-class intent, not a `workspace.build` sub-action. Code Agent sees `<pod>:workspace:/ build evidence [jobId]` and `<pod>:debug-probe download evidence [jobId]`; cloud-api maps to a new device-pod executor job, the executor maps to `deviceHostArgs = ["workspace", "evidence", kind, ...]`, and the host-side `device-host-cli.mjs` dispatches via `if (command === "evidence")` at the top level of `main()` (not nested under `if (command === "build")`). `workspace.evidence kind=build` → `keil-build` job; `debug.evidence kind=download` → `keil-download` job; the kind sub-arg must be `build` / `download` and the optional jobId selects a specific past job.
### Output text propagation chain
`body.output.text` flows through three layers in order; each layer tries more fields and only falls back when earlier sources are empty:
1. **host `device-host-cli.mjs`** returns a JSON envelope that already contains `stdout` / `stderr` / `summary` / `logTail` / `buildSummary` for build/download ops; `workspace.ls` / `workspace.cat` / `workspace.rg` are inline and include a JSON body.
2. **executor `cmd/hwlab-device-pod/main.ts` `gatewayDispatchText(result, dispatch)`** walks `result.stdout` → `result.stderr` → `dispatch.stdout` → `dispatch.stderr` → `result.evidence.{text,logTail,summary}` → `dispatch.message` (only when `dispatchStatus === "completed"`) → `dispatch.summary` → `result.summary` → `dispatch.buildSummary` → `result.text` → `JSON.stringify(result)`. The executor stores this as `job.output` and exposes it via `boundedOutput()` which clips at `DEVICE_JOB_OUTPUT_MAX_BYTES` (12000) and drops `executor` / nested `output` when truncated.
3. **cloud-api `executorOutputPayload(body, httpStatus)`** wraps what the executor sent and exposes `body.text` / `body.output` / `body.bytes` / `body.truncation` to the `/v1/device-pods/{id}/jobs/{jobId}/output` endpoint. `text` is `firstString(body?.text, output.text, nestedOutput.text, output.summary, nestedOutput.summary, evidence.text, evidence.logTail, evidence.summary)`; the matched key is recorded by caller convention. `executor` payload stays on the response so callers can read `dispatch.exitCode` / `dispatch.message` / `dispatch.stdout` even when text is empty.
The `evidence.*` and `*summary` lookups exist so a dispatcher that already includes host `logTail` / `buildSummary` becomes visible without a separate `bootsharp` re-run on the Code Agent side. The `summary` lookups also keep error messages (`dispatch.message`) in the response even when `dispatchStatus` is not `completed`; this is the reason `body.error.message` always has something to show for failed host dispatches.
### Cloud-api vs host-side boundary
`/root/hwlab-v02/skills/device-pod-cli/assets/device-host-cli.mjs` is the v0.2-shipped copy of the host-side CLI. The actual hardware host runs a separate `F:\Work\ConStart\tools\device-host-cli.mjs` that is **not** a deployment of the v0.2 repo; it is a D601 ops-side copy that must be synced manually when the v0.2 repo changes host-side behavior. The two-step contract is:
- v0.2 cloud-api / executor changes are valid once `PipelineRun Succeeded` + `git mirror flush` complete; runtime revision is `commit.id` from `/health/live` and source commit can be forced to match via the next `trigger-current`.
- v0.2 host-side `device-host-cli.mjs` changes are NOT visible until someone replaces `F:\Work\ConStart\tools\device-host-cli.mjs` on the D601 Windows host; cloud-api `body.text` will faithfully surface the "unsupported command" JSON error from the stale host binary, which proves the cloud-api propagation chain works but the host side is stale.
A live `workspace.evidence` / `debug.evidence` / `download evidence` selector that returns the host `logTail` end-to-end therefore requires both (a) the v0.2 PR merged and rolled, and (b) the D601 host binary replaced; missing either half is a known gap tracked in #779.
### v0.2 device-pod closeout checks
Device-pod fixes still follow `$dad-dev` and the service/runtime side of the `## v0.2 Source Workflow` route above. The device-pod-specific closeout is the three-layer runtime matrix below; keep these checks because they prove the cloud-api -> executor -> D601 host chain, while generic PR/CI/CD and worktree mechanics stay in `$dad-dev`.
```bash
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'cd tools && bun test device-pod-cli.test.ts'
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'cd cmd/hwlab-device-pod && bun test main.test.ts'
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'cd internal/cloud && bun test access-control.test.ts'
trans G14:/root/hwlab-v02/.worktree/<task> sh -- 'node --check skills/device-pod-cli/assets/device-host-cli.mjs'
```
Treat `access-control.test.ts` workbench failures as pre-existing on the v0.2 base unless the new test list explicitly covers them. After PR merge and `trigger-current --lane v02 --confirm`, the live `http://74.48.78.17:19667/` CLI 验收 must hit all three layers:
1. `body.output.text` non-empty for at least one happy-path intent (`workspace.ls` / `workspace.cat` are the cheapest ones to verify propagation without needing a real D601 build).
2. `workspace.evidence kind=build` / `kind=download` accepted by cloud-api, dispatched to executor, executor `blocker === null` and `job.reason === ""`.
3. `<mutating intent> action=status` accepted without `--reason` while the same intent with `action=start` is still rejected with `device_job_reason_required`.
There is no separate `device-pod` doc; this section is the single authoritative reference for the architecture, and the AGENTS.md index points to it.
旧命令只有在 owning YAML 精确声明 `legacy-manual` 时才可能存在,并且只能从 `$unidesk-cicd` 定义的 `legacy-cicd` / `legacy-ops` scoped help 发现。未知、混合或 PaC authority 必须 fail-closed;本退役文件不能作为启用 legacy 写入口的依据。
+4 -4
View File
@@ -7,11 +7,11 @@
- 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 CI/CD source authority 由 `config/hwlab-node-lanes.yaml#lanes.v03.targets.NC01.sourceAuthority` 决定。`trigger-current` / `status` / build 只消费受控 source snapshot,不得把 host worktree、本地 fetch/pull、可变 branch ref 或 Pipeline 直连 GitHub 当 authoritative source。固定 workspace 只服务源码修改、PR 准备和 repo 内验证;rollout closeout 以 source snapshot、PipelineRun、GitOps/Argo revision 和 runtime revision 收口。完整操作口径见 `$unidesk-cicd`
- 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 补齐
- 进入任何 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 受控入口检查并同步 NC01 workspace`bun scripts/cli.ts hwlab nodes control-plane source-workspace sync --node NC01 --lane v03 --confirm`,再用 `source-workspace status` 读取 clean、branch、remote、HEAD 和依赖状态。`source-workspace status` 必须显示声明 remote/base 已对齐;如果返回 `source-workspace-not-ready``remoteUpToDate=false`、ahead/behind/diverged 或 HEAD 与声明 remote/base 不一致,先修复固定 workspace,再创建 `.worktree/<task>`、提交测试 PR 或给验收写结论。运行面 `status` healthy、PaC snapshot 对齐和 GitOps/Argo healthy 只能证明 runtime source authority,不代表 host 固定 workspace 已可用于源码工作。
- 每次开始源码修改、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。
@@ -202,7 +202,7 @@ HWLAB 当前真实 runtime 固定为 NC01 `v0.3`。只读观察使用 YAML 解
trans NC01:k3s kubectl -n hwlab-v03 get deploy,svc,pod -o wide
```
HWLAB node/lane control-plane 的 PipelineRun 失败定位优先使用 UniDesk 受控状态入口,而不是先手工拼 TaskRun/Pod 查询:`bun scripts/cli.ts hwlab nodes control-plane status --node <node> --lane <lane> --pipeline-run <name>``trigger-current --wait` 返回对象,以及异步 `bun scripts/cli.ts job status <jobId> --tail-bytes 12000` 都应直接暴露失败 TaskRunPod、container、失败 step、termination reason 和 bounded `trans <node>:k3s logs --namespace hwlab-ci --pod <pod> --container <container> --tail 200` 查看命令。默认状态输出只给诊断命令和受控摘要,不打印完整 pod 日志、Secret、完整 DSN、API key 或其他可复制凭据。遇到 service build 的 `step-publish` 失败时,下一步先跑 `bun scripts/cli.ts platform-infra sub2api status --target <node>``bun scripts/cli.ts platform-infra sub2api validate --target <node>`,区分 Sub2API/proxy 整体故障和单上游 transient,再选择受控 rerun 或修复 artifact-publish/envRecipe 的有限 retry。
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 或其他可复制凭据。
`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` 为准。
@@ -236,7 +236,7 @@ HWLAB v0.3 订阅 AgentRun Kafka event 的权威输入是 `agentrun.event.v1`
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 等诊断字段缺失,先用 `bun scripts/cli.ts agentrun control-plane status --node NC01 --lane nc01-v02``bun scripts/cli.ts cicd status --node NC01` 核对 AgentRun manager source/runtime 是否包含对应修复,再通过受控 `trigger-current``refresh`PaC closeout 对齐运行面。不要通过重建 HWLAB 容器、修改 operator 本机 `config.toml` 或重复下发 Secret 掩盖 AgentRun manager 版本漂移。
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、refreshPaC 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 和原入口验收结果。
+1 -1
View File
@@ -89,7 +89,7 @@ OA Event Flow 的高频 trace 统计不得把每个 `trace-stats-updated` 投影
branch-follower 的 gate 与 TaskRun drill-down 默认输出必须能直接作为 target-side 性能证据使用,不能稳定触发 stdout dump。CI gate 应返回有界的 PipelineRun 总耗时、慢 TaskRun 对象名、阶段 timeline 和性能摘要;成功 TaskRun drill-down 默认只返回容器状态、日志字节数和 `node-cicd-timing` 摘要,不把成功日志正文塞进 JSON。失败、等待或显式 full/raw drill-down 才展开日志 tail。
`web-probe sentinel control-plane trigger-current --confirm --wait` 的成功路径必须默认输出 compact closeout summary至少包含 node/lane、source commit、PipelineRun 名称与状态、GitOps revision、Argo sync/health、runtime ready、public route readiness 和 PaC/status/history drill-down;成功时不得稳定触发全局 stdout guard。阻塞或失败路径仍应展开 status diagnosis、recovery next 和必要 drill-down,不得为了压缩输出隐藏失败原因
Web sentinel 的 PaC migrated lane 只能通过只读 status/history 展示自动交付证据。默认 compact 摘要至少包含 node/lane、source commit、外层 PaC PipelineRun 名称与状态、GitOps revision、Argo sync/health、runtime ready、public route readiness,以及 Gitea delivery 与 PaC history drill-down。内层 deterministic publish 只能标记为执行观察,不能代替外层 PaC event 驱动 latest、status 或 closeout。阻塞或失败时必须展示阶段、原因和只读 drill-down,并指向 owning YAML/controller/源码修复;禁止输出 `trigger-current`、sync、flush、人工 PipelineRun 或 recovery Next 来推进当前交付
全局 stdout guard 不能只返回 dump 元数据。对已知高频长输出命令,bounded wrapper 的 `summary` 必须保留可直接 closeout 的命令特定字段,同时把完整 payload 留在 dump/raw drill-down 中。例如 `debug dispatch ... provider.upgrade` 超阈值时应保留 dispatch task、wait task、plan host root、当前/目标 gateway 版本、scheduler 结果和最终 promoted container 的 `version``restartPolicy``pidMode``heartbeatTimestamp``provider triage --full` 超阈值时应保留 `decision``scope``retryable`、failed/degraded/healthy scopes、signal counts、recommended cross-checks 和问题信号预览。新增会稳定超阈值的诊断命令时,优先补命令特定 compact summary,而不是扩大全局 stdout 阈值。
+31 -27
View File
@@ -1,35 +1,39 @@
# Platform Infra
# Platform Infra 平台基础设施
`platform-infra` is the shared UniDesk-operated platform service area. Runtime placement is service-specific and YAML-selected: some services run in a k3s `platform-infra` namespace, while host-Docker services such as the current PK01 Sub2API target still belong to the same platform-infra control surface. For Sub2API, the active target is selected by `config/platform-infra/sub2api.yaml` and may be a host-Docker or k3s target; G14 remains a predeployed standby target scaled to zero. Other platform services may still declare G14 or another node as their active runtime in their own YAML. It is separate from HWLAB runtime lanes, AgentRun lanes, D601 user services, and legacy `devops-infra` control-plane helpers. New shared infra should land here first; old `devops-infra` resources migrate gradually only when a concrete owner and validation path exists.
`platform-infra` 是 UniDesk 统一运维的平台服务域。具体运行位置由各服务 owning YAML 选择,可以是 k3s `platform-infra` namespace,也可以是 YAML 明确声明的 host-Docker target。它与 HWLAB laneAgentRun laneD601 用户服务和旧 `devops-infra` 控制面 helper 分离。新增共享基础设施优先进入本域;旧资源只有在具备明确 owner 与原入口验收路径时才迁移。
## Source Of Truth
## 配置真相
- UniDesk-owned platform configuration must be YAML-first. `config/platform-infra/*.yaml` is the durable source for images, versions, endpoints, FRP exposure, account profile selection, and local consumer configuration.
- Runtime Secrets and local `~/.codex/config.toml*` / `auth.json*` files are inputs or generated local state, not committed truth. CLI output may show Secret object/key names, source paths, byte counts, presence and fingerprints. Short previews are allowed only for non-secret configuration; Secret and credential values are never previewed or printed.
- Code that reads platform YAML must validate object shape, field types, required fields, Kubernetes names, image strings, and ports before mutating G14 k3s or local consumer files.
- Do not hide image versions, namespace names, endpoint URLs, FRP ports, or profile lists in Python/TOML/JSON helper constants when they are UniDesk-owned choices. External tools may still require their own TOML/JSON/env file formats at the edge.
- Fresh node outbound bootstrap uses the zero-dependency host proxy boundary defined in `docs/reference/yaml-first-ops.md`. Platform-infra egress proxy settings may provide the benchmark-validated proxy source and workload consumer settings, but a new node must not depend on Docker, k3s, containerd or package-manager network access to install the initial host proxy client.
- UniDesk 平台配置必须 YAML-first`config/platform-infra/*.yaml` 持久拥有 imageversionendpointFRP 暴露、账号 profile 与 consumer 配置。
- Runtime Secret`~/.codex/config.toml*` `auth.json*` 只是输入或生成态,不是提交真相。CLI 只可显示对象/key、sourceRef、presence、长度和 fingerprint,不得预览或打印凭据值。
- 读取平台 YAML 的代码必须在写运行面或本地 consumer 文件前校验对象形状、字段类型、必填字段、Kubernetes 名称、image 和 port。
- UniDesk 拥有的 image versionnamespaceendpointFRP port profile 列表不得隐藏在 helper 常量里;外部工具要求的 TOMLJSONenv 仅是边缘渲染格式。
- 新节点出网 bootstrap 遵循 `docs/reference/yaml-first-ops.md` 的零依赖 host proxy 边界,不得依赖尚未安装的 Dockerk3scontainerd 或包管理器网络能力。
## Gitea And Pipelines-as-Code Boundary
## Gitea Pipelines-as-Code 边界
- Gitea mirror and Pipelines-as-Code are platform-infra CI source/trigger services operated by UniDesk. Their durable configuration lives in `config/platform-infra/gitea.yaml` and `config/platform-infra/pipelines-as-code.yaml`; do not hide repo URLs, mirror repo names, webhook settings, public exposure, FRP/Caddy ports, token sourceRefs or PaC Repository params in helper constants.
- The canonical Gitea entrypoints are `bun scripts/cli.ts platform-infra gitea plan|apply|status|validate|mirror --target <node>`, `bun scripts/cli.ts platform-infra gitea mirror plan|bootstrap|sync|status --target <node>`, and `bun scripts/cli.ts platform-infra gitea mirror webhook apply|status|test --target <node>`. Mirror bootstrap/sync must repair declared repo/org visibility such as `publicRead: true`; create-time defaults alone are not enough for long-lived repos.
- Gitea mirror webhook sync is GitHub -> Gitea only. GitHub remains the upstream write authority; the webhook bridge may update the YAML-declared Gitea branch and immutable snapshot refs, but it must not write Gitea changes back to GitHub or add a second polling path.
- The Gitea public Web UI and GitHub webhook endpoint may share the YAML-declared HTTPS hostname only through explicit Caddy path routing. Each target node that needs GitHub -> Gitea sync must have its own YAML-declared webhook path and FRP proxy/remote port; multiple nodes must not reuse one GitHub hook URL or one bridge. Ordinary Web UI traffic continues to route to the Gitea Web UI proxy. Internal k8s consumers still use the ClusterIP service URL, not the public hostname.
- Applying Gitea public exposure or webhook sync must roll the affected connector workloads after Secret or ConfigMap changes. A successful Secret apply is not sufficient evidence that `frpc` loaded a new proxy; closeout should use `platform-infra gitea apply` output plus `platform-infra gitea mirror webhook status|test` rather than assuming unchanged connector Pods picked up mounted config.
- Target webhook FRP remote ports must be checked against the live PK01 FRP listener set as well as YAML declarations. `frpc` log `port already used` means the GitHub delivery may hit an unrelated service and return 404; a port outside the PK01 published listener set can return 502 from Caddy. Fix the YAML port, re-apply with rollout wait, then use a fresh GitHub PR merge as the trigger evidence.
- `platform-infra gitea mirror webhook status --target <node>` is the default webhook closeout surface: it must show hook readiness, GitHub head, Gitea branch/snapshot, latest delivery status and bridge log event so a node-specific webhook problem can be diagnosed without reading source code or raw logs.
- The canonical PaC entrypoints are `bun scripts/cli.ts platform-infra pipelines-as-code plan|apply|status|history|webhook-test --target <node>`. PaC status is the operator-facing closeout surface for migrated CI lanes and must expose webhook count, latest PipelineRun/TaskRun duration, image status, env identity, digest, GitOps commit, Argo revision and runtime provenance without requiring raw `kubectl`, `tkn` or Gitea UI inspection.
- PaC Repository CR `spec.url` is the URL matcher for incoming Gitea webhook payloads, so `config/platform-infra/pipelines-as-code.yaml#repositories[].url` must use the public Gitea repository URL emitted by Gitea webhooks. Internal ClusterIP/service URLs belong in `cloneUrl` and `params.git_read_url`; putting an internal URL in `spec.url` makes PaC log `cannot find a repository match` and creates no PipelineRun even though the Gitea mirror is current.
- PaC Repository CR params must use the node-specific Gitea snapshot prefix declared for that consumer, such as `unidesk-master-nc01` for NC01 sentinel. A generic or other-node prefix can make the repository look healthy while fresh pushes cannot close out against the target node source.
- PaC history is the trigger/timing audit surface for Gitea/PaC-managed lanes. It must query Gitea Repository CR and Tekton PipelineRun/TaskRun live objects on the target node, aggregate there, return Beijing-time display by YAML timezone, expose a detail id for drill-down, and report read errors explicitly; a large namespace or unreadable target object must never be rendered as a successful empty table.
- PaC 迁移 consumer 的目标 source branch GitHub PR merge 是唯一正式交付触发事件。合并后必须由 GitHub webhook、Gitea controlled mirror 与 immutable snapshot ref、Gitea webhook、PaC、Tekton、GitOps/Argo 自动推进到运行面;主代理、子代理和操作者都不得执行 apply、closeout、trigger、sync、flush、人工 PipelineRun、直接 Gitea push、本地 Git mirror 修改或其他补跑来完成本次交付。`webhook-test` 只是连通性诊断,不得代替真实 PR merge 事件
- 自动链不通时必须修复 owning YAML、controller 或源码中的自动链缺陷,并用修复 PR 合并后产生的新自动事件验证;禁止用人工操作制造对齐状态或掩盖故障。`status``history``events``logs` 与单步 debug 入口只在显式调查时按需读取,不是交付前置;`closeout` 仅保留只读兼容能力,不得成为主/子代理的 PR 合并后步骤。任何诊断入口都不得隐式推进交付
- `config/platform-infra/pipelines-as-code.yaml` may declare multiple repositories and consumers. JD01 and NC01 both declare AgentRun, Web sentinel and HWLAB consumers; use `cicd status --node <NODE>` for node-level status, `history --target <NODE>` for all-consumer audit, and `status --target <NODE> --consumer <id>` for consumer-scoped closeout. Consumer-scoped status must not mix PipelineRuns or env reuse evidence across repositories.
- Shared upstream repositories must isolate target-specific `.tekton` files with Repository CR params, currently `node`, and `pipelinesascode.tekton.dev/on-cel-expression`. A NC01 webhook delivery must not create JD01 PipelineRuns in the NC01 cluster, and history drill-down must not attribute the same PipelineRun to multiple consumers.
- Public Gitea UI may use the YAML-declared HTTPS hostname, but k8s-internal consumers must use the ClusterIP service URL from YAML. Internal CI/Argo/runtime reads must not loop through public DNS/Caddy/FRP, and migrated lanes must not fall back to legacy git-mirror read URLs when the commit exists only in Gitea.
- A PaC-migrated lane must keep a single trigger path: Gitea webhook -> Pipelines-as-Code -> Tekton -> GitOps/Argo -> k8s runtime. Do not add Gitea Actions, `act_runner`, branch-follower or custom script fallback unless a later issue explicitly changes the architecture. `config/cicd-gitea-actions-poc.yaml` is archived POC context only; snapshot refs that still contain `gitea-actions` are historical names retained for compatibility, not evidence that Gitea Actions is active.
- k8s runtime remains Docker-free from the point it pulls already built images. CI build steps may use YAML-declared native build tooling, but Docker socket/daemon access must not become part of the runtime plane.
- Gitea mirror Pipelines-as-Code 是 UniDesk 运维的 CI source/trigger 服务。`config/platform-infra/gitea.yaml` `config/platform-infra/pipelines-as-code.yaml` 分别拥有 mirror/webhook 与 PaC Repository/consumer 配置;repo URL、snapshot、webhook、public exposureFRP/Caddy porttoken sourceRef PaC params 不得隐藏在代码特例中。
- Delivery authority resolver 必须组合上述两份 YAML,并按 consumer id、node、lane、upstream repository、branch 和 Gitea repository 精确关联。不得通过 URL 片段、repo 名称前缀或仓库专属 `if` 推断迁移状态。零匹配、多匹配和配置错误都必须返回 `unknown``mutation=false` 并停止生成或执行写操作。
- PaC consumer 的唯一正式触发是目标 source branch 的 GitHub PR merge。完整自动链固定为:GitHub PR merge -> GitHub webhook -> Gitea controlled mirror -> immutable snapshot -> Gitea webhook -> PaC/Pipelines-as-Code -> Tekton -> GitOps/Argo -> runtime 运行面。
- PR 合并后,主代理、子代理和操作者都不得用 `apply``closeout``trigger-current``refresh``sync``flush``webhook-test`、人工 PipelineRun、直接 Gitea push、本地 mirror 写入或其他补跑完成当前交付。自动链不通时必须修 owning YAML、controller 或源码,并只用修复 PR 合并产生的新正常自动事件验收。
- Migrated consumer 的默认 help、status、`Next``REPAIR` 只能给 `status``history``events``logs`、只读单步下钻以及本节稳定引用。CLI 必须省略 mutation command,而不是只给命令加警告文字。`unknown` authority 同样 fail-closed。
- `trigger-current``refresh` 和 mirror `sync|flush` 只允许在 owning YAML 明确解析为 `legacy-manual` 后执行,并且只在显式 `legacy-cicd``legacy-ops` scoped help 中可发现。平台 bootstrap、Secret 与配置维护属于独立职责,只在 `platform-bootstrap``platform-maintenance` scoped help 中展示,不能作为 source delivery recovery。
- `closeout` 仅保留只读历史/诊断兼容入口,并且只能从 `compatibility-diagnostics` scoped help 发现。会 POST hook test 的 `webhook-test` mutation 入口已经删除;连通性只能通过真正只读的 status、GET 与 readiness 观察,禁止制造伪 push。
- 默认 Gitea webhook 观察入口是 `bun scripts/cli.ts platform-infra gitea mirror webhook status --target <node>`。它应显示 hook readiness、GitHub head、Gitea branch/snapshot、最近 delivery 与 bridge event;状态陈旧时只能给只读下钻和“修复自动链”指引,不得输出人工 `REPAIR`
- GitHub webhook receiver 只有在验签 delivery 通过 fsync 与 atomic rename 写入 YAML 声明的 PVC durable inbox 后才能返回 HTTP `202 Accepted`。状态必须分为 `accepted``processing``committed``failed``202 Accepted` 只证明持久接收,不证明 refs 已提交。只有同一 deliveryId 的 exact-after immutable snapshot 与 authority branch 经 atomic push 后重新读取 refs 证明一致,才能进入 `committed`
- 同 deliveryId 与同 payload 必须幂等,异 payload 返回 `409`。PVC 不可用或容量满返回 `503` 且 readiness=false。inbox worker 按 YAML 有界 backoff 自动重试并在重启后恢复 `accepted` / `processing`;Caddy 只对尚未持久化的请求做小于 10 秒的 body-safe 有界重试。
- Durable inbox 只是 webhook delivery 的持久接收与重试 journal,不是业务 source/ref 的第二份真相,也不得被 PaC、Tekton、GitOps、Argo 或 runtime 当作源码/read model。最终 source authority 仍只有 Gitea authority branch 与 immutable snapshot;禁止新增 polling/read-model fallback 或第二 source authority。
- 默认 PaC 入口是 `bun scripts/cli.ts platform-infra pipelines-as-code status|history --target <node>``status` 应显示 webhook、PipelineRun/TaskRun duration、image status、env identity、digest、GitOps commit、Argo revision 和 runtime provenance`history` 必须读取 target node 上的实时对象、显式报告 read error,并支持 consumer 与 PipelineRun id 下钻。`history --id` 必须通过运行面 provenance 与 PipelineRun prefix 唯一解析实际 consumer;零匹配、多匹配或显式 consumer 不一致都 fail-closed,不能回退到默认 consumer 或默认 node
- Sentinel 内部 publish capability 由 `config/platform-infra/pipelines-as-code.yaml#capabilities.sentinelInternalPublish` 单一拥有,默认 `enabled=false`。Pod env、ownerReferences、ServiceAccount、label 与 annotation 不能证明 creator#1769 的 admission-owned provenance 与最小权限边界落地前不得启用。`.tekton` 继续走既有自动 `publish-current`,不能退化成人工 publish
- PaC 观察必须把 PipelineRun 分类为外层 `outer-pac-event`、内层 `inner-deterministic-publish``unknown`。只有唯一外层 PaC push event 能驱动 latest、status、debug-step 与兼容 closeout;内层只能展示为未绑定执行观察,缺少 admission-owned 父子证据时标记 `unproven`,禁止按名称前缀推断关系。
- `config/platform-infra/pipelines-as-code.yaml` 可以声明多个 repository 和 consumer。`cicd status --node <NODE>` 用于 node 汇总,`history --target <NODE>` 用于全 consumer 审计,`status --target <NODE> --consumer <id>` 用于 consumer 只读状态;不得跨 repository 混合 PipelineRun 或 env reuse 证据。
- PaC Repository CR `spec.url` 必须匹配 Gitea webhook payload 的 public repository URLClusterIP/service URL 只属于 `cloneUrl``params.git_read_url`。Repository params 必须使用该 consumer 的 node-specific snapshot prefix。
- GitHub 始终是 upstream write authority。Bridge 只允许 GitHub -> Gitea 更新 YAML 声明的 branch 与 immutable snapshot,不得反向写 GitHub,也不得增加 polling 或第二 trigger path。
- 每个 target 必须使用 YAML 独立声明的 webhook path、bridge 与 FRP port;多个节点不得复用 hook URL。Public Gitea Web UI 与 webhook 只能通过明确 Caddy path routing 共享 hostnamek8s 内部 consumer 必须使用 ClusterIP URL。
- Secret 或 ConfigMap 改变后,Gitea connector 的显式平台配置维护必须等待 workload rollout。成功写 Secret 不能证明 `frpc` 已加载新配置;端到端交付证据仍必须来自之后的新正常 PR merge。
- Shared upstream repository 必须通过 Repository CR params 与 `pipelinesascode.tekton.dev/on-cel-expression` 隔离 target-specific `.tekton`。NC01 delivery 不得在 JD01 cluster 创建 PipelineRunhistory 也不得把同一 PipelineRun 归属给多个 consumer。退役 branch-follower 的 unknown 或多 node scope 只返回 follower 自身的只读 status/events/logs 和稳定修复引用,禁止猜测 `JD01` 或其他默认 node 的 PaC status/history。
- PaC migrated lane 保持单 trigger path,禁止新增 Gitea Actions、`act_runner`、branch-follower 或脚本 fallback。`config/cicd-gitea-actions-poc.yaml` 只是归档 POC;历史 snapshot 名称不代表该路径仍在运行。
- k8s runtime 从拉取已构建 image 起保持 Docker-free。CI 可以使用 YAML 声明的 build tooling,但 runtime 不得依赖 Docker socket 或 daemon。
## Secret Distribution Boundary
+3 -3
View File
@@ -87,11 +87,11 @@ This split must be implemented explicitly in `deploy.json`, deploy planning, dep
During a v1 stabilization window, the future split-lane implementation is deliberately deferred. The current `environments.dev` lane is reserved for v1 validation until the v1 release criteria are met, then the explicit split-lane work can proceed as a separate infrastructure change.
## CI/CD Infrastructure Repair
## CI/CD 基础设施引导与修复
CI/CD infrastructure bootstrap, repair and upgrade must not depend on a new CI job that tests the CI/CD bootstrap path itself. When the CI/CD infrastructure is the broken component, use manual validation: runtime health, logs, source commit metadata, capability endpoints, bounded smoke commands and operator review.
CI/CD 基础设施的首次引导、修复和升级都必须先修改 Git-backed owning YAML、controller 或源码,并通过正常 PR 合并产生的自动 webhook、PaC、Tekton、GitOps/Argo 事件完成交付与验收。自动链自身故障时,可以用只读 health、status、logssource commit、capability 和有界 GET/smoke 收集证据,但这些观察不能替代正常自动事件,也不能被包装成人工交付。
Manual CI/CD infrastructure fixes may be promoted directly to production when production infrastructure is the intended target. The durable state still has to return to Git-backed desired state: any code, manifest, config or policy change that should survive must be committed and pushed after the repair.
禁止通过运行面 patch、人工 PipelineRun、直接 Gitea push、人工 mirror sync、伪 push、hook test 或隐藏脚本补齐当前 release。紧急情况下只允许经明确授权执行有界、可逆、可验证的临时缓解;缓解不能晋升为 desired state 或交付成功,随后仍必须在 Git-backed 来源修复根因,并用修复 PR 合并产生的新自动事件验收。
## Feature Flags