24 KiB
Gitea + Pipelines-as-Code
Gitea/Pipelines-as-Code 迁移后的 CI/CD 只有一条正式路径:
GitHub PR merge -> GitHub webhook bridge -> Gitea controlled mirror and immutable snapshot refs -> CLI release plan -> CLI manual PaC webhook -> Tekton PipelineRun -> GitOps/Argo -> k8s runtime
GitHub 是唯一上游写入权威。目标 source branch 的 GitHub PR merge 只更新 Gitea source authority,不创建 PipelineRun。操作者必须先执行 release plan,审阅本次请求范围、累计有效范围、env reuse、镜像构建和 rollout;范围准确后再执行 release trigger --confirm,由 CLI 手动发送 PaC webhook。禁止恢复 Gitea 自动 hook、直接创建 PipelineRun 或用 apply、sync、flush 补跑。
发布链任一环节不通时,任务目标是修复该环节的 YAML、controller、planner 或源码,并重新执行 plan;范围准确后才允许手动 webhook 验收。禁止用 Gitea Actions、act_runner、branch-follower、host worktree、Pipeline 内直连 GitHub、直接 Gitea push、人工 mirror sync、裸建 PipelineRun、trigger-current、hook test 或自定义脚本补齐当前交付。config/cicd-gitea-actions-poc.yaml 只保留只读历史材料。
Webhook bridge 自举保护
- Argo
selfHeal的边界:- 只能把 GitOps branch 已存在的 desired 恢复到运行面;
- 不能从 GitHub 获取尚未进入 Gitea branch/snapshot 的 bridge 修复 commit;
- 不得把
selfHeal描述为 bridge 故障后的独立 source authority。
- Bridge desired 更新必须经过同一 Argo Application 的 PreSync candidate gate:
- owning 配置是
config/platform-infra/gitea.yaml#sourceAuthority.webhookSync.bridge.candidateGate; - candidate 使用与正式 Deployment 相同的镜像、
server.mjs和 ConfigMap 符号链接挂载形状; - candidate 使用隔离的临时 inbox,只验证进程启动、durable storage readiness 与
/healthz; - candidate 通过后才允许 Argo 更新正式 ConfigMap 和
RecreateDeployment; - candidate 失败时必须保留现有 ready bridge,不能继续替换单写者。
- owning 配置是
- Candidate gate 不是第二触发或恢复路径:
- 不读取 GitHub branch;
- 不写 Gitea branch/snapshot;
- 不创建业务 PipelineRun;
- 不接收或重投 webhook delivery。
- 若 bridge 在 candidate gate 引入前已经不可用:
- 新 PR 不能通过同一故障 bridge 自动部署自身修复;
- 状态必须明确报告自举死锁;
- 禁止用 branch-follower、轮询、人工 mirror sync、直接 Gitea push、人工 PipelineRun、
trigger-current、sync/flush 或 delivery 重投伪造自动恢复。
唯一事实来源
- Gitea source authority、GitHub webhook bridge、公网暴露与 source snapshot 归属
config/platform-infra/gitea.yaml。 - PaC controller、Repository CR、consumer 参数、Tekton pipeline 名称与 Argo Application 归属
config/platform-infra/pipelines-as-code.yaml。 - CLI 必须组合两份 YAML,并按 consumer、node、lane、upstream repository、branch 与 Gitea repository 精确解析 authority。不得用 URL heuristic 或 repo 专属特例;
unknown、歧义和配置错误都必须mutation=false并 fail-closed。 - 旧 source mutation 入口已退役;历史 help/status 只能返回只读诊断,Gitea/PaC consumer 和
unknownauthority 都不得回退到 legacy 实现。 - 包含
gitea-actions的历史 snapshot prefix 只为既有 ref 保留,不代表 Gitea Actions 仍是有效触发架构。 - node 级状态入口是
bun scripts/cli.ts cicd status --node <NODE>,consumer 级默认下钻入口是platform-infra pipelines-as-code status|history --target <NODE> --consumer <id>。 - Gitea 公网入口:
- 公开 Web UI 与 owning webhook path 统一由
config/platform-infra/gitea.yaml#app.publicExposure声明; - NC01 public-edge 直接连接 ClusterIP Service;
- 不得为 Gitea 保留 FRPC、PK01 Caddy 或公网回环 fallback;
- target 仍通过
targets[].webhookSync.publicPath保持 hook 身份隔离; - 禁止在 source PR 合并后人工 apply 补齐。
- 公开 Web UI 与 owning webhook path 统一由
- PaC Repository CR
spec.url必须与 Gitea webhook payload 中的 URL 一致。config/platform-infra/pipelines-as-code.yaml#repositories[].url保持公网 Gitea repository URL,k8s 内网 service URL 只能写入cloneUrl或params.git_read_url;否则 PaC 会记录cannot find a repository match且不会创建 PipelineRun。 - JD01 与 NC01 共用的 repository 必须传入 target-specific
nodeRepository param,每个.tektonPipelineRun 必须用pipelinesascode.tekton.dev/on-cel-expression过滤该参数。一个 target 的 push 不得在同一 target cluster 创建另一 target 的 PipelineRun。
PaC consumer 首发最短路径
- 在 source PR 合并前完成源码侧准备:
- owning YAML 中声明 Gitea repository、PaC repository 与 consumer;
- consumer 声明
sourceArtifact时,先在 source worktree 执行source-artifact check;该命令同时检查内联 Pipeline 的command、args与 shellscript实际执行的仓库相对源码入口是否存在; - 不创建人工 PipelineRun,也不推送 Gitea branch 或 snapshot。
- 只使用同一个组合入口完成控制面首次引导:
bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target <NODE> --consumer <consumer> --dry-run
bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target <NODE> --consumer <consumer> --confirm
- 组合入口负责以下职责:
- 组合
config/platform-infra/gitea.yaml与config/platform-infra/pipelines-as-code.yaml; - 按 target、Gitea owner/name 与 read URL 精确选择唯一 repository;
- 初始化选中的空 Gitea repository;
- 安装或更新 PaC controller、consumer RBAC、Repository CR、Argo bootstrap 与 Gitea webhook;
- 不同步 source branch,不创建 snapshot,不触发业务 PipelineRun。
- 组合
- 预检与失败边界:
- 默认 human 与显式
--json使用同一份紧凑 typed projection,按yaml-exact-match、github-upstream、gitea-repository前置条件,以及gitea-init、pac-controller、tekton-consumer、gitops-argo阶段展示结果;只有显式--full或--raw才披露底层组合结果; - YAML 精确匹配必须显示匹配数量,并区分
yaml-repository-no-match与yaml-repository-multiple-matches; - GitHub 上游或凭据不可用、Gitea 初始化失败与 PaC apply 失败必须使用不同 blocker code;不得折叠为同一条未知错误;
- 失败输出只给 owning YAML 修正提示或 consumer 只读
status下钻;不得给 mirror sync、人工 PipelineRun、Argo refresh 或其他 mutation 恢复命令; - PaC
apply在任何 Kubernetes 写入前读取 Gitea repository; - repository 缺失时返回
gitea-repository-missing,不留下半套 Tekton/RBAC/Argo 状态; - YAML 零匹配或多匹配时在本地拒绝,不猜测 repository;
gitea mirror bootstrap未带--confirm时输出可读 dry-run 与精确 confirm 命令,不再返回空白拒绝。
- 默认 human 与显式
- 完成
bootstrap --confirm后合并 source PR:- GitHub PR merge 只更新 source authority;
- 执行
release plan并审阅范围; - plan 未扩大时执行
release trigger --confirm; - 不再调用 bootstrap、apply、mirror sync、裸建 PipelineRun、Argo refresh 或 closeout。
- 只有出现自动链故障或用户明确要求验收时才读取状态:
- 首选一次
cicd status --node <NODE>获取 node 摘要; - 只对失败 consumer 使用一次
pipelines-as-code status --consumer <id>; - 只有归属或 TaskRun 断点不清楚时再用
history --id或debug-step --id; - 默认不使用
--full或--raw,避免把大对象带回本机; - consumer 声明
sourceArtifact且需要精确提交验收时,最后执行一次source-artifact verify-runtime。
- 首选一次
- node 状态摘要必须保持可读且可下钻:
- text 输出在零 consumer、ready、warning 与失败时都返回非空 typed 摘要;
- 摘要只给出与当前状态匹配的精确下钻命令;
- 不要求操作者阅读源码或手工拼接多个 consumer 命令。
- registry applicability 只由 owning YAML 决定:
- consumer 对应 pipeline 声明 image repository 时,registry 状态适用;
- 适用的 registry 缺失是 blocker;
- GitOps-only consumer 未声明 image repository 时,registry 显示
N/A; - 不得因 registry 不适用而把 GitOps-only consumer 判为失败。
PaC 源码侧制品同步
当 owning Pipeline 与消费仓 .tekton 内联 pipelineSpec 或远程 Pipeline 文件存在漂移时,统一使用以下入口:
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact plan --target <NODE> --consumer <id> --source-worktree <absolute-path>
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact check --target <NODE> --consumer <id> --source-worktree <absolute-path>
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact write --target <NODE> --consumer <id> --source-worktree <absolute-path> --confirm
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact status --target <NODE> --consumer <id> --source-worktree <absolute-path> [--source-commit <full-sha>]
bun scripts/cli.ts platform-infra pipelines-as-code source-artifact verify-runtime --target <NODE> --consumer <id> --source-worktree <absolute-path> --source-commit <full-sha>
config/platform-infra/pipelines-as-code.yaml#consumers[].sourceArtifact是生成职责的声明入口;未声明的 consumer 必须稳定拒绝,模板不得替它发明 owner。source-artifact plan|check|write只能在 source PR 合并前生成或校验该 PR 的受管源码制品,不是合并后的 CI/CD 触发、同步、恢复或补跑入口。- desired 只来自 owning YAML 与共享 domain renderer;live Pipeline 和 PipelineRun 只能作为只读诊断证据,禁止从运行面反向导出 desired 或源码制品。
plan、check、write只比较或更新显式消费仓 worktree,不访问运行面;worktree 必须是绝对路径、Git 根目录,并与声明仓库的精确 remote identity 一致。write先完成全部路径、内容和 renderer 合同校验,再通过同目录临时文件与 rename 更新;连续执行必须无差异。plan|check|write对受管 Pipeline 做通用源码入口检查:- 只识别实际执行上下文中的仓库相对
scripts/、src/、deploy/与config/入口; - 缺失入口进入
sourceEntrypoints.missing并使check|writefail-closed; ..逃逸和仓库外路径不参与读取,输出只披露相对路径。
- 只识别实际执行上下文中的仓库相对
status是非门禁诊断,允许不传 commit。verify-runtime必须满足以下条件:- 传入完整四十位
--source-commit; --source-worktree干净;- worktree
HEAD与该 commit 完全相同; - 合并提交的验收新建精确提交 worktree,不使用带未提交修改或停在父提交的开发 worktree;
- PipelineRun 按 PaC/source-commit label 或 annotation 精确筛选,不按 prefix 直接取全局 latest。
- 传入完整四十位
- 同一 source commit 因 webhook 重投或 PaC retry 产生多个 PipelineRun 时,只在完整内联 spec 与 provenance 全部一致时确定性选择最新一条并披露候选数;存在冲突必须 fail-closed。
- runtime observer 必须满足以下约束:
- 区分对象
NotFound、transport/RBAC/命令不可用和 spec drift; - 完整大 spec 通过受控临时文件传入目标侧原生 observer,不放入 shell argv 或环境变量;
- 默认、
--json和--full的错误与 drift 只输出结构路径、类型、长度、SHA-256 或 URL fingerprint; - 不回显脚本、URL 凭据、查询参数、Authorization、Secret、stdout 或 stderr 原文。
- 区分对象
- 完整 spec 比较只移除六类已验证的 Tekton admission default,并限定在裸 Pipeline spec、完整 Pipeline 或 PipelineRun
pipelineSpec三个明确根;其他同名字段不得过滤。 sourceArtifact.taskRunTemplate是hostNetwork、dnsPolicy和fsGroup的唯一所有者;生成器不得另设硬编码默认值。- HWLAB source artifact 必须经过完整 domain renderer、正式 postprocess/verify 和完整 spec 比较。
- Kafka refresh 等业务合同由 owning renderer 与消费者结构化负例测试保障;
- 不得在通用生成器中用字符串 marker 扫描建立第二真相。
- AgentRun source artifact 通过完整 owning Pipeline 与完整 spec 比较保留 RBAC、参数和 manager 环境合同。
覆盖矩阵
| Consumer | Source | PaC namespace | Pipeline | Argo Application | 状态命令 |
|---|---|---|---|---|---|
agentrun-jd01-v02 |
pikasTech/agentrun@v0.2 |
agentrun-ci |
agentrun-jd01-v02-ci-image-publish |
agentrun-jd01-v02 |
platform-infra pipelines-as-code status --target JD01 --consumer agentrun-jd01-v02 |
sentinel-jd01-v03 |
pikasTech/unidesk@master |
devops-infra |
hwlab-web-probe-sentinel-jd01-pac |
hwlab-web-probe-sentinel-jd01 |
platform-infra pipelines-as-code status --target JD01 --consumer sentinel-jd01-v03 |
hwlab-jd01-v03 |
pikasTech/HWLAB@v0.3 |
hwlab-ci |
hwlab-jd01-v03-ci-image-publish |
hwlab-node-v03 |
platform-infra pipelines-as-code status --target JD01 --consumer hwlab-jd01-v03 |
agentrun-nc01-v02 |
pikasTech/agentrun@v0.2 |
agentrun-ci |
agentrun-nc01-v02-ci-image-publish |
agentrun-nc01-v02 |
platform-infra pipelines-as-code status --target NC01 --consumer agentrun-nc01-v02 |
sentinel-nc01-v03 |
pikasTech/unidesk@master |
devops-infra |
hwlab-web-probe-sentinel-nc01-pac |
hwlab-web-probe-sentinel-nc01 |
platform-infra pipelines-as-code status --target NC01 --consumer sentinel-nc01-v03 |
hwlab-nc01-v03 |
pikasTech/HWLAB@v0.3 |
hwlab-ci |
hwlab-nc01-v03-ci-image-publish |
hwlab-node-v03 |
platform-infra pipelines-as-code status --target NC01 --consumer hwlab-nc01-v03 |
-
history 范围:
- 使用
bun scripts/cli.ts platform-infra pipelines-as-code history --target <NODE> --limit 10审计全部 consumer 的触发、耗时与 reuse; - target 汇总读取实时 Gitea Repository CR 与 Tekton PipelineRun/TaskRun,并按 namespace 复用读取、聚合和显式输出
READ_ERRORS; - 指定
--consumer <id>或可唯一归属的--id时只走单 consumer 快路径; config.scope明确显示target-summary或single-consumer。
- 使用
-
单次交付耗时查询:
- 使用
bun scripts/cli.ts platform-infra pipelines-as-code delivery-timing --target <NODE> --consumer <id>; - 命令从 owning YAML 解析上游 GitHub repository、branch 与 consumer;
- 默认关联最新 PipelineRun source commit 对应的 GitHub PR
mergedAt; - 同时披露 trigger wait、PipelineRun、端到端耗时及 Argo/runtime closeout;
- 缺少 PR、时间戳或 provenance 时返回
partial与evidenceGaps,不得用 commit 时间替代mergedAt; - 整个入口保持
mutation=false,不写入历史库,不触发、同步或补跑交付。
- 使用
-
registry cache 性能验收:
- 冷缓存样本:
- 第一个正常 source PR 只负责预热;
- PipelineRun 成功且 cache 已导出后记录一次
delivery-timing; - 不得把首次
mode=max导出耗时当作 cache-hit 回归结果。
- cache-hit 样本:
- 第二个正常 source PR 不得改变目标构建输入;
- 可以使用本来就需要合并的源码或文档变更;
- 禁止制造无语义 source-artifact 刷新。
- 耗时判定:
- 两个样本各执行一次
delivery-timing; - 按 owning YAML 的
deliveryTiming.endToEndBudgetSeconds比较 trigger wait、PipelineRun、最长 TaskRun 和端到端耗时; state=partial时保留已成立的时间字段与evidenceGaps,不立即串联status、history或日志;- 只有真实失败或证据矛盾才按最短分层诊断入口下钻。
- 两个样本各执行一次
- source artifact 收口:
- consumer 声明
sourceArtifact时,在最终 cache-hit commit 建立 clean exact-commit worktree; - 只执行一次
source-artifact verify-runtime,完成 artifact、embedded PipelineRun 与 provenance 对齐。
- consumer 声明
- 禁止项:
- 两个样本分别使用一次 PR merge、一次 plan 和一次手动 webhook;
- 不创建人工 PipelineRun;
- 不执行 mirror sync、Argo sync、补链或第二触发。
- 冷缓存样本:
history --id <pipelinerun> 必须按 target 内唯一 PipelineRun prefix 或 Tekton pipeline 归属解析实际 consumer,并用该 consumer 生成 status/history Next。零匹配、多匹配或显式 --consumer 与实际归属冲突时必须在远端读取前 fail-closed;禁止回退默认 consumer。
按需只读调查
手动 webhook 发出后,PaC、Tekton、GitOps/Argo 必须自动完成后续交付并记录 terminal 状态。以下入口只在显式调查发布链故障时按需使用:
-
bun scripts/cli.ts platform-infra gitea mirror status --target <NODE> -
bun scripts/cli.ts platform-infra gitea mirror webhook status --target <NODE> -
bun scripts/cli.ts platform-infra pipelines-as-code status --target <NODE> --consumer <id> -
bun scripts/cli.ts platform-infra pipelines-as-code history --target <NODE> --consumer <id> --limit 10 -
对归属或状态不明的行,使用
bun scripts/cli.ts platform-infra pipelines-as-code history --target <NODE> --id <pipelinerun>下钻 PipelineRun。 -
已知失败 PipelineRun 时使用
debug-step --target <NODE> --consumer <id> --id <pipelinerun>;默认结果一次包含失败 TaskRun、step、container、exit code、termination reason 与脱敏有界日志尾。 -
closeout兼容边界:- 只保留为可选的只读历史/诊断兼容入口;
- 不得出现在 migrated consumer 的默认观察顺序、覆盖矩阵、正常下一步或 PR 合并后的主/子代理任务中;
- 不得创建、同步、刷新、重试或补跑任何交付对象;
- 显式
--wait由 Kubernetes watch 持有等待; - 只有 source commit、GitOps exact/descendant ancestry、Argo 与 runtime sha256 digest 一致时才 ready;
no-runtime-change使用独立保留合同;- consumer 专属 debug 命令只允许复现单步故障,不得成为正式交付或恢复入口。
GitHub webhook delivery 的持久接收与 refs 提交必须分层:
-
receiver 验签并校验 payload 后,只有在 delivery 通过 fsync 与 atomic rename 写入 YAML 声明的 PVC durable inbox 后才能返回 HTTP
202 Accepted; -
202 Accepted只证明 delivery 已持久接收,不证明 Gitea authority branch 或 immutable snapshot 已 committed; -
journal 状态必须显式区分
accepted、processing、committed、failed,并保留 deliveryId、payload correlation、尝试次数与有界失败摘要; -
同 deliveryId 与同 payload 必须幂等,异 payload 必须返回
409;PVC 不可用或容量满必须返回503并使 readiness=false; -
inbox worker 使用 YAML 声明的有界 backoff 自动重试,进程重启后恢复
accepted/processing,不要求人工 sync、repair、test 或 PipelineRun; -
只有同一 delivery 的 exact-after immutable snapshot 与 authority branch 经 atomic push 后重新读取 refs 证明一致,才能进入
committed; -
Caddy 只可对尚未成功落盘的请求做小于 10 秒、body-safe 的有界重试,不能把代理成功当作 refs committed。
-
当前 bridge 的权威状态链:
- GitHub delivery GUID -> durable inbox record -> post-push refs proof -> live refs;
- HTTP
202后记录仍是accepted、refsCommitted=false; - 只有 worker 完成 exact refs 复核后才成为 committed;
- 旧 durable journal 上线前的记录可以标记
pre-durable-bootstrap,但不得标记 committed; ingressRetry由 owning YAML 与 NC01 public-edge 自动交付共同控制,不能由操作者手工补配置。
Durable inbox 只是 webhook delivery 的持久接收与重试 journal,不是业务 source/ref 的第二份真相,也不得被 PaC、Tekton、GitOps、Argo 或 runtime 当作源码/read model。最终 source authority 仍只有 Gitea authority branch 与 immutable snapshot。webhook status 默认摘要必须显示 accepted -> processing -> committed/failed 分层,并将同一 delivery 的 accepted correlation 与 committed refs proof 关联。非 202、缺 accepted correlation、accepted 后未 committed 或进入 failed 都是失败/stale;只能修 webhook bridge、inbox worker、受控 mirror、snapshot controller 或 owning YAML,禁止输出或执行人工 REPAIR、mirror sync、直接 Gitea push 等补齐命令。
不得使用 cicd branch-follower status 判定三个已迁移 JD01 consumer 是否最新。该入口只保留历史/迁移用途,可能包含 PaC cutover 前的过期状态。
对已迁移 sentinel CI/CD 的 half-state 诊断,PaC status 命令负责 source/registry/GitOps/Argo/runtime 归因。它必须通过 target 侧短探针区分 registry-missing、GitOps-missing、Argo-pending 和 runtime-mismatch,并使用 registry_probe_base 等 YAML 声明的 probe endpoint;禁止重新把旧 control-plane status 当作主架构,也禁止把 registry manifest HEAD 当作 readiness 校验。
Sentinel 的内部 pac-publish-current capability 由 config/platform-infra/pipelines-as-code.yaml#capabilities.sentinelInternalPublish 单一拥有,当前必须保持 enabled=false 与 admissionProvenance=unavailable。.tekton 继续使用既有 publish-current 自动入口;不得因为 capability 关闭而改成人工 publish。Pod env、ownerReferences、ServiceAccount、label、annotation 与 controller metadata 都是普通 workload 可声明或可修改的运行对象,不能作为 creator proof。只有 #1769 定义的独立 admission-owned provenance、专用最小权限 SA/RBAC 与拒绝 workload 自声明标记全部落地后,才能通过 YAML 评审启用 capability。
PaC history/status 必须把候选 PipelineRun 分成 outer-pac-event、inner-deterministic-publish 与 unknown。只有唯一外层 PaC push event 可以驱动 latest、status、debug-step 或 closeout;内层 deterministic publish 只能作为未绑定的执行观察展示,父子关系缺证据时必须标记 unproven,不得按名称前缀推断。运行对象 metadata 只能支持“已观察到 PaC 形态”的分类,不能宣称 admission provenance 已验证。
Reuse 证据解释
- AgentRun
IMAGE_STATUS=reused表示本次跳过 image build,或复用了同一 env identity 的 registry artifact。 - Sentinel
ENV_REUSE=hit表示依赖可复用;cache=hit是独立的 BuildKit 证据。 - HWLAB
skipped,skip=<n>表示 service-level artifact plan 跳过了可复用 build;判定具体变更 service 时应查看 PipelineRun 详情。
UniDesk Host 运行面变更过滤
unidesk-host consumer 将镜像构建与 GitOps 资源发布分离:
-
config/unidesk-host-k8s.yaml#delivery.changeDetection.sharedImagePaths只声明所有服务镜像共同复制的源码输入; -
delivery.services[].imagePaths只声明对应服务镜像复制的源码输入; -
delivery.gitops.resources每次都从当前 source commit 确定性渲染,再由 Git diff 判定是否发布; -
delivery.services[].gitops.releaseStatePath指向各服务镜像比较基线。 -
build:- 至少一个声明的镜像路径发生变更,或没有可信的历史 release state;
- 流水线构建镜像、更新 manifest 与 release state,然后由 Argo 收敛。
-
skip:- source commit 已推进,但没有声明的镜像路径发生变更;
- 流水线不得构建镜像,应保留既有镜像 source commit 与 digest;
- GitOps 资源仍独立渲染和发布。
-
disabled:delivery.enabled=false。删除生成的 manifest 与 release state;这代表 service retirement,不是skip的同义词。 -
镜像路径边界:
- CLI、文档、issue metadata、GitOps renderer 和交付 helper 不能进入镜像路径列表;
- 否则会把配置发布误判为应用镜像重建。
-
成功跳过镜像构建时:
- 仍应产生一条短 PaC PipelineRun 作为 source observation 证据;
- 允许 GitOps 资源独立形成新 revision;
- 默认
status必须校验保留的 digest、GitOps revision、Argo state、runtime readiness 与 health; - 不得强制镜像 source commit 等于更新的非镜像 source commit;
- 兼容
closeout只能复用同一只读证据。
缺少 reuse 文本不能证明 reuse path 失败,应先核对 consumer type 与 PipelineRun 详情。