---
name: unidesk-cicd
description: UniDesk CI/CD 控制面 — `hwlab g14` 和 `agentrun` 子命令，覆盖 PR 监控自动合并、Tekton/Argo 控制面、git-mirror、Secret、observability、CI tools image、PipelineRun 清理、AgentRun v0.1 部署和 AgentRun YAML-only lane 部署。用户提到 CI/CD、deploy、rollout、PipelineRun、trigger、git-mirror、control-plane、k3s 部署、agentrun 部署、hwlab g14、monitor-prs、trigger-current 时使用。任何需要把代码变更推送部署到 G14 k3s 的操作都必须走本 skill。
---

# UniDesk HWLAB G14 CI/CD CLI

HWLAB G14 的 PR → CI → CD 控制面和运维入口，统一通过 `bun scripts/cli.ts hwlab g14 ...` 管理。

**固定入口前缀**: `cd /root/unidesk && bun scripts/cli.ts hwlab g14 ...`

---

## PR 监控与自动合并

### G14 主线

```bash
bun scripts/cli.ts hwlab g14 monitor-prs \
  [--lane g14|v02] [--once] [--dry-run] \
  [--interval-seconds N] [--max-cycles N] [--timeout-seconds N]
```

后台 worker 监控 `pikasTech/HWLAB` 的 open PR → preflight → 自动合并 → 观察 CI/CD 直到 DEV `Synced/Healthy`。成功 rollout 后自动追加指挥简报。状态指针按用途分离（`latest-monitor-job.json` / `latest-once-job.json` 等）。

### v0.2 lane

```bash
bun scripts/cli.ts hwlab g14 monitor-prs --lane v02 [--once] [--dry-run]
```

只监控 base=`v0.2` 的 PR。CD 采用 latest-only：旧 PipelineRun 不取消不等待，stale commit 以 superseded/no-op 收口。合并后在原 PR 下追加语义化状态评论（含起止时间、source commit、PipelineRun、targetValidation、git mirror 状态）。

### v0.3 lane

```bash
bun scripts/cli.ts hwlab g14 monitor-prs --lane v03 [--once] [--dry-run]
```

只监控 base=`v0.3` 的 PR。ready PR 经 UniDesk `gh pr merge` 合并后触发 runtime lane CD，检查 PipelineRun、Argo、`hwlab-v03` runtime public probes 和 Git mirror flush，并对失败 check、冲突、CD failure/timeout 创建或更新 failure issue。public probe 必须使用 `config/hwlab-node-lanes.yaml` 选中 node/lane 的 formal public URL；D601 `v0.3` 当前是 `https://hwlab.pikapython.com`，裸 IP、FRP 端口和 legacy `20666/20667` 只作为边缘诊断证据，不能作为用户入口验收口径。

---

## 控制面（Tekton/Argo）

### 状态查询

```bash
# 最新 head
bun scripts/cli.ts hwlab g14 control-plane status --lane v02

# 定点 PipelineRun
bun scripts/cli.ts hwlab g14 control-plane status \
  --lane v02 --pipeline-run hwlab-v02-ci-poll-<short-sha>

# 定点 source commit
bun scripts/cli.ts hwlab g14 control-plane status \
  --lane v02 --source-commit <full-sha>
```

定点 status 输出 `targetValidation.state=passed|superseded`，只检查指定 target 的证据。

### 手动触发

```bash
bun scripts/cli.ts hwlab g14 control-plane trigger-current \
  --lane v02|v03 [--dry-run|--confirm]
```

从 `/root/hwlab-v02-cicd.git` 解析当前 `origin/v0.2` full SHA，创建 commit-pinned PipelineRun。confirmed trigger 创建异步 job 并立即返回 `job.id`。

### 应用 RBAC/Pipeline/Argo

```bash
bun scripts/cli.ts hwlab g14 control-plane apply --lane v02 [--dry-run|--confirm]
```

server-side apply v02 的 Tekton RBAC、Pipeline 和 Argo Application。

### D601 节点本地 infra bootstrap

