---
name: unidesk-cicd
description: >-
  UniDesk CI/CD 控制面，覆盖 PaC consumer 首发 bootstrap、Tekton/Argo、GitOps、
  git-mirror、PR 自动交付、Secret、observability、CI tools image、PipelineRun 清理、
  AgentRun 与 HWLAB 部署，以及 branch-follower 退役只读诊断。
  用户提到 CI/CD、deploy、rollout、PipelineRun、PaC、bootstrap、GitOps、Tekton、
  git-mirror、control-plane、k8s/k3s 部署、branch follower、agentrun、hwlab g14、
  monitor-prs 或 trigger-current 时使用。
  任何需要把代码变更推送部署到 G14 k3s 的操作都必须走本 skill。
---

# UniDesk CI/CD

HWLAB G14 和 AgentRun CI/CD 的受控入口。任何 PR 监控、Tekton/Argo、git-mirror、Secret、observability、CI tools image、PipelineRun 清理或 AgentRun 部署都必须走 `bun scripts/cli.ts`。

## 高频入口

```bash
bun scripts/cli.ts cicd status --node NC01
bun scripts/cli.ts cicd status --node <NODE>
bun scripts/cli.ts hwlab g14 monitor-prs --lane v02 --once --dry-run
bun scripts/cli.ts hwlab g14 control-plane status --lane v02
bun scripts/cli.ts hwlab nodes control-plane status --node NC01 --lane v03 --json
bun scripts/cli.ts hwlab g14 git-mirror status --lane v02
bun scripts/cli.ts agentrun control-plane status
bun scripts/cli.ts platform-infra gitea mirror status --target JD01
bun scripts/cli.ts platform-infra gitea mirror webhook status --target JD01
bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target NC01 --consumer <consumer> --dry-run
bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target NC01 --consumer <consumer> --confirm
bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01
bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01 --consumer hwlab-jd01-v03
bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 --limit 10
bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 --id <pipelinerun>
bun scripts/cli.ts platform-infra pipelines-as-code debug-step --target JD01 --consumer <consumer>
bun scripts/cli.ts platform-infra pipelines-as-code debug-step --target JD01 --consumer <consumer> --id <pipelinerun>
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact check --target NC01 --consumer <consumer> --source-worktree <absolute-path>
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact verify-runtime --target NC01 --consumer <consumer> --source-worktree <absolute-path> --source-commit <full-sha>
bun scripts/cli.ts agentrun control-plane legacy-cicd --help
bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
```

- 节点级只读状态：
  - 优先用 `cicd status --node <NODE>`；
  - 单 consumer 再用 `platform-infra pipelines-as-code status --target <NODE> --consumer <id>` 下钻；
  - 状态输出与 registry applicability 的长期判定见 [references/gitea-pac.md](references/gitea-pac.md)。

- 新增 PaC consumer 的首次引导：
  - 先执行一次 `pipelines-as-code bootstrap --dry-run`；
  - 确认后把同一命令改为 `--confirm`；
  - 入口从 Gitea 与 PaC owning YAML 精确解析仓库；
  - 入口只初始化空 Gitea 仓库和 PaC/Tekton/GitOps 控制面；
  - 随后合并 source PR，作为唯一交付触发；
  - 详细最短路径见 [references/gitea-pac.md](references/gitea-pac.md)。

按职责读取拆分后的 reference：

- PR monitor 与自动合并: [references/pr-monitor.md](references/pr-monitor.md)。
- Tekton/Argo、node-scoped runtime lane、D601 infra bootstrap: [references/control-plane.md](references/control-plane.md)。
- HWLAB/AgentRun legacy git-mirror source authority 与 flush: [references/git-mirror.md](references/git-mirror.md)。
- JD01/NC01 Gitea mirror + Pipelines-as-Code 正式架构、三 consumer 覆盖矩阵、target 级 webhook path 和旧入口处置: [references/gitea-pac.md](references/gitea-pac.md)。
- AgentRun/HWLAB env reuse 机制、证据解释和 PaC status/history 观察口径: [references/env-reuse.md](references/env-reuse.md)。
- Secret、observability、platform-infra、CI tools image、PipelineRun 清理和 rollout 补记: [references/platform-ops.md](references/platform-ops.md)。
- AgentRun YAML-only lane、v0.1 兼容入口和 AgentRun git-mirror: [references/agentrun.md](references/agentrun.md)。
- 已退役 K8s branch-follower 的只读状态、事件、日志和历史边界: [references/branch-follower.md](references/branch-follower.md)。

