# Agent Note: CI 消费方独立构建

Status: implemented

[English](2026-07-30-independent-ci-consumer-build.md) | 中文

## 问题

[大型运行器拓扑](2026-07-22-evidence-based-larger-hosted-runners.md)将静态门禁清单和构建后消费方清单分配给不同作业，但二者共用的构建由静态作业负责。静态作业要等所有静态门禁完成后才上传生成的目录树，消费方作业则在恢复该目录树前声明了作业级依赖。基于编译输出的快照与发布校验确实需要完整构建，但不依赖运行时依赖闭包检查、文档生成、模块图验证或 Knip。

这项过宽的依赖使运行器可用性成为必需关键链的一环。一次故障切换运行中，静态作业等待运行器 8 分 1 秒，随后运行 1 分 41 秒；直到此时，消费方作业才能进入同一个共享池，它又等待 10 分 34 秒，随后运行 1 分 58 秒。复用静态作业的构建省去了部分仓库工作，却让两次原本相互独立的运行器分配串行发生。

## 决策

3 个必需 Linux 作业分别进入运行器分配。覆盖率仍只消费源码。静态作业负责无需消费生成输出的源码检查与文档检查。消费方作业负责唯一一次 Linux 构建，以及文档类型检查、基于编译输出的快照、发布校验、NodeNext 检查和 built-bin 冒烟测试。

消费方内部的门禁图保留实际依赖关系。构建和只消费源码的 Node 兼容性检查率先启动；publint 等待构建完成，已构建包不变式检查会验证该发布视图，所有编译输出消费方都等待这项验证完成。因此，示例和 Web 快照仍会在普通 Node 下验证当前 `lib/` 输出；同时，没有任何 GitHub 作业需要等待无关作业或传输已构建目录树产物。

Windows 与串行参考聚合流程仍各自负责自身构建。本变更仅涉及拉取请求的必需 Linux 拓扑；`all checks passed` 仍聚合同一批具名作业，任一依赖未成功时都会失败。

## 曾考虑的替代方案

**继续发布静态作业的构建。** 此方案只需构建一次，却无法表达实际的步骤级依赖：GitHub 会让消费方等到整个静态作业结束后才可请求运行器。故障切换池饱和时，再次排队的延迟超过了省下的构建时间。

**在两个作业中分别独立构建。** 在静态作业中保留构建、同时移除作业依赖，可以恢复并行分配，但每个拉取请求都会对同一目录树编译两次。将文档类型检查和构建职责移给消费方，则仍只需构建一次。

**新增专用构建作业。** 职责单一的生产方能让依赖名称与实际关系相符，但会在消费方之前新增第 4 个需要设置和分配运行器的阶段。所有需要持续使用生成输出的任务都已由消费方作业负责，因此单独增设生产方也没有第二个相互独立的消费方。

**仅在故障切换期间合并静态作业与消费方作业。** 单个长作业可以避免第二次分配，但带条件分支的作业清单与结果聚合会形成第二套 CI 拓扑。独立作业能让托管池与故障切换池使用同一作业图。

## 后果

静态作业与消费方作业的排队延迟会相互重叠，不再累加。消费方的活动耗时包含构建；静态作业则变短，产物上传、下载、压缩和解压步骤全部消失。Linux 构建总次数仍为 1 次。

静态作业失败不再阻止消费方清单生成自身证据；最终判定仍会失败。构建与文档类型检查失败会归入 `node 24 / snapshots and artifacts` 而非 `node 24 / static`，这一归类与输出依赖的实际归属一致。