```bash
bun scripts/cli.ts hwlab nodes control-plane infra plan --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra apply --node D601 --lane v03 --dry-run
bun scripts/cli.ts hwlab nodes control-plane infra apply --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra tools-image status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra tools-image build --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image preload --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image logs --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra argo status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra argo apply --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra ci-build-benchmark --node D601 --lane v03 --profile no-mirror-full --confirm
bun scripts/cli.ts hwlab nodes control-plane infra ci-build-benchmark status --node D601 --lane v03 --profile no-mirror-full
bun scripts/cli.ts hwlab nodes control-plane infra ci-build-benchmark logs --node D601 --lane v03 --profile no-mirror-full
bun scripts/cli.ts hwlab nodes control-plane status --node D601 --lane v03 [--pipeline-run <name>|--source-commit <sha>] [--full|--raw]
bun scripts/cli.ts hwlab nodes control-plane trigger-current --node D601 --lane v03 --confirm --wait
bun scripts/cli.ts hwlab nodes control-plane sync --node D601 --lane v03 --confirm
```

从 `config/hwlab-node-control-plane.yaml` 渲染 D601 HWLAB v03 的节点本地 CI/CD、git-mirror、Tekton、runtime dependency image preload 和 Argo 前置对象。confirmed apply 只做 control-plane bootstrap，不触发 runtime rollout，不创建 PK01 DB，也不修改 Caddy/FRP。node-local registry 镜像只能作为 tools image 或 runtime dependency 的输出 artifact；输入 base/pull image 必须是 YAML 中声明的公开 registry 来源，缺失 output image 时通过 `status.next.blockers` 或 `runtime-image status` 暴露。D601 Argo CD 安装也必须由 YAML 声明：官方 manifest URL、版本、镜像 rewrite/preload、CRD、期望 workload 和 AppProject/Application 都来自 YAML，不能使用手工 kubectl/argo CLI 作为正式安装路径。

`ci-build-benchmark` 是 HWLAB v0.3 k3s CI/CD 全量无缓存构建出网测速入口。profile、独立 catalog path 模板、cache policy、必须输出的 timing 阶段和失败族来自 `config/hwlab-node-control-plane.yaml`；实际 service set、git mirror URL、Pipeline、ServiceAccount、registry prefix 和 base image 仍以 `config/hwlab-node-lanes.yaml` 为准。`forbidBuildkitCache=true` 时会向 PipelineRun 传 `build-cache-mode=disabled`。confirmed benchmark 只创建唯一 PipelineRun 并返回 status/logs 轮询命令；通过证据必须包含每个 `build-<service>` TaskRun，PipelineRun 成功但缺少 build task 要按 `cache-hit-forbidden` 处理。

`hwlab nodes control-plane status` 默认返回 compact commander summary，只保留 source commit、PipelineRun、Argo、runtime readiness、public probe 和 next action；完整 expected YAML/render target、kubectl result tail、Secret/sourceRef 详情和 probe 原始结果只在 `--full` 或 `--raw` 下展开。

`hwlab nodes control-plane sync --confirm` 是 Argo runtime 收敛修复入口：会先按 YAML `runtimeStore.postgres.mode=local-k3s` 同步本地 postgres bootstrap Secret，再终止卡住的 running Argo operation、删除失败 hook Job，并在 StatefulSet template 已更新但旧 controller-revision pod 因 `ImagePullBackOff` / `ErrImagePull` / `CrashLoopBackOff` 卡住时受控删除该旧 pod，让 StatefulSet 按最新 revision 重建。不要手工裸删 pod；需要解除这类死锁时走该入口。

`hwlab nodes control-plane trigger-current --node <node> --lane <lane> --confirm --wait` 是 node/lane CI/CD 一键入口：按 YAML 解析 source head，执行 git-mirror pre-sync/pre-flush，刷新 control-plane，创建或复用 commit-pinned PipelineRun，等待 PipelineRun 终态，并在终态成功后执行 post-flush。默认输出必须是低噪声 CICD 表格摘要；完整 JSON 只能通过 `--full` 或 `--raw` 展开。120 秒是严重超时阈值：PipelineRun wait 或 `trigger-current` total elapsed 超过 120 秒时，即使最终 status=ok/completed，也必须输出并在 closeout 中记录 `node-runtime-trigger-over-120s` warning、total elapsed、pipeline wait、git mirror status，并从 env-reuse 和 git-mirror/control-plane path 着手排查；未到终态时 CLI 返回 `pending` warning，不继续长时间阻塞，也不把仍在运行误报为构建失败。小范围 PR 触发 120s 时必须看 `plan-artifacts` 的 `affectedServices/buildServices/reusedServices`：如果 source diff 很小却出现所有 envreuse 服务都在 `buildServices` 且 `reusedServices=[]`，优先怀疑 current GitOps artifact catalog 没有 hydrate 到 source plan 阶段，而不是继续盲目重跑 PipelineRun。