## P0 边界

- PaC migrated consumer 的唯一正式 CI/CD 触发是目标 source branch 的 GitHub PR merge。合并后由 GitHub webhook -> Gitea controlled mirror + immutable snapshot ref -> Gitea webhook -> PaC -> Tekton -> GitOps/Argo -> runtime 自动滚动；不得要求主代理、子代理或操作者再执行 apply、closeout、trigger、sync、flush 或补跑。
- PaC consumer 首次引导必须满足以下约束：
  - 默认使用 `pipelines-as-code bootstrap --dry-run|--confirm`，不再人工串联 Gitea bootstrap 与 PaC apply；
  - `apply` 必须在任何 release、RBAC、Secret、Repository CR、Argo 或 webhook 写入前确认 YAML 匹配的 Gitea 仓库已存在；
  - source PR 合并后不得再次调用 bootstrap 或 apply；正常验收不串联 mirror/status/history，只有失败归因才逐层下钻。
- 自动链路不通时必须修复自动链自身，并通过修复 PR 合并产生的新正常事件验收。禁止人工 mirror sync、直接 Gitea push、人工 PipelineRun、`trigger-current`、运行面 patch 或其他手段补齐当前交付；只读 status/history/events/logs/debug-step 可用于定位，但不得改变交付状态。`closeout` 只属于显式 compatibility diagnostics，不得进入默认观察或恢复路径。
- CI/CD、GitOps、rollout、PipelineRun、Argo、git-mirror 和 AgentRun 的观察、诊断与 legacy 控制必须走受控 CLI；不要用裸 `kubectl`、`argo`、`tkn`、`curl` 当正式控制入口。PaC migrated consumer 仍只由 GitHub PR merge 触发，不得因“必须走 CLI”而额外调用写命令。
- CI/CD source authority 只能来自 YAML 声明的 Kubernetes 托管 source authority：
  - legacy lane 由受控命令在 k8s 内同步并创建不可变 `refs/unidesk/snapshots/.../<commit>` stage ref；
  - Gitea/PaC migrated lane 由 GitHub PR merge 自动驱动 GitHub webhook bridge、Gitea controlled mirror 与 immutable snapshot ref，禁止合并后人工同步或创建 snapshot；
  - build/status/publish 只消费对应 snapshot，host worktree、本地 `git fetch/pull`、可变 branch ref 或 Pipeline 内直连 GitHub 都不能作为 authoritative source。
