# Agent Note: 跨平台串行 CI 参考流程

Status: implemented

[English](2026-07-21-serial-cross-platform-ci-reference.md) | 中文

## 问题

拉取请求工作流将必需检查合并到专用的 Linux 和 Windows 作业中。这些作业仍不应成为唯一的完整性判定基准：如果其门禁清单或依赖图存在缺陷，即使必需聚合结果保持绿灯，也可能漏掉部分工作。

将非 Windows 作业的 1 分钟目标和 Windows 作业的 3 分钟目标写成作业超时，会引入另一种失败模式。托管运行器的启动时间和性能会波动，因此即使门禁本身正确，也可能在到达目标时间边界时被取消，来不及输出有用的诊断信息。性能目标需要根据 GitHub 时间戳衡量，而正确性验证需要给门禁留足完成时间。

评审人还需要直接回答一个更简单的问题：在每个选定的托管操作系统上，如果仓库完整的主 Node CI 聚合流程不使用矩阵选择、分片变量或并发门禁，运行结果会怎样？

真实内核沙箱验证需要特定的托管操作系统和架构，但不会产生拉取请求的合并裁决。在每个拉取请求上重复运行这个包含四个作业的矩阵会消耗 Linux、arm64 Linux 和 macOS 容量，却既不能满足分支保护，也无法参与另一工作流中的必需聚合结果。

## 决策

[CI](../../../../.github/workflows/ci.yml)（仅 pull request）与 [CI master](../../../../.github/workflows/ci-master.yml)（master 推送 + `workflow_dispatch`）为拉取请求事件与 master 推送事件赋予互补的职责。拉取请求在 GitHub 标准托管容量上运行合并后的 Linux 和由 Wine 承载的 Windows 作业，以及 Node 兼容性与 Python 约定；一个独立的原生 Windows 作业会报告完整的 Windows 清单，但不参与必需聚合流程。向 `master` 推送时，当前启用的参考作业是公司自有 `vm-backup` 池上的 `serial / linux (self-hosted standby)` 和 `dsh-win-ci` 池上的 `serial / windows (self-hosted standby)`——这些热备演练持续验证[故障切换手册](2026-07-26-ci-failover-runbook.zh.md)所描述的切换目标。不存在标准托管的 `serial / linux` 定义；标准托管的 `serial / macos` 仍处于禁用状态，并由 `TODO(hosted-serial-ci)` 标记，直到其可移植容量恢复。当前 `serial / windows` 定义是公司自有 `dsh-win-ci` 池的 standby。各自独立的作业定义有意显式保留简短的代码检出、运行时设置和依赖锁定的安装步骤，而不是用矩阵或可复用工作流隐藏操作系统差异。`workflow_dispatch` 仅用于运行器基准测试。

每个参考作业均在不设置任何分片选择器的情况下运行 `pnpm run check:ci`。`DSH_GATE_CONCURRENCY=1` 使顶层聚合每次只执行一个已经就绪的门禁；覆盖率、快照回放、built-bin 冒烟测试和发布验证的 worker 数量也设为 1。各参考作业可以彼此并行，但每台主机上的仓库门禁都串行运行且完整执行。Linux 在回放快照前安装 bubblewrap，Windows 则在安装采用符号链接的工作区前启用开发人员模式。

该完整聚合流程仍明确划分平台归属。`terminal-bash` 支持 Linux 与 macOS，因此其单元测试和逐文件覆盖率约定由 POSIX 平台负责，而不会在 Windows 上加载一个明确拒绝 `win32` 的后端；Windows 仍会执行所有可移植包。可移植 fixture（测试前置数据）通过 `node:path` 派生原生路径，使用与生产代码相同的原生 realpath 实现比较规范化后的路径标识，并采用所有宿主机均允许的文件名。ACP（Agent Client Protocol）快照运行还会把生成的 cwd 分别通过 realpath 的 JavaScript 实现与原生实现得到的两种表示一并传给规范化器；规范化器按长度从长到短替换这些别名，避免 Windows 的短路径与长路径表示差异导致共享 fixture 反复变化。

macOS 参考流程使用 fork 进程运行常规 Vitest 项目。macOS arm64 上的 Node 24 曾在工作线程中执行 CJS 词法分析器时异常终止；进程边界能够隔离这一外部运行时故障，且无需从聚合流程中删除任何测试，而 Linux 与 Windows 仍使用开销更低的线程池。仓库自身引入的竞态均在相应的观测边界修复：开发构建产物的轮询逻辑每次发布重新扫描结果前，都会先暂存候选表、候选图和候选监视基线映射；构建产物缺失后会一直保持脏状态，直到成功计算内容哈希。PTY 就绪检测会在轮询检查前台进程组归属期间保留提示符候选项；常规静默时限也涵盖从交互式子进程继承而来的标记。真实 PTY fixture 会在运行时拼接同步标记，使就绪等待逻辑不会把交互式 shell 的输入回显误判为子进程已就绪。实时链接场景下的包管理器 e2e 会保留由工作流预先准备的 Corepack 主目录、pnpm 元数据缓存和 store 缓存，同时隔离其他包管理器的可变缓存，因此不会在安装前丢弃可复用的包管理器状态。