### G14 v0.3 runtime base image

```bash
bun scripts/cli.ts hwlab nodes control-plane runtime-image status --node G14 --lane v03
bun scripts/cli.ts hwlab nodes control-plane runtime-image preload --node G14 --lane v03 --confirm
```

G14 v0.3 的 Tekton/BuildKit base image 也走 `config/hwlab-node-lanes.yaml`：`baseImageSource` 是公开来源，`baseImage` 是 node-local registry 目标。缺失 base image 时先用 `runtime-image status` 判断 `registryTagPresent`，再用 `preload --confirm` seed；不要手工 `docker tag/push`。`trigger-current` 后若 PipelineRun 已越过 base image 阶段但卡在某个 service build task，按 TaskRun 单独提 issue/修复，不把它并回 base-image preload 问题。长期边界见 `docs/reference/g14.md`。

D601/v03 env-reuse service build task 失败时，先看 `build-<service>` TaskRun 的 `step-publish` 日志；Debian apt、npm、Go module 等外部依赖下载通过 lane YAML 注入 egress proxy 后可能出现 502、reset 或超时。先用 `platform-infra sub2api status|validate` 区分 proxy 整体故障和单个上游 transient；proxy 健康但单次下载 transient 时可以受控 `trigger-current --rerun`，重复失败应修 HWLAB `scripts/artifact-publish.mjs` / envRecipe 的有限 retry 后合并发布，不手工 patch pod 或裸删 PipelineRun。若 Pod 内 unset `HTTP_PROXY/HTTPS_PROXY/ALL_PROXY` 后外部 registry/DNS 不可达，说明该 lane 的外部依赖下载依赖 egress proxy；此时 npm/Bun retry 只能降噪，根因仍是 proxy upstream 或 catalog/plan 误触发的过量 build。凡是为证明 proxy 加速 CI/CD 而跑测速，必须同时采集 `platform-infra egress-proxy traffic --target <id>` 的 proxyserver 侧每客户端速率和窗口累计流量；只贴 PipelineRun 总耗时或 client-side benchmark 不能证明 workload 确实走了 proxy。

---

## Git Mirror

```bash
bun scripts/cli.ts hwlab g14 git-mirror status
bun scripts/cli.ts hwlab g14 git-mirror apply [--dry-run|--confirm]
bun scripts/cli.ts hwlab g14 git-mirror sync [--dry-run|--confirm]
bun scripts/cli.ts hwlab g14 git-mirror flush [--dry-run|--confirm]

# D601 node-local v0.3 lane
bun scripts/cli.ts hwlab nodes git-mirror status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes git-mirror sync --node D601 --lane v03 --confirm --wait
bun scripts/cli.ts hwlab nodes git-mirror flush --node D601 --lane v03 --confirm --wait
```

- `apply`: 渲染并 apply `devops-infra/git-mirror.yaml`
- `sync`: 把当前配置声明的 GitHub refs 拉入本地 mirror
- `flush`: 把本地 lane GitOps ref 快进推回 GitHub

PipelineRun `gitops-promote` 如果报 git mirror 控制面漂移、refs 不一致或 flush/publish 未完成，优先按当前 `devops-infra/git-mirror.yaml` 收敛：先 `git-mirror apply --confirm`，再 `git-mirror sync --confirm --wait`，然后用 `control-plane cleanup-runs --pipeline-run <failed-run> --confirm` 受控清理失败 PipelineRun 后重试。旧 branch/path allowlist gate 已删除，不要恢复旧 hook、直接 `kubectl delete`、手工 patch pod 内 hook 或绕过 `flush`。

手动 trigger closeout 不能只看 PipelineRun `Completed`。必须继续查 `control-plane status --pipeline-run <name>` 和 `git-mirror status`；node-scoped `trigger-current --confirm --wait` 会自动做必要的 mirror pre/post flush，但 closeout 仍要确认最终 `pendingFlush=false`、`githubInSync=true`。如果 lower-level 手工路径或旧 job 留下 `pendingFlush=true`，执行 `git-mirror flush --confirm --wait` 到 `githubInSync=true`。