- CLI 必须组合 `config/platform-infra/gitea.yaml` 与 `config/platform-infra/pipelines-as-code.yaml`，按 consumer、node、lane、upstream repository、branch 和 Gitea repository 精确解析 delivery authority。不得用 URL 片段或 repo 专属条件判断迁移；零匹配、多匹配和配置错误都返回 `unknown`、`mutation=false` 并 fail-closed。
- PaC 与 `unknown` authority 的 help、plan、status、失败态 `Next`、`REPAIR` 和实际执行 guard 都不得包含或执行 mutation command。`trigger-current|refresh|sync|flush` 只在 YAML 精确解析为 `legacy-manual` 后进入旧实现，且只从 `legacy-cicd` / `legacy-ops` scoped help 发现；平台 bootstrap、Secret 与配置维护使用独立 scoped help，不得充当 source delivery recovery。
- JD01/NC01 `agentrun-<node>-v02`、`sentinel-<node>-v03`、`hwlab-<node>-v03` 的交付收口由自动链自行完成并写入状态。仅在显式调查自动链故障时读取 `cicd status --node <NODE>` 或 `platform-infra pipelines-as-code status|history --target <NODE> --consumer <id>`；主代理与子代理不得把 `closeout` 当作 PR 合并后的人工步骤。`cicd branch-follower` 和 `cicd gitea-actions-poc` 对这些 consumer 只保留历史/迁移只读用途。
- PaC `.tekton` 文件必须用 Repository CR 的 target/node 参数隔离 JD01/NC01，避免同一个 Gitea push 在一个 target cluster 内额外创建另一个 target 的 PipelineRun。`history --id` 必须按运行面 provenance 和实际 PipelineRun prefix 唯一归属 consumer；零匹配、多匹配或显式 consumer 不一致均 fail-closed，不得回退到默认 consumer 或默认 node。
- PaC source artifact 必须遵循以下边界：
  - 由 `config/platform-infra/pipelines-as-code.yaml#consumers[].sourceArtifact` 显式声明；
  - 通过 `source-artifact plan|check|write|status|verify-runtime` 管理；
  - `taskRunTemplate` 等运行事实归属 owning YAML，代码只负责校验和渲染；
  - desired 只来自 owning YAML 与共享 renderer，禁止从 live Pipeline/PipelineRun 反向导出；
  - `verify-runtime` 使用干净且 `HEAD` 等于完整 source commit 的独立 worktree；
  - 未声明 owner、worktree 证据不成立或运行面证据冲突时 fail-closed；
  - 错误和 drift 在默认、`--json`、`--full` 中都只能披露路径、类型、长度、SHA-256 或 URL fingerprint；
  - 禁止回显脚本、URL 凭据、查询参数、Authorization、Secret 和远端 stderr。
- PaC source-only closeout 必须使用目标侧结构化证据：
  - `g14-ci-plan` 的 source commit、affected/build/rollout 计数必须完整；
  - `collect-artifacts` 与 `gitops-promote` 必须同时明确 `no-build-no-rollout-plan`；
  - 三者一致时才允许返回 `no-runtime-change` 和 `retained` provenance；
  - 仍要求 PipelineRun 成功、原 Argo `Synced/Healthy`、runtime ready、sha256 digest pin 与 health ready；
  - 证据缺失、冲突或 source commit 不匹配必须 fail-closed，不得等价为 delivery disabled，也不得伪造新 artifact、GitOps revision 或 runtime source commit。
- PaC `status|closeout|history` 的 `--json` 是有界 machine contract；`--full` 展开单条详情，`--raw` 才展开目标侧原始 payload。`history --id --full` 不得重复嵌套同一 rows/consumer payload或依赖 `/tmp/unidesk-cli-output` 才可读。
- PaC `debug-step --id <pipelinerun>` 必须读取目标侧真实 PipelineRun、TaskRun condition/result 与合同 Task 日志：
  - 默认输出每个 terminal role 的 presence、status、source 与首个断点；
  - 同时披露 artifact catalog、digest、GitOps commit 和 collector 读错；
  - 该入口只读且 `mutation=false`，不能创建 PipelineRun 或写 GitOps/runtime；
  - 未带 `--id` 时才只运行 evaluator fixture。
