Portable pull-request CI recovery boundary
拉取请求 CI 的可移植恢复边界
Required pull-request jobs assigned to organization-owned runner labels remain queued when GitHub cannot allocate those pools. The workflow is valid and standard GitHub-hosted jobs can still pass, but `all checks passed` never starts and an otherwise healthy pull request cannot satisfy branch protection. Billing health, a runner definition's `Ready` state, and a large autoscaling ceiling do not prove that a named poo
English
Problem
Required pull-request jobs assigned to organization-owned runner labels remain queued when GitHub cannot allocate those pools. The workflow is valid and standard GitHub-hosted jobs can still pass, but all checks passed never starts and an otherwise healthy pull request cannot satisfy branch protection.
Billing health, a runner definition's Ready state, and a large autoscaling ceiling do not prove that a named pool can receive a job. Required correctness checks need a known portable recovery path even when the ordinary low-latency path depends on repository-external runner provisioning.
Decision
CI (pull-request-only) runs the required primary Node 24 jobs, plus the stable all checks passed aggregate, on repo-restricted enterprise 32-core pools. The aggregate performs no checkout or repository gate, but sharing the enterprise pool prevents the required verdict from introducing a separate standard-hosted billing dependency after its substantive jobs have already succeeded. The required Windows job runs Windows Node under Wine on standard ubuntu-latest for the blocking surfaces; an independent native windows-2025 job starts automatically but does not participate in the aggregate (dual Windows decision). Standard ubuntu-latest jobs retain Node 22.19, Node 26, the Python SDK unit suite, and the release-shaped Linux x64 Python runtime validation, while the serial references (in ci-master.yml) remain the complete unsharded cross-platform definitions. Those standard-hosted jobs keep the portable execution boundary observable without duplicating the primary inventory on every pull request.
The three Linux primary jobs, Node compatibility, Python SDK unit suite, Python runtime validation, and windows node 24 / wine blocking remain dependencies of all checks passed; windows node 24 / native complete is deliberately absent. Branch protection continues to require e2e and all checks passed. There is no automatic fallback when a remaining enterprise Linux label cannot allocate: the standard jobs continue to report their own contracts, but they cannot manufacture the missing required result.
The larger-runner decision owns the current primary topology and its measurements. The serial cross-platform reference remains the independent completeness check, now provided by the self-hosted vm-backup/dsh-win-ci standby lanes on master; the only hosted serial reference is the disabled serial-macos. The manual larger-runner suites retain size comparisons without expanding the ordinary required matrix.
Alternatives considered
Keep the Linux primary jobs and aggregate on standard capacity. This removes the remaining enterprise allocation dependency, but complete standard-runner jobs give materially slower feedback and still experience shared-capacity queues. The current split retains portable compatibility and serial evidence while spending enterprise capacity on the Linux primary critical path.
Select enterprise size from advertised core count. Benchmarks show non-monotonic scaling and setup variance, so exact complete-job measurements choose the required pools instead.
Skip or demote checks while capacity is unavailable. This would make the status green by dropping evidence rather than by running the repository's required contracts.
Use one worker policy on every host. Outer gate concurrency and inner tool workers contend differently on Linux, Windows, and standard runners; measured host-specific bounds avoid turning additional cores into slower execution.
Consequences
Ordinary pull requests spend enterprise capacity on the Linux critical path while the Wine job keeps the required Windows verdict on standard Linux allocation. The independent native job uses standard Windows allocation without delaying or changing the aggregate. A live exact-head run distinguishes the commands branch protection consumes from the separate diagnostic contract; queue delay is reported separately from each job's startedAt to completedAt execution interval.
Standard compatibility, required Wine, and diagnostic native Windows jobs remain useful when enterprise allocation is degraded, but they do not make a blocked required Linux job or aggregate green. Recovering Linux availability may require restoring the complete standard-hosted topology; changing a pool definition's status alone is insufficient evidence that it can receive work.
中文
问题
分配到组织自有运行器标签的拉取请求必需作业,在 GitHub 无法为这些池分配运行器时会持续排队。工作流本身有效,GitHub 标准托管作业仍能通过,但 all checks passed 始终无法启动,原本健康的拉取请求因此无法满足分支保护要求。
账单状态正常、运行器定义处于 Ready 状态以及较高的自动扩缩容上限,都不能证明指定的运行器池可以接收作业。必需的正确性检查需要预先明确一条可移植恢复路径,即使日常低延迟路径依赖仓库外部的运行器预配也不例外。
决策
CI(仅 pull request)在仅限本仓库使用的企业级 32 核运行器池上运行必需的主 Node 24 作业,以及稳定的 all checks passed 聚合流程。该聚合流程不执行代码检出或仓库门禁;但让它与所依赖的实质性作业共用企业级运行器池,可以避免这些作业已经成功后,必需判定结果又引入一项单独的标准托管计费依赖。必需的 Windows 作业在标准 ubuntu-latest 上通过 Wine 运行 Windows Node,覆盖阻断性检查范围;一个独立的原生 windows-2025 作业会自动启动,但不参与聚合流程(双 Windows 决策)。标准 ubuntu-latest 作业保留 Node 22.19、Node 26、Python SDK 单元测试套件与发布形态的 Linux x64 Python 运行时验证,串行参考流程(在 ci-master.yml 中)仍是完整且未分片的跨平台定义。这些标准托管作业让可移植执行边界保持可观测,而不必在每个拉取请求中重复主清单。
三项 Linux 主作业、Node 兼容性、Python SDK 单元测试套件、Python 运行时验证和 windows node 24 / wine blocking 继续作为 all checks passed 的依赖项;windows node 24 / native complete 被刻意排除。分支保护继续要求 e2e 和 all checks passed。剩余的企业级 Linux 运行器标签无法分配运行器时没有自动后备机制:标准作业会继续报告各自的约定,但无法产出缺失的必需结果。
当前主拓扑及其测量结果以大型运行器决策为准。跨平台串行参考流程继续作为独立的完整性检查,现由 master 上公司自有 vm-backup/dsh-win-ci 自托管热备通道提供;仅存的托管串行参考是禁用的 serial-macos。手动大型运行器套件则保留规格比较,同时不扩大普通必需矩阵。
曾考虑的替代方案
将 Linux 主作业和聚合流程保留在标准容量上。 此方案消除了剩余的企业级运行器分配依赖,但标准运行器上的完整作业反馈明显更慢,仍会遇到共享容量排队。当前拆分既保留可移植兼容性和串行证据,又将企业级运行器容量用于 Linux 主关键路径。
根据标称核心数选择企业规格。 基准测试表明扩展效果不呈单调变化,设置耗时也存在波动,因此必需运行器池改由完整作业的精确测量结果选定。
在容量不可用时跳过检查或降低其级别。 这种方式通过丢弃证据而非执行仓库的必需约定来使状态变绿。
在每台主机上使用同一工作线程策略。 外层门禁并发与内层工具工作线程在 Linux、Windows 和标准运行器上的争用方式不同;按主机实测的上限可以避免新增核心反而拖慢执行。
后果
普通拉取请求会将企业级运行器容量用于 Linux 关键路径,而 Wine 作业让必需的 Windows 判定继续使用标准 Linux 运行器容量。独立原生作业使用标准 Windows 运行器容量,不会延迟或改变聚合流程。一次针对确切分支头的实际运行会区分分支保护采用的命令与单独的诊断约定;排队延迟与每个作业从 startedAt 到 completedAt 的执行区间分开报告。
企业级运行器分配能力下降时,标准兼容性作业、必需的 Wine 作业与诊断性原生 Windows 作业仍能提供有用证据,但无法让受阻的必需 Linux 作业或聚合流程变绿。恢复 Linux 可用性时,可能需要恢复完整的标准托管拓扑;仅改变运行器池定义的状态,不足以证明它可以接收作业。