node-scoped lane 可能在本次 PR 合并后又被后续 PR 推进。`control-plane status --pipeline-run <name>` 是定点观察某个 PipelineRun，但输出里的当前 `sourceHead` / `summary.sourceCommit` 可能已经是最新 branch tip，而不是该 PipelineRun 名称对应的 merge commit。closeout 必须同时记录 PR merge commit、PipelineRun 名称/状态、Argo sync revision、当前 branch tip，并用 `git merge-base --is-ancestor <merge-commit> HEAD` 或等价证据说明最新 tip 包含本次 PR；不要只凭当前 source head 判断本次 rollout。

`trigger-current --node D601 --lane v03 --confirm --wait` 可能先解析到一个 source commit，随后在 control-plane apply 的 `source-render` / `local-git-clone-worktree` 阶段因 `v0.3` 被并行 PR 推进而 clone 到更新 head，触发 `rev-parse HEAD == source_commit` 校验失败。这不是业务代码构建失败；先执行 `hwlab nodes git-mirror sync --node D601 --lane v03 --confirm --wait`，再快进固定 worktree，确认原 PR merge commit 是最新 `origin/v0.3` 的祖先，然后按最新 branch tip 重新运行 `trigger-current`。收口时同时记录原 PR merge commit、最新 source head 和 ancestor 证据。

D601 `v0.3` 固定 worktree 的 fetch remote 是 node-local git mirror。GitHub PR 合并后，如果 `D601:/home/ubuntu/workspace/hwlab-v03` 中 `git fetch origin v0.3` 仍看不到最新 merge commit，先执行 `hwlab nodes git-mirror sync --node D601 --lane v03 --confirm --wait`，再在固定 worktree `git fetch origin v0.3 && git pull --ff-only origin v0.3`。D601/v03 `git-mirror` 的 GitHub upstream 标准传输固定为 YAML 声明的 SSH：`githubTransport.mode=ssh`，脚本通过 `GIT_SSH` wrapper 访问 `ssh://git@ssh.github.com:443/...`；node-global HTTP proxy 只作为 SSH CONNECT tunnel，不是 GitHub HTTPS auth/token transport。若 CLI 输出 `transport=https`、`GITHUB_TOKEN`、`git-mirror-github-token` 或 HTTPS token sourceRef，按 control-plane drift/配置回归处理：先修 `config/hwlab-node-control-plane.yaml` 并执行 `hwlab nodes control-plane apply --node D601 --lane v03 --confirm`，不要改走 HTTPS、不要增加 fallback、不要用 host workspace repair。`sync/flush` 的 retry 只消费 SSH upstream transient，并在耗尽后输出 stopped/exhausted；`trigger-current --lane v03` 会为 PipelineRun 做 mirror pre-sync，但不替代固定 worktree 的 fetch hygiene。promotion 后若 node-local `git-mirror status` 显示 `pendingFlush=true`，执行 node-local flush 并等到 `pendingFlush=false`、`githubInSync=true`。

D601/node-scoped mirror status 的 `githubGitops` 来自本地 mirror cache 的 `refs/mirror-stage/...`；`status` 输出应通过 `refSources.githubFieldsAreMirrorStageCache=true` 显示这一点。`hwlab nodes git-mirror flush --node D601 --lane v03 --confirm --wait` 如果已经显示 `v0.3-gitops -> v0.3-gitops` 推送成功，但随后因 GitHub SSH `kex_exchange_identification` 或 fetch 确认失败导致命令非零退出，会标记 `partialSuccess=push-succeeded-fetch-failed`。当前 CLI 会自动做一次受控 sync/recheck；恢复后输出 `partialSuccessRecovered=true`、`postPushRecovery` 且整体 `ok=true`，未恢复时才把下一步指向 `hwlab nodes git-mirror sync --node D601 --lane v03 --confirm --wait`。不要连续盲目 flush；先刷新 mirror-stage，再用 status 确认 `localGitops=githubGitops`、`pendingFlush=false`、`githubInSync=true`。

---

## Secret 管理