- GitHub/Git 相关 egress 必须走 YAML-first host proxy/sourceRef：branch-follower controller 读 `config/cicd-branch-followers.yaml#controller.source.githubSsh`，runtime legacy git-mirror 读 owning lane/control-plane YAML 的 host proxy 和 `githubTransport`，Gitea/PaC 迁移 lane 读 `config/platform-infra/gitea.yaml` 与 `config/platform-infra/pipelines-as-code.yaml`；禁止依赖未声明 host env、trans proxy、裸直连 GitHub 或 CLI 输出解析。
- Gitea/PaC lane 的 GitHub -> Gitea 自动同步由 owning YAML 渲染的 webhook bridge 与 durable inbox worker 承担：
  - receiver 验签并校验 GitHub payload 后，只有在 delivery 通过 fsync 与 atomic rename 写入 YAML 声明的 PVC inbox 后才能返回 HTTP `202 Accepted`；
  - `202 Accepted` 只证明 delivery 已持久接收，不证明 Gitea refs 已提交；状态必须分为 `accepted`、`processing`、`committed`、`failed`；
  - 只有同一 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。默认只使用 `platform-infra gitea mirror webhook status` 观察 accepted -> committed 分层。会 POST hook test 的 mutation 测试入口已经删除；连通性只能通过真正只读的 status、GET 与 readiness 观察，不能制造伪 push。平台 bootstrap 必须先落入 Git-backed YAML/controller/source，并由正常 PR merge 的自动事件验收；禁止 Gitea -> GitHub 回写、轮询/read-model fallback、Gitea Actions/act_runner fallback 或第二 source authority。
- Gitea webhook/FRPC/Caddy 自动链变更的 closeout 必须观察修复 PR 合并后的真实 delivery、rollout、只读 status/GET/readiness 证据；Secret 或 ConfigMap 声明已生成不等于 connector Pod 已加载新 proxy/path，也不得用人工 apply 或 hook test 补齐。
- Sentinel 内部 publish capability 的唯一真相是 `config/platform-infra/pipelines-as-code.yaml#capabilities.sentinelInternalPublish`，当前默认关闭。Pod env、ownerReferences、ServiceAccount 与 metadata 不能证明 creator；#1769 的 admission-owned provenance 落地前不得启用。`.tekton` 保持既有自动 `publish-current` 入口，禁止改成人工 publish。
- PaC PipelineRun 必须区分外层 `outer-pac-event`、内层 `inner-deterministic-publish` 与 `unknown`。只有唯一外层事件可驱动 latest/status/debug-step/closeout；内层只能作为未绑定执行观察，缺少 admission-owned 父子证据时不得按前缀推断。
- `cicd branch-follower` 对当前 PaC consumer 已退役，只保留 `status|events|logs` 与只读 `debug-step` 历史诊断。默认入口、任意 `--config` 和混合/未知 authority 都不能恢复 `apply|run-once|state-write` 等写能力，也不能猜测默认 node。
- k8s 运行面从拉取已构建镜像开始必须 0 Docker；CI 构建面可以使用 YAML 声明的原生构建工具，但不得把 Docker socket、Docker daemon 或 host Docker 带入运行面。
- 正式 CI/CD、publish、image build 和 rollout 必须走 Tekton Task/Pipeline/PipelineRun 承担 CI，并通过 GitOps/Argo 承担部署收敛；普通 Kubernetes Job 只允许用于 bounded helper、source sync、diagnostic、cleanup 或 bootstrap，不得作为正式发布、镜像构建或 rollout 入口。
- 未迁移的 legacy CI/CD 若仍保留人工入口，必须由单一受控命令完成 source sync、构建、发布、GitOps/Argo 收敛、runtime provenance 校验和 `/health` 端点验证。PaC migrated consumer 不得使用该人工入口，其唯一交付触发是 GitHub PR merge。
- CI/CD 端到端 wall-clock 目标及预算只由 owning YAML 声明；PaC migrated consumer 从 GitHub PR merge 事件计时，legacy lane 才从操作者触发受控命令计时，均到 runtime ready 且 `/health` 端点验证完成为止。
- CI/CD validation 阶段只能验证部署对象的 `/health` 端点和必要 provenance；禁止在 CI/CD gate 中运行 web-probe、Playwright、远程浏览器截图、用户路径 E2E 或等价重型业务探针。业务/用户入口验证只能作为发布后的独立 post-deploy validation 证据，不得阻塞 CI/CD 一键交付。
- 产品功能配置 schema 的 CI/CD 合同：
  - repo 内唯一约定路径固定为 `config/feature-config.schema.json`；
  - schema 固定使用 JSON Schema Draft 2020-12，并由 lockfile 锁定的完整结构化 validator 校验；禁止用正则、浅层字段遍历或关闭整体 strict 代替；
  - schema 只描述产品功能配置，不得承载数据 authority、事件路径、投影实现或交付路径选择；
  - 每个顶层 `properties` 成员必须声明唯一非空 `x-unidesk-feature`；一个产品功能只允许一个规范配置 property，禁止别名、反向变量或重复开关；
  - candidate snapshot 必须由正常 source-artifact 阶段从 owning YAML 或已渲染 workload 显式生成；禁止读取 runner `process.env` 作为产品配置 authority；
  - HWLAB、AgentRun 与 Sentinel consumer 必须复用同一 shared helper，并在正常 source-artifact/GitOps publish 路径执行，不得只挂到某个 consumer 的专属后处理；
  - CI/CD 必须检查 schema 语法、嵌套配置值匹配和功能到配置 property 的一对一关系，并输出有界 typed warning；输入快照缺失使用独立 typed warning，不得与 schema 非法混淆；
  - schema 缺失、非法、candidate 缺失、配置不匹配、重复 property、validator/依赖异常或 OTel 导出失败时，只允许记录 warning；embedded 命令必须以 `0` 退出，PipelineRun、artifact、GitOps promote、Argo reconcile、runtime rollout 和 `/health` closeout 必须继续；
  - schema 校验器只读，禁止写回配置、注入默认值、删除变量、关闭功能、切换架构路径或执行任何补救操作；运行时必须原样接收本次待发布配置；
  - schema warning 必须实际导出 CI/CD OTel span/event；endpoint、serviceName、sampling 和 exporter timeout 只读 `config/platform-infra/pipelines-as-code.yaml#observability` 与精确 repository params，零/多 repository 匹配在 renderer 生成期 fail-closed；
  - OTel exporter 自身失败继续输出 `blocking=false` warning；warning 同时进入 `status|history|debug-step` 的同构有界投影，但不得改变成功/失败终态。