独立的 [Sandbox](../../../../.github/workflows/sandbox.yml) 工作流属于同一职责划分中的参考侧。其 bwrap、Landlock x64/arm64 与 Seatbelt 真实内核矩阵只在向 `master` 推送后运行。这四个作业仅用于诊断：它们既不是分支保护的必需项，也不会跨工作流计入 `all checks passed`。拉取请求 CI 仍通过常规的单元测试与覆盖率清单检查沙箱源码；宿主内核与 packed-install 验证在合并后报告结果。

master 分支的参考作业仅用于诊断，不参与拉取请求所要求的 `all checks passed` 结果。ci-master 与 Sandbox 工作流把跨平台参考流程保留在 master 推送上。系统根据已完成托管作业的时间戳评估性能，并将其报告为测量结果，而不是写成 `timeout-minutes` 值。

当前启用的参考流程运行在公司自有 `vm-backup`（`serial / linux`）与 `dsh-win-ci`（`serial / windows`）自托管池上；唯一剩余的禁用托管参考作业（`serial-macos`）使用 `macos-latest`，且不存在标准托管的 `serial / linux` 标签。拉取请求必需的 Windows 作业在 `ubuntu-latest` 上通过 Wine 运行，而独立的拉取请求原生作业在正常运行下使用托管的 `dsh-windows-2025-16core` 运行器，故障切换时使用自托管 `[self-hosted, dsh-win-ci, windows]` 池（参见[故障切换手册](2026-07-26-ci-failover-runbook.zh.md)），依据[双 Windows 决策](2026-08-08-native-windows-pull-request-ci.zh.md)不参与必需聚合流程。依据[必需 CI 决策](2026-07-23-portable-required-pull-request-ci.zh.md)，拉取请求必需作业使用可移植的标准容量。更高核心数的托管运行器仍仅用于手动基准测试，因为正确性路径必须无需仓库外部的运行器配置即可运行。

## 曾考虑的替代方案

- **将每个超时值设为相应延迟目标**：不予采纳，因为调度波动会中止原本正确的执行，并使诊断回归所需的证据无法产生。
- **仅信任并发执行的主门禁清单**：不予采纳，因为调度逻辑与校验逻辑共享实现假设；串行聚合流程是一项独立的完整性检查。
- **在每个拉取请求上运行串行参考作业**：不予采纳，因为这些作业会重复完整的跨平台聚合流程，并为每项改动增加 macOS 工作；必需作业已经执行阻塞性的 Linux 和由 Wine 承载的 Windows 约定，而独立原生作业提供完整的 Windows 结果。
- **在每个拉取请求上运行真实内核 Sandbox 矩阵**：不予采纳，因为它的四个状态不参与分支保护，而重复安装、Landlock 构建以及为保持平台一致而运行的 macOS 单元测试会消耗运行器容量，却不会改变合并裁决。master 上的运行保留平台与已安装 launcher 的信号。
- **使用一个操作系统矩阵**：不予采纳，因为三个具名作业无需另一套选择机制，就能让参考流程的构成清晰可见。
- **在大型运行器上运行串行参考流程**：不予采纳，因为当组织自有运行器池无法分配作业时，必需 CI 及其独立参考流程都必须仍可运行。

## 后果

工作流包含重复的设置步骤，master 参考运行也可能比优化后的拉取请求路径耗时长得多。这些重复是有意保留的：评审人无需解析矩阵或并发调度器，就能直接检查每种操作系统执行的完整命令。

参考流程可能暴露某些平台上的故障，而优化后的阻塞门禁集合尚未声明支持这些平台，Windows 尤其如此。这类失败反映了当前的跨平台行为，不应成为削弱或静默跳过该聚合流程的理由。

仅在真实宿主内核或打包后的 Landlock 安装中可见的沙箱回归，可能在 master 上的运行报告前已经合并。我们接受这个合并后检测窗口，以换取从每个拉取请求中移除四个非阻塞作业；默认分支仍保留完整信号。

明确的 `terminal-bash` 归属边界意味着 Windows 不会声称覆盖一个无法加载的后端，而 macOS 采用 fork 的单元测试工作进程会增加进程启动开销。这些代价换来的是：支持范围内的每个方面都有能够如实反映对应平台行为的判据，原生运行时异常终止不会抹掉其余单元测试结果，各项对时序敏感的观测逻辑也都会以调用方有机会修改状态前已建立的状态作为起点。

移除严格的时长超时后，系统会观测到延迟回归，而不是在发生回归时自动取消运行。因此，性能改动必须附带托管环境测量结果，已完成的日志则保留优化最慢通道所需的信息。