```bash
# 查看
bun scripts/cli.ts hwlab g14 secret status --lane v02 \
  --name hwlab-v02-openfga|hwlab-v02-master-server-admin-api-key

# 确保
bun scripts/cli.ts hwlab g14 secret ensure --lane v02 \
  --name hwlab-v02-master-server-admin-api-key [--dry-run|--confirm]

# 删除废弃 Secret
bun scripts/cli.ts hwlab g14 secret delete --lane v02 \
  --name <obsolete-secret> [--dry-run|--confirm]
```

---

## 运行时迁移

```bash
bun scripts/cli.ts hwlab g14 control-plane runtime-migration \
  --lane v02 [--dry-run|--confirm]
```

通过 `deployment/hwlab-cloud-api` 容器内 migration CLI 执行。

---

## Observability

```bash
bun scripts/cli.ts hwlab g14 observability status|apply|query|targets|boundary|closeout \
  [--lane v02] [--promql <expr>] [--expect-count N] [--expect-value V] [--dry-run|--confirm]
```

管理 G14 Prometheus 基础设施和 HWLAB v0.2 监控 closeout。

---

## Platform Infra

```bash
bun scripts/cli.ts platform-infra sub2api plan|apply|status|validate
bun scripts/cli.ts platform-infra sub2api codex-pool plan|sync|validate|expose|configure-local
bun scripts/cli.ts platform-infra wechat-archive plan|apply|status|validate|pull
bun scripts/cli.ts platform-infra wechat-archive wcf-host-status|collector-plan|collector-apply|collector-status
```

- `platform-infra` 是 G14 k3s 上 UniDesk 运维的平台基础设施 namespace；新增平台服务优先进入该 namespace，旧 `devops-infra` 只作为渐进迁移来源。
- Sub2API 的日常部署、Codex pool、FRP 暴露、master `~/.codex` 配置、验收和排障统一使用 `$unidesk-sub2api`（UniDesk 仓库 `.agents/skills/unidesk-sub2api/SKILL.md`）。
- WeChat archive 是 platform-infra 的 YAML-first 工作流入口；D601 personal WeChat upstream 必须复用既有 D601 `platform-infra` namespace，`createNamespace=false`，只读 collector 的副本、镜像、WCF host、端口和版本 pin 都以 `config/platform-infra/wechat-archive.yaml` 为准。
- 如果 WeChatFerry 配套的 PC 微信版本被微信服务端拒绝登录，按上游兼容阻塞处理：把 collector 的 YAML 副本数调为 `0` 并通过 `collector-apply --confirm --wait` 同步，保留 Secret/ConfigMap/PVC 和 Windows 准备态；不要手工 `kubectl scale`、新建 namespace 或采用版本检查绕过工具作为长期路径。
- UniDesk 仓库 `docs/reference/platform-infra.md` 只保留开发边界、YAML-first 真相和探针口径，不重复日常操作手册。

---

## CI Tools Image

```bash
bun scripts/cli.ts hwlab g14 tools-image status
bun scripts/cli.ts hwlab g14 tools-image build \
  --name ci-node-tools --tag <tag> \
  [--dockerfile deploy/ci/hwlab-ci-node-tools.Dockerfile] [--dry-run|--confirm]
```

在 G14 host 构建并 push 到本地 registry。

---

## PipelineRun 清理

```bash
# 清理已完成 PipelineRun
bun scripts/cli.ts hwlab g14 control-plane cleanup-runs \
  --lane v02|g14|all [--min-age-minutes N] [--limit N] [--dry-run|--confirm]

# D601/G14 node-scoped runtime lane retention
bun scripts/cli.ts hwlab nodes control-plane cleanup-runs \
  --node D601 --lane v03 [--min-age-minutes N] [--limit N] [--dry-run|--confirm --wait]

# 补充清理 Released PV
bun scripts/cli.ts hwlab g14 control-plane cleanup-released-pvs \
  --lane all [--limit N] [--dry-run|--confirm]
```

---

## 手动补记 rollout

```bash
bun scripts/cli.ts hwlab g14 record-rollout --pr <number> --source-commit <sha>
```

手动补记 CI/CD 耗时、TaskRun 指标和语义化 changelog 到指挥简报。

---

## AgentRun 控制面

YAML-only lane 以 `config/agentrun.yaml` 为部署真相；node/lane、source workspace/branch、image build、GitOps branch/path、runtime namespace、Secret、外置数据库、manager env、git-mirror 和 edge 暴露都从 YAML 进入 CLI。AgentRun service repo 的 `deploy/deploy.json` 不能作为 UniDesk deployment truth；新 lane 不再维护该文件。