- 任一 CI/CD 阶段或总耗时超过 owning YAML 预算时，不要继续死等或把超长等待视为正常；先输出阶段耗时分解，并优先从 env reuse、git mirror、BuildKit/cache、GitOps/Argo watch 和 runtime readiness 探测方向优化后再继续交付。
- 仅 legacy lane 的 node-scoped `trigger-current --wait` 可以把 source sync、pre/post flush、PipelineRun、GitOps/Argo、runtime readiness 和 `/health` closeout 放进同一端到端 YAML 预算；超预算时由 CLI 输出阶段分解、Argo target revision、runtime/public 状态和 TaskRun/Pod drill-down。PaC migrated consumer 禁止执行 `trigger-current`，也禁止手动串联状态、sync 或 flush 命令补齐交付。
- node-scoped `control-plane status` 可见性规则：
  - 必须立即输出 started/阶段，并按 owning YAML 输出 heartbeat；
  - 正常、总预算超时、中断和 worker 失败都要返回有界 closeout；
  - closeout 必须披露探针完成情况、进程组清理结果和脱敏的 worker 错误摘要；
  - 显式 `--json` 的 stdout 只允许一个合法 JSON 文档，progress 写入 stderr。
- 触发或验收 rollout 时必须绑定 lane、source commit、PipelineRun/GitOps revision、runtime ready 和 `/health` 端点验证结果；web-probe/Playwright 结果只能作为单独的 post-deploy 证据。
- CI/CD 状态、日志和事件查询必须减少 trans/SSH 传输：能在目标 NODE/k8s 内解析、聚合、裁剪的内容，必须在目标侧计算成短 JSON/table 摘要后再回传；禁止为了本地解析而把完整 ConfigMap、大对象、长日志或原始 API payload 透传回来。
- 退役 branch-follower 排障只允许使用 `state-read|controller-source|status-read|decide`、`status|events|logs` 等只读边界；`state-write`、gate、`run-once` 和自动 loop 不再是当前 PaC consumer 的修复或验收入口。
- 自动链单步测不通时，应在 Git-backed YAML、controller 或源码中修复根因，并由修复 PR 合并产生的新正常 webhook/PaC 事件复测；不得用 branch-follower 写状态或补跑当前交付。
- CI/CD 排障中再次踩到已暴露的运行面坑、工具误用、镜像假设或状态可见性缺口时，必须先把长期规则写入本 skill/reference，再继续只读定位。
- CI/CD 运行面验收必须在目标 NODE/k8s 内计算短摘要；本机只用于源码阅读、编辑和静态合同测试，不能替代正常 PR merge 自动事件产生的目标运行面证据。
- in-cluster/controller/native helper 不能假设镜像内存在 `kubectl` binary。目标 Pod/Job 内读取或写入 Kubernetes 对象必须走 serviceaccount token + Kubernetes HTTPS API 或已封装的 native helper；`kubectl` 只允许在 operator 侧受控 CLI/trans 边界作为 transport/debug 包装，不得进入正式 controller 状态读写链路。
- 一旦发现 CI/CD CLI 被误用且可能写入错误状态、产生伪证据或绕过目标运行面，必须立刻先把用法改成更符合直觉的公开入口并更新本 skill/reference，再继续验证或交付；不要只靠口头记忆、隐藏 flag、手动约定或后续小心来避免复发。内部 in-cluster 模式必须只由目标 k8s Job/Pod 调用，操作者从本机只能用公开入口提交目标侧 Job 或读取目标侧摘要。
- PaC migrated consumer 的旧手动 publish/debug 命令不得作为正式 closeout 引导；调查命令默认只能指向只读的 `platform-infra pipelines-as-code status|history`。`closeout` 仅保留只读兼容能力，不得作为主代理、子代理或操作者的 PR 合并后步骤。需要保留的旧 mutation 命令只能属于 YAML 明确的 legacy authority 或独立平台维护，不得标记或引导 manual recovery；单步确认的自动链缺陷必须回到 YAML/controller/源码修复。
- Secret 只通过 YAML sourceRef/targetKey 和受控 CLI 下发；输出只披露 presence/fingerprint。
- 长命令用异步 job 或短轮询；不要长时间挂住 trans/ssh。
- Web 哨兵 YAML cadence 变更不新增发布 orchestrator：提交前用 `web-probe sentinel validate --local`；合并后用只读 `web-probe sentinel control-plane status --wait`。线上 desired schedule 必须来自 GitOps 清单，不能用操作者旧 worktree 的本地渲染值做阻塞门禁；收敛差异统一为非阻塞 warning。

## 何时读取 reference

- PR 自动合并、v0.2/v0.3 lane 差异：读 [references/pr-monitor.md](references/pr-monitor.md)。
- Legacy 手动触发、定点 PipelineRun/source commit、RBAC/Pipeline/Argo、node-scoped runtime lane：读 [references/control-plane.md](references/control-plane.md)。
- Legacy git-mirror source authority 或 flush：读 [references/git-mirror.md](references/git-mirror.md)。
- AgentRun/HWLAB env reuse、`IMAGE_STATUS=reused`、`ENV_REUSE=hit`、`skipped,skip=<n>` 或 PaC history/status 解释：读 [references/env-reuse.md](references/env-reuse.md)。
- JD01/NC01 Gitea+PaC 覆盖、旧 branch-follower/Gitea Actions POC 处置和只读观察顺序：读 [references/gitea-pac.md](references/gitea-pac.md)。
- Secret、observability、CI tools image、PipelineRun/PV 清理：读 [references/platform-ops.md](references/platform-ops.md)。
- AgentRun v0.1 或 YAML-only lane 部署：读 [references/agentrun.md](references/agentrun.md)。
- 退役 branch-follower 的 `status|events|logs|debug-step` 历史只读诊断：读 [references/branch-follower.md](references/branch-follower.md)。