```bash
bun scripts/cli.ts agentrun control-plane plan --node D601 --lane v02
bun scripts/cli.ts agentrun control-plane apply --node D601 --lane v02 [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane secret-sync --node D601 --lane v02 [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane restart --node D601 --lane v02 [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane trigger-current --node D601 --lane v02 [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane cleanup-runners --node D601 --lane v02 [--force-active] [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane status --node D601 --lane v02 [--pipeline-run <name>|--source-commit <sha>] [--full|--raw]
```

- `plan`: 只读解析 YAML，输出控制面、source、image build、GitOps、runtime 和 Secret plan，不打印 Secret value
- `apply`: 按 YAML 渲染并 apply Tekton RBAC/Pipeline、Argo AppProject/Application 和 runtime namespace
- `secret-sync`: 按 YAML 的 Secret sourceRef/keyMapping 同步 runtime Secret 和外置 DB Secret，只输出 fingerprint
- `restart`: patch manager Deployment 的 restart annotation 并等待 rollout，用于 Secret export/DB 连接串变化后让 workload 读取新 Secret；不要手工删除 Pod
- `trigger-current`: 确保 source branch/workspace，删除新 lane source branch 的 `deploy/deploy.json`，构建并推送 YAML 声明的 image，渲染 GitOps/artifact catalog，触发 git-mirror sync 和 provenance PipelineRun；confirmed 运行可返回异步 job，必须用 `job status <jobId> --tail-bytes 12000` 看 `agentrun-yaml-lane-trigger` progress，再用 `status --pipeline-run <name>` 轮询收口；confirmed 收尾会尝试恢复 fixed source workspace 到 lane branch，恢复失败只作为 warning 披露
- `cleanup-runners`: 只清 YAML 选中 lane runtime namespace 中匹配 `deployment.runner.retention.selectors` 的 runner Job/Pod；runner 上限、最后活跃排序、active heartbeat 窗口、age-based cleanup 开关和 selector 都以 YAML 为准。`dry-run` 必须先看 manager facts、inactive candidates、selected 和 `activeRunRisk`；普通 `--confirm` 只删除 selected inactive runner，不替代 CI PipelineRun 清理。`--force-active` 只用于 operator 明确决定强杀所有匹配 runner pod/job 的资源恢复场景，必须先 dry-run 确认 `criteria.forceActive=true` 和 selection 范围；它会中断 active run/command/session，但仍优先于裸 `kubectl delete pod/job`。
- `status`: 默认返回 compact commander JSON，关键结论在 `.data.summary` 和 `.data.alignment`，完整 YAML target、原始 source/runtime/gitMirror payload 和成功 probe tail 只在 `--full|--raw` 展开

YAML-only lane 的长步骤必须由 CLI 拆成短提交和状态轮询：source bootstrap、image build、GitOps publish、git-mirror sync 和 PipelineRun 创建不得塞进一个顶层 `trans` 长连接。GitOps publish 必须使用隔离临时 clone/worktree，不能切换或污染 YAML 声明的固定 source workspace；如果历史失败发布留下 dirty/detached/GitOps branch 状态，只清理已知发布残留并恢复到 lane source branch 后再重试。后台步骤的 `status` 和 `ok` 要共同判定，`status=succeeded` 但 `ok=false` 是终态失败，不继续轮询到超时。

AgentRun YAML-only lane closeout 必须同时看当前 source branch tip、目标 PipelineRun、GitOps revision、Argo revision 和 manager source commit。发布过程中如果 source branch 被并行 PR 推进，`status --pipeline-run <name>` 会通过 `summary.branchDrift` / `alignment.branchDrift` 标记目标 PipelineRun 是否已被当前 branch tip supersede；先确认最新 tip 包含本次修复，再按最新 tip 重新 `trigger-current`。最终只用最新 PipelineRun 的 `status` 中 `aligned=true`、`blockers=[]`、`argoSyncedToGitops=true` 和 `managerSourceMatchesExpected=true` 收口。`trigger-current` 会尝试恢复 YAML 声明的固定 source workspace 到 lane branch；若返回 `source-worktree-restore-failed` warning、workspace dirty，或后续 `status` 仍显示 `summary.source.workspaceDetached=true`，先按 `sourceWorkspaceRestore.failureKind` 修复 fixed workspace，再继续下一轮开发或部署。

Runner egress proxy 只从 `config/agentrun.yaml` 的 `deployment.runner.egressProxyUrl` 与 `deployment.runner.noProxyExtra` 进入部署；manager Deployment 必须带 `AGENTRUN_RUNNER_EGRESS_PROXY_URL` 与 `AGENTRUN_RUNNER_NO_PROXY_EXTRA`，验收时还要用真实 `create/apply/send` 触发 runner Job，检查 Pod env、event/trace 和 final response。GitOps 已更新但 Argo 仍在旧 revision 时，走 `agentrun control-plane refresh --node <node> --lane <lane> --confirm`，不要手工 patch runtime。

Runner 持久化、idle timeout 和 runner retention 只从 `config/agentrun.yaml` 的 `deployment.runner.*` / `deployment.runner.retention` 进入部署，不在 HWLAB 仓库放运维 YAML。验收不能只看 manager Deployment/Pod env；必须用 HWLAB/AgentRun 原入口创建新 turn 或 runner Job，并检查新 runner Job env、session PVC、`AGENTRUN_SOURCE_COMMIT` 和 trace/result 是否复用同一 run 且没有 `reuse-blocked`；runner retention closeout 还要用 `cleanup-runners --dry-run` 证明 over-limit selection 不触碰 active runner。

Provider credential 的 `config.toml` 变更同样走 YAML `sourceRef`、`secret-sync` 和 `restart`；lane config 只声明该 lane 需要的 Codex CLI runtime options。不要复制指挥机全局 `~/.codex/config.toml` 作为长期事实，也不要在没有同 lane `auth.json` / API key source 验证的情况下覆盖 provider endpoint。

### AgentRun v0.1 兼容入口

```bash
bun scripts/cli.ts agentrun control-plane status \
  [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane trigger-current \
  [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane refresh \
  [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane cleanup-runs \
  [--min-age-minutes N] [--limit N] [--dry-run|--confirm]
bun scripts/cli.ts agentrun control-plane cleanup-released-pvs \
  [--limit N] [--dry-run|--confirm]
```

- `status`: 只读汇总 source commit、PipelineRun、Argo、manager image、git mirror 和 `aligned` 结论
- `trigger-current`: 快进 `G14:/root/agentrun-v01` → mirror sync → 创建 `agentrun-v01-ci-<short12>` PipelineRun
- `refresh`: Argo hard refresh（不 patch runtime workload）
- `cleanup-runs`: 只清理 `agentrun-ci` 中已完成 PipelineRun + 临时 PVC；不清理 `agentrun-v01` runtime runner Job/Pod/Secret
- `cleanup-released-pvs`: 回收 Released PV

AgentRun `control-plane status` 的 compact JSON 关键字段在 `.data.summary.sourceCommit`、`.data.summary.expectedPipelineRun`、`.data.summary.runtimeAlignment`、`.data.summary.gitMirror`、`.data.summary.ci.pipelineRun`、`.data.summary.argo` 和 `.data.alignment`，不要假设存在 `.data.status`。触发部署后如果 GitOps 已 promotion 但 git mirror `pendingFlush=true`，先执行 `bun scripts/cli.ts agentrun git-mirror flush --confirm --wait`，再 `control-plane refresh --confirm`，最后用 `control-plane status --full` 证明 `.data.summary.runtimeAlignment.argoSyncedToGitops=true`、`.data.summary.runtimeAlignment.managerSourceMatchesExpected=true` 且 `.data.summary.ci.pipelineRun.status=True`。

## AgentRun v0.1 Git Mirror

```bash
bun scripts/cli.ts agentrun git-mirror status [--full|--raw]
bun scripts/cli.ts agentrun git-mirror sync [--dry-run|--confirm] [--wait]
bun scripts/cli.ts agentrun git-mirror flush [--dry-run|--confirm] [--wait]
```

- `status`: 返回 `localV01`/`githubV01`/`localGitops`/`githubGitops`/`pendingFlush`/`githubInSync`
- `sync`: 拉取 GitHub `v0.1` + `v0.1-gitops` refs
- `flush`: 推送本地 `v0.1-gitops` → GitHub

与 HWLAB v0.2 mirror 共用 `devops-infra` 服务和 cache PVC。
