diff --git a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml index 4c325327de..f0213f0864 100644 --- a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md -2026-07-21-serial-cross-platform-ci-reference.md: cfe6fd1028d03056e5ac6da7f014db2ac6db8fab -2026-07-21-serial-cross-platform-ci-reference.zh.md: 8ac7e087c3d015c0ed6bdf71feed9806bd6fb997 +2026-07-21-serial-cross-platform-ci-reference.md: d1ab9590df1252c9c91e7ec53dc1559e221d8f68 +2026-07-21-serial-cross-platform-ci-reference.zh.md: c9ffbac42858a19cca7c5fef6fd8f583030c195d diff --git a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md index cfe6fd1028..d1ab9590df 100644 --- a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md +++ b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.md @@ -16,7 +16,7 @@ Real-kernel sandbox proofs require specific hosted operating systems and archite ## Decision -[CI](../../../../.github/workflows/ci.yml) gives pull-request and master-push events complementary responsibilities. Pull requests run consolidated Linux and Wine-hosted Windows jobs plus the Node compatibility and Python contracts on standard GitHub-hosted capacity; an independent native Windows job reports the complete Windows inventory without participating in the required aggregate. On a push to `master`, the active references are `serial / linux (self-hosted standby)` on the in-house `vm-backup` pool and `serial / windows (self-hosted standby)` on the in-house `dsh-win-ci` pool — the hot-standby drills that continuously re-prove the failover targets described in the [failover runbook](2026-07-26-ci-failover-runbook.md). The standard-hosted `serial / linux`, `serial / macos`, and `serial / windows` definitions remain disabled under `TODO(hosted-serial-ci)` until their portable capacity can be restored. The separate job definitions intentionally keep their short checkout, runtime setup, and immutable install sequences visible instead of hiding operating systems behind a matrix or reusable workflow. `workflow_dispatch` is reserved for runner benchmarks. +[CI](../../../../.github/workflows/ci.yml) gives pull-request and master-push events complementary responsibilities. Pull requests run consolidated Linux and Wine-hosted Windows jobs plus the Node compatibility and Python contracts on standard GitHub-hosted capacity; an independent native Windows job reports the complete Windows inventory without participating in the required aggregate. On a push to `master`, the active references are `serial / linux (self-hosted standby)` on the in-house `vm-backup` pool and `serial / windows (self-hosted standby)` on the in-house `dsh-win-ci` pool — the hot-standby drills that continuously re-prove the failover targets described in the [failover runbook](2026-07-26-ci-failover-runbook.md). There is no standard-hosted `serial / linux` definition; the standard-hosted `serial / macos` remains disabled under `TODO(hosted-serial-ci)` until its portable capacity can be restored. The current `serial / windows` definition is the in-house `dsh-win-ci` standby. The separate job definitions intentionally keep their short checkout, runtime setup, and immutable install sequences visible instead of hiding operating systems behind a matrix or reusable workflow. `workflow_dispatch` is reserved for runner benchmarks. Each reference job runs `pnpm run check:ci` without any shard selector. `DSH_GATE_CONCURRENCY=1` makes the top-level aggregate execute one ready gate at a time; coverage, snapshot replay, built-bin smoke, and publication validation also receive worker counts of one. The reference jobs may run beside one another, but each host's repository gates are serial and complete. Linux installs bubblewrap before replaying snapshots, and Windows enables Developer Mode before installing the symlinked workspace. @@ -28,7 +28,7 @@ The standalone [Sandbox](../../../../.github/workflows/sandbox.yml) workflow bel Master reference jobs are diagnostic and do not participate in the pull request's required `all checks passed` result. The CI and Sandbox workflows keep their cross-platform references on master pushes. Performance is evaluated from completed hosted-job timestamps and reported as a measurement; it is not encoded as a `timeout-minutes` value. -The portable reference uses GitHub's standard `ubuntu-latest`, `macos-latest`, and `windows-2025` labels. The required pull-request Windows job runs under Wine on `ubuntu-latest`, while the independent pull-request native job uses the hosted `dsh-windows-2025-16core` runner under normal operation and the self-hosted `[self-hosted, dsh-win-ci, windows]` pool under failover (see the [failover runbook](2026-07-26-ci-failover-runbook.md)), and is absent from the required aggregate under the [dual Windows decision](2026-08-08-native-windows-pull-request-ci.md); when enabled, `serial / windows` remains a second complete, unsharded native-kernel oracle. Required pull-request jobs use portable standard capacity under the [required-CI decision](2026-07-23-portable-required-pull-request-ci.md). Higher-core hosted runners remain manual benchmarks because a correctness path must remain runnable without repository-external runner configuration. +The active serial references run on the self-hosted `vm-backup` (`serial / linux`) and `dsh-win-ci` (`serial / windows`) pools; the one remaining disabled hosted serial reference (`serial-macos`) uses `macos-latest`, and there is no standard-hosted `serial / linux` label. The required pull-request Windows job runs under Wine on `ubuntu-latest`, while the independent pull-request native job uses the hosted `dsh-windows-2025-16core` runner under normal operation and the self-hosted `[self-hosted, dsh-win-ci, windows]` pool under failover (see the [failover runbook](2026-07-26-ci-failover-runbook.md)), and is absent from the required aggregate under the [dual Windows decision](2026-08-08-native-windows-pull-request-ci.md). Required pull-request jobs use portable standard capacity under the [required-CI decision](2026-07-23-portable-required-pull-request-ci.md). Higher-core hosted runners remain manual benchmarks because a correctness path must remain runnable without repository-external runner configuration. ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md index 8ac7e087c3..c9ffbac428 100644 --- a/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md +++ b/.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md @@ -16,7 +16,7 @@ Status: implemented ## 决策 -[CI](../../../../.github/workflows/ci.yml) 为拉取请求事件与 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.md)所描述的切换目标。标准托管的 `serial / linux`、`serial / macos` 和 `serial / windows` 定义仍处于禁用状态,并由 `TODO(hosted-serial-ci)` 标记,直到其可移植容量恢复。各自独立的作业定义有意显式保留简短的代码检出、运行时设置和依赖锁定的安装步骤,而不是用矩阵或可复用工作流隐藏操作系统差异。`workflow_dispatch` 仅用于运行器基准测试。 +[CI](../../../../.github/workflows/ci.yml) 为拉取请求事件与 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.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 则在安装采用符号链接的工作区前启用开发人员模式。 @@ -28,7 +28,7 @@ macOS 参考流程使用 fork 进程运行常规 Vitest 项目。macOS arm64 上 master 分支的参考作业仅用于诊断,不参与拉取请求所要求的 `all checks passed` 结果。CI 与 Sandbox 工作流把跨平台参考流程保留在 master 推送上。系统根据已完成托管作业的时间戳评估性能,并将其报告为测量结果,而不是写成 `timeout-minutes` 值。 -可移植的参考流程使用 GitHub 标准的 `ubuntu-latest`、`macos-latest` 和 `windows-2025` 标签。拉取请求必需的 Windows 作业在 `ubuntu-latest` 上通过 Wine 运行,而独立的拉取请求原生作业在正常运行下使用托管的 `dsh-windows-2025-16core` 运行器,故障切换时使用自托管 `[self-hosted, dsh-win-ci, windows]` 池(参见[故障切换手册](2026-07-26-ci-failover-runbook.md)),依据[双 Windows 决策](2026-08-08-native-windows-pull-request-ci.md)不参与必需聚合流程;`serial / windows` 启用时,仍作为第二个完整且未分片的原生内核标尺。依据[必需 CI 决策](2026-07-23-portable-required-pull-request-ci.md),拉取请求必需作业使用可移植的标准容量。更高核心数的托管运行器仍仅用于手动基准测试,因为正确性路径必须无需仓库外部的运行器配置即可运行。 +当前启用的参考流程运行在公司自有 `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.md)),依据[双 Windows 决策](2026-08-08-native-windows-pull-request-ci.md)不参与必需聚合流程。依据[必需 CI 决策](2026-07-23-portable-required-pull-request-ci.md),拉取请求必需作业使用可移植的标准容量。更高核心数的托管运行器仍仅用于手动基准测试,因为正确性路径必须无需仓库外部的运行器配置即可运行。 ## 曾考虑的替代方案 diff --git a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml index 3bc9415b40..8ac0ed6dbe 100644 --- a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md -2026-07-22-evidence-based-larger-hosted-runners.md: b3310988decb2916ac895aaf154dbc106c51ed48 -2026-07-22-evidence-based-larger-hosted-runners.zh.md: 2d408173a657c77add750a53eaee4ecb9177919c +2026-07-22-evidence-based-larger-hosted-runners.md: db08a4eb9812a2cb13499718cbd3ceb023960c06 +2026-07-22-evidence-based-larger-hosted-runners.zh.md: 1b9b622e929a693d165d23ce256ba4d3c85a0460 diff --git a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md index b3310988de..db08a4eb98 100644 --- a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md +++ b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.md @@ -14,7 +14,7 @@ Larger runners make it possible to pay setup once and parallelize inside the rep The enterprise keeps repo-restricted x64 larger-runner pools for Ubuntu and Windows. Ordinary pull requests run the three primary Linux jobs on the 16-core Ubuntu 24.04 pool and the independent native Windows signal on the 16-core Windows 2025 pool. The required Wine signal remains on standard hosted Linux. Public IPs are disabled, and workflow concurrency remains bounded because an autoscaling ceiling neither allocates idle machines nor makes repository work scale without limit. -The required primary path depends on those enterprise pools. Standard GitHub-hosted jobs retain the Node 22.19, Node 26, and Python SDK compatibility contracts, while the [portable recovery boundary](2026-07-23-portable-required-pull-request-ci.md) and [serial reference](2026-07-21-serial-cross-platform-ci-reference.md) keep complete standard-runner evidence available on `master`. `suite=larger-runner-benchmark` compares isolated critical lanes across provisioned sizes, and `suite=consolidated-runner-benchmark` compares whole aggregates. Each benchmark reports its observed processor and memory capacity before running repository work. +The required primary path depends on those enterprise pools. Standard GitHub-hosted jobs retain the Node 22.19, Node 26, and Python SDK compatibility contracts, while the [portable recovery boundary](2026-07-23-portable-required-pull-request-ci.md) and [serial reference](2026-07-21-serial-cross-platform-ci-reference.md) keep complete-aggregate evidence available on `master` through the self-hosted standby pools (no Linux hosted serial reference remains). `suite=larger-runner-benchmark` compares isolated critical lanes across provisioned sizes, and `suite=consolidated-runner-benchmark` compares whole aggregates. Each benchmark reports its observed processor and memory capacity before running repository work. The former gate-level and coarse primary shard jobs are absent from the workflow. Their workflow-facing static, lint, coverage, snapshot, and scenario selectors are also absent, so an unused diagnostic path cannot preserve a second CI architecture. Instrumented coverage may use [process-local partitions inside its existing job](2026-08-18-in-job-partitioned-coverage.md); that coordinator neither selects workflow jobs nor transfers reports between runners. @@ -50,9 +50,9 @@ Inner and outer worker limits are separate controls. An exact-head 32-worker ESL The process-bound coverage project contains exactly five suite files. Thirty-two forks crashed Node 24's CJS lexer twice, and a later 16-fork run reproduced the worker loss and invalid coverage result. The single Vitest invocation therefore uses threads for the broad inventory and reserves forks for suites that exercise process-global state, `process` APIs, or timing-sensitive process I/O. That narrow fork inventory includes the local bash process-plumbing suite and the pi-ai adapter suite because aggregate contention changed timing observations in both. These failures make deterministic coverage, not advertised cores, the upper bound on worker selection. -Complete serial Linux, macOS, and Windows references run only when `master` moves. Pull requests use the enterprise required path plus standard-hosted compatibility jobs, while other larger-runner sizes run only by manual dispatch. +The self-hosted serial Linux and Windows standby references and the disabled `serial-macos` job exist. Pull requests use the enterprise required path plus standard-hosted compatibility jobs, while other larger-runner sizes run only by manual dispatch. -An additional serial Linux reference runs on the in-house self-hosted pool (`vm-backup` label: a 64-core VM with six always-on systemd-managed runner instances) on every `master` push. It is a hot-standby drill, not a required check: each run re-proves that the persistent VM can execute the complete unsharded aggregate. The actual switch is pre-wired: the three required Linux jobs resolve their pool through the writer-manageable `DSH_CI_FAILOVER_LINUX` repository variable, so an outage response is setting one variable and re-running — no merge, which would be deadlocked behind the failing checks themselves ([runbook](2026-07-26-ci-failover-runbook.md)). The standby lane is push-triggered, so it always executes the base branch's workflow definition. Under failover, however, `pull_request` jobs do reach these runners with the PR merge ref's own workflow definition — the trust boundary is repository membership (the repository is private with forking disabled, and the selectors exclude Dependabot), as the [failover runbook](2026-07-26-ci-failover-runbook.md) records. +The self-hosted serial Linux reference runs on the in-house self-hosted pool (`vm-backup` label: a 64-core VM with six always-on systemd-managed runner instances) on every `master` push. It is a hot-standby drill, not a required check: each run re-proves that the persistent VM can execute the complete unsharded aggregate. The actual switch is pre-wired: the three required Linux jobs resolve their pool through the writer-manageable `DSH_CI_FAILOVER_LINUX` repository variable, so an outage response is setting one variable and re-running — no merge, which would be deadlocked behind the failing checks themselves ([runbook](2026-07-26-ci-failover-runbook.md)). The standby lane is push-triggered, so it always executes the base branch's workflow definition. Under failover, however, `pull_request` jobs do reach these runners with the PR merge ref's own workflow definition — the trust boundary is repository membership (the repository is private with forking disabled, and the selectors exclude Dependabot), as the [failover runbook](2026-07-26-ci-failover-runbook.md) records. ## Alternatives considered @@ -70,7 +70,7 @@ An additional serial Linux reference runs on the in-house self-hosted pool (`vm- **Publish the static job's build to post-build consumers.** A run-scoped artifact preserves one exact build, but the workflow can only consume it by waiting for the entire static job and then requesting another runner. The [independent consumer build](2026-07-30-independent-ci-consumer-build.md) assigns the single Linux build to its actual consumers instead. -**Keep the complete required path on standard GitHub-hosted capacity.** This avoids repository-external runner configuration, but exact-head standard-runner runs remain materially slower and can spend longer queued behind shared capacity. Standard-hosted compatibility and serial references preserve portable evidence without making that slower topology the ordinary primary path. +**Keep the complete required path on standard GitHub-hosted capacity.** This avoids repository-external runner configuration, but exact-head standard-runner runs remain materially slower and can spend longer queued behind shared capacity. Standard-hosted compatibility jobs preserve portable evidence, while the self-hosted serial standby preserves complete-aggregate evidence, without making that slower topology the ordinary primary path. **Keep blocking and observational native Windows checks in separate jobs.** This would preserve their distinction at the workflow level but pay Windows setup twice. `run-gates` preserves the same blocking versus observational result inside one job. diff --git a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md index 2d408173a6..1b9b622e92 100644 --- a/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md +++ b/.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md @@ -14,7 +14,7 @@ Status: implemented 企业保留仅限本仓库使用的 Ubuntu 和 Windows x64 大型运行器池。普通拉取请求在 16 核 Ubuntu 24.04 池上运行 3 个 Linux 主作业,并在 16 核 Windows 2025 池上运行独立的原生 Windows 信号。必需的 Wine 信号仍位于标准托管 Linux。公网 IP 已禁用;工作流并发仍设有边界,因为自动扩缩容上限既不会分配闲置机器,也不意味着仓库工作可以无限扩展。 -必需主路径依赖这些企业级运行器池。GitHub 标准托管作业保留 Node 22.19、Node 26 和 Python SDK 兼容性约定,而[可移植恢复边界](2026-07-23-portable-required-pull-request-ci.md)与[串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)则在 `master` 上持续提供完整的标准运行器证据。`suite=larger-runner-benchmark` 比较已预配规格上相互独立的关键通道,`suite=consolidated-runner-benchmark` 则比较完整聚合流程。每项基准测试都会先报告实测的处理器和内存容量,再运行仓库工作。 +必需主路径依赖这些企业级运行器池。GitHub 标准托管作业保留 Node 22.19、Node 26 和 Python SDK 兼容性约定,而[可移植恢复边界](2026-07-23-portable-required-pull-request-ci.md)与[串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)则在 `master` 上通过自托管热备池持续提供完整聚合流程证据(不存在托管的 Linux 串行参考)。`suite=larger-runner-benchmark` 比较已预配规格上相互独立的关键通道,`suite=consolidated-runner-benchmark` 则比较完整聚合流程。每项基准测试都会先报告实测的处理器和内存容量,再运行仓库工作。 原有的门禁级和粗粒度主流程分片 job 已从工作流中移除。面向工作流的静态、lint、覆盖率、快照和场景选择器也已移除,因此未使用的诊断路径无法继续维系第二套 CI 架构。插桩覆盖率可以在[既有 job 内使用进程本地分区](2026-08-18-in-job-partitioned-coverage.md);该协调器既不选择工作流 job,也不在 runner 之间传输报告。 @@ -50,9 +50,9 @@ Windows 仓库工作在超过 16 核后收益很小。原生通道把阻断性 进程约束的覆盖率项目恰好包含 5 个套件文件。32 个 fork 曾两次导致 Node 24 的 CJS 词法分析器崩溃,后来一次使用 16 个 fork 的运行又复现了工作进程丢失和无效的覆盖率结果。因此,单次 Vitest 调用会对大范围测试清单使用线程,只为涉及进程全局状态、`process` API 或对时间敏感的进程 I/O 的套件保留 fork。这份有限的 fork 清单包括本地 bash 进程通路套件和 pi-ai 适配器套件,因为聚合争用改变了二者的时序观测结果。这些故障表明,选择工作线程数量时,上限取决于覆盖率结果能否保持确定性,而非标称核心数。 -只有在 `master` 移动时,才运行完整的 Linux、macOS 和 Windows 串行参考。拉取请求使用企业级运行器必需路径和标准托管兼容性作业,其他大型运行器规格仅通过手动触发运行。 +自托管的 Linux 与 Windows 串行热备参考,以及被禁用的 `serial-macos` 任务仍然存在。拉取请求使用企业级运行器必需路径和标准托管兼容性作业,其他大型运行器规格仅通过手动触发运行。 -另有一条串行 Linux 参考在每次 `master` 推送时运行于公司自有的自托管池(`vm-backup` 标签:一台 64 核虚拟机,运行 6 个常驻的 systemd 管理运行器实例)。它是热备演练而非必需检查:每次运行都重新证明这台持久化虚拟机能够执行完整的未分片聚合流程。实际切换机制已预先布线:三个必需 Linux 作业通过写入权限持有者可管理的仓库变量 `DSH_CI_FAILOVER_LINUX` 解析运行器池,因此故障响应就是设置一个变量并重跑——无需合并(合并本身会被正在失败的检查阻塞,形成死锁)([切换手册](2026-07-26-ci-failover-runbook.md))。该热备通道由 push 触发,执行的始终是基础分支自身的工作流定义。但需要注意:故障切换期间,`pull_request` 作业确实会带着 PR merge 引用自带的工作流定义到达这些运行器——信任边界是仓库成员资格(仓库为私有且禁用 fork,选择器排除 Dependabot),详见[故障切换手册](2026-07-26-ci-failover-runbook.md)的记录。 +自托管的串行 Linux 参考在每次 `master` 推送时运行于公司自有的自托管池(`vm-backup` 标签:一台 64 核虚拟机,运行 6 个常驻的 systemd 管理运行器实例)。它是热备演练而非必需检查:每次运行都重新证明这台持久化虚拟机能够执行完整的未分片聚合流程。实际切换机制已预先布线:三个必需 Linux 作业通过写入权限持有者可管理的仓库变量 `DSH_CI_FAILOVER_LINUX` 解析运行器池,因此故障响应就是设置一个变量并重跑——无需合并(合并本身会被正在失败的检查阻塞,形成死锁)([切换手册](2026-07-26-ci-failover-runbook.md))。该热备通道由 push 触发,执行的始终是基础分支自身的工作流定义。但需要注意:故障切换期间,`pull_request` 作业确实会带着 PR merge 引用自带的工作流定义到达这些运行器——信任边界是仓库成员资格(仓库为私有且禁用 fork,选择器排除 Dependabot),详见[故障切换手册](2026-07-26-ci-failover-runbook.md)的记录。 ## 曾考虑的替代方案 @@ -70,7 +70,7 @@ Windows 仓库工作在超过 16 核后收益很小。原生通道把阻断性 **将静态作业的构建发布给构建后消费方。** 仅供本次运行使用的产物能保留同一份构建结果,但工作流要消费它,只能先等待整个静态作业完成,再请求另一台运行器。[消费方独立构建](2026-07-30-independent-ci-consumer-build.md)则转而让实际消费方负责唯一一次 Linux 构建。 -**将完整必需路径保留在 GitHub 标准托管容量上。** 此方案可以避免依赖仓库外部的运行器配置,但标准运行器上的分支头精确运行仍明显更慢,也可能因共享容量而排队更久。标准托管兼容性作业和串行参考流程保留可移植证据,无需让这套较慢的拓扑成为普通主路径。 +**将完整必需路径保留在 GitHub 标准托管容量上。** 此方案可以避免依赖仓库外部的运行器配置,但标准运行器上的分支头精确运行仍明显更慢,也可能因共享容量而排队更久。标准托管兼容性作业保留可移植证据,自托管串行热备则保留完整聚合流程证据,无需让这套较慢的拓扑成为普通主路径。 **把阻断性与观测性原生 Windows 检查放在不同 job。** 此方案会在工作流层面保留二者的区别,却要承担两次 Windows 设置开销。`run-gates` 在一个 job 内保留了相同的阻断与观测结果。 diff --git a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml index 91879fb4d5..5b9f89e609 100644 --- a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md -2026-07-23-portable-required-pull-request-ci.md: 418f996383ebc08e78cb5b061bfde4b90dd89495 -2026-07-23-portable-required-pull-request-ci.zh.md: ada3cc52e3f6436269969a3ff93b7a2616353337 +2026-07-23-portable-required-pull-request-ci.md: 740ef20d6b7a1edc1a010f37bcbcddab2981e8ad +2026-07-23-portable-required-pull-request-ci.zh.md: 5b24af59fb13e32a642e15c79aee3910601ad45b diff --git a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md index 418f996383..740ef20d6b 100644 --- a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md +++ b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.md @@ -16,7 +16,7 @@ Billing health, a runner definition's `Ready` state, and a large autoscaling cei 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](2026-07-22-evidence-based-larger-hosted-runners.md) owns the current primary topology and its measurements. The [serial cross-platform reference](2026-07-21-serial-cross-platform-ci-reference.md) remains the independent standard-hosted completeness check, and the manual larger-runner suites retain size comparisons without expanding the ordinary required matrix. +The [larger-runner decision](2026-07-22-evidence-based-larger-hosted-runners.md) owns the current primary topology and its measurements. The [serial cross-platform reference](2026-07-21-serial-cross-platform-ci-reference.md) 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 diff --git a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md index ada3cc52e3..5b24af59fb 100644 --- a/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md +++ b/.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md @@ -16,7 +16,7 @@ Status: implemented 三项 Linux 主作业、Node 兼容性、Python SDK 单元测试套件、Python 运行时验证和 `windows node 24 / wine blocking` 继续作为 `all checks passed` 的依赖项;`windows node 24 / native complete` 被刻意排除。分支保护继续要求 `e2e` 和 `all checks passed`。剩余的企业级 Linux 运行器标签无法分配运行器时没有自动后备机制:标准作业会继续报告各自的约定,但无法产出缺失的必需结果。 -当前主拓扑及其测量结果以[大型运行器决策](2026-07-22-evidence-based-larger-hosted-runners.md)为准。[跨平台串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)继续作为独立的标准托管完整性检查,手动大型运行器套件则保留规格比较,同时不扩大普通必需矩阵。 +当前主拓扑及其测量结果以[大型运行器决策](2026-07-22-evidence-based-larger-hosted-runners.md)为准。[跨平台串行参考流程](2026-07-21-serial-cross-platform-ci-reference.md)继续作为独立的完整性检查,现由 `master` 上公司自有 `vm-backup`/`dsh-win-ci` 自托管热备通道提供;仅存的托管串行参考是禁用的 `serial-macos`。手动大型运行器套件则保留规格比较,同时不扩大普通必需矩阵。 ## 曾考虑的替代方案 diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml index 392c4f54f2..c75e36f054 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md -2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 499141ca6a3703a12d10c195b732441d49419599 -2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 0b1e280db6e4e04cc4a210203e97026174fc02f6 +2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 31d1ebf009a6e044722081985546e87b9d4ae0e2 +2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 84445abfdd42671f2f5c9de8403ed7891f9cc292 diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md index 499141ca6a..31d1ebf009 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md @@ -13,7 +13,7 @@ Outside `landlock-run.yml`, each workflow that installed pnpm hand-provisioned i `pnpm/action-setup@v4` is the only pnpm provisioning mechanism in CI: no workflow runs `corepack enable`. The root dev dependency on `@yarnpkg/cli-dist` separately supplies the modern Yarn CLI exercised by the generated-project e2e; package-manager coverage therefore does not inherit the runner image's Yarn Classic. Caching remains per-job policy on top of pnpm provisioning, in three deliberate shapes: - **Symmetric cache** (restore and save): `actions/setup-node` with `cache: pnpm` — `e2e.yml`, `docs-pages.yml`, `pi-ai-provider-e2e.yml`, `build-exe-for-python-sdk.yml`, and the node-compat and two benchmark jobs of `ci.yml`. The larger-runner benchmark keeps its store cache Linux-only through a conditional `cache:` input; the consolidated benchmark caches on both platforms. -- **Restore-only / producer pairing** (hand-rolled `actions/cache` steps): the three enterprise-runner PR jobs and the Wine-based required Windows job restore without saving, keeping cache compression/upload off their latency-sensitive paths — an asymmetry `setup-node`'s cache cannot express. Each configures a store outside the action's replaceable install directory and resolves that path, matching the master-push serial-linux producer's path and exact key; the enterprise jobs skip restore during self-hosted failover because that VM's persistent store is already warm. +- **Restore-only caching** (hand-rolled `actions/cache` steps): the three enterprise-runner PR jobs and the Wine-based required Windows job restore without saving, keeping cache compression/upload off their latency-sensitive paths — an asymmetry `setup-node`'s cache cannot express. Each configures a store outside the action's replaceable install directory and resolves that path. No master job produces these hosted caches, so these restores hit matching archived entries until they evict. The enterprise jobs skip restore during self-hosted failover because that VM's persistent store is already warm. - **Cache-less or persistent** (no store-cache action): the independent native Windows job, native serial-windows and serial-macos, plus `sandbox.yml` install from a cold or runner-local store. Extracting the many-file pnpm store costs more than a clean Windows install; the self-hosted standby and failover jobs instead reuse their VM's persistent pnpm store without transferring a hosted cache archive. ## Alternatives considered @@ -31,4 +31,4 @@ Outside `landlock-run.yml`, each workflow that installed pnpm hand-provisioned i - The generated-project e2e runs the root-pinned Yarn 4 CLI instead of inheriting or silently skipping the runner image's Yarn version. - The cache-key format changed once for converted lanes; one cold run repopulated it, after which hit rates match the old steps. The built-in key spans platform, arch, and the lockfile hash but not the Node version, so the node-compat matrix legs share one store entry — safe, because the pnpm store is Node-version-independent. - `setup-node`'s built-in pnpm cache restores by exact key only, with no `restore-keys` prefix fallback: a `pnpm-lock.yaml` change starts a converted lane from a cold store instead of seeding from the previous entry. -- `pnpm/action-setup` deletes its install directory on every run and places the default store beneath the resulting `PNPM_HOME`. Linux jobs that need cache pairing or self-hosted persistence therefore set `PNPM_CONFIG_STORE_DIR` to `$HOME/.local/share/pnpm/store`, outside the action directory; the restore-only jobs and serial-linux resolve and share that stable path and exact key. +- `pnpm/action-setup` deletes its install directory on every run and places the default store beneath the resulting `PNPM_HOME`. Linux jobs that need hosted cache restores or self-hosted persistence therefore set `PNPM_CONFIG_STORE_DIR` to `$HOME/.local/share/pnpm/store`, outside the action directory; the restore-only jobs resolve that stable path and exact key. diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md index 0b1e280db6..84445abfdd 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md @@ -13,7 +13,7 @@ Status: implemented `pnpm/action-setup@v4` 是 CI 中提供 pnpm 的唯一机制:没有任何工作流运行 `corepack enable`。根目录的 `@yarnpkg/cli-dist` 开发依赖另行提供 generated-project e2e 所运行的现代 Yarn CLI(命令行界面);因此,用于包管理器覆盖率的 Yarn 不会沿用 runner 镜像里的 Yarn Classic。缓存仍是叠加在 pnpm 提供机制上的按作业策略,保留三种有意采用的形态: - **对称缓存**(既恢复也保存):带 `cache: pnpm` 的 `actions/setup-node`——`e2e.yml`、`docs-pages.yml`、`pi-ai-provider-e2e.yml`、`build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat 与两个 benchmark 作业。larger-runner benchmark 通过条件化的 `cache:` 输入让 store 缓存仅限 Linux;consolidated benchmark 在两个平台上都启用缓存。 -- **只恢复不上传/生产者配对**(手写的 `actions/cache` 步骤):企业 runner 上的三个 PR(Pull Request)作业和基于 Wine 的必需 Windows 作业只恢复不保存,把缓存压缩/上传挡在它们的延迟敏感路径之外——这种不对称是 `setup-node` 的缓存无法表达的。每个作业都在 action 可替换的安装目录之外配置 store,并解析该路径,从而与 master 推送触发的 serial-linux 生产者所用的路径和精确键匹配;企业作业在自托管故障切换期间跳过恢复,因为该 VM 的持久 store 已经预热。 +- **只恢复不上传**(手写的 `actions/cache` 步骤):企业 runner 上的三个 PR(Pull Request)作业和基于 Wine 的必需 Windows 作业只恢复不保存,把缓存压缩/上传挡在它们的延迟敏感路径之外——这种不对称是 `setup-node` 的缓存无法表达的。每个作业都在 action 可替换的安装目录之外配置 store,并解析该路径。没有任何 master 作业生产这些 hosted 缓存,这些恢复步骤只能命中仍有归档的旧条目,直至其被逐出;企业作业在自托管故障切换期间跳过恢复,因为该 VM 的持久 store 已经预热。 - **无缓存或持久化**(不使用 store 缓存 action):独立的原生 Windows 作业、原生 serial-windows 和 serial-macos,以及 `sandbox.yml` 均从冷 store 或 runner 本地 store 安装。解压含有大量文件的 pnpm store,成本高于在 Windows 上进行一次全新安装;自托管热备与故障切换作业则复用其 VM 的持久 pnpm store,不传输托管缓存归档。 ## 曾考虑的替代方案 @@ -31,4 +31,4 @@ Status: implemented - generated-project e2e 运行根目录锁定的 Yarn 4 CLI,既不再沿用 runner 镜像中的 Yarn 版本,也不会因此悄然跳过。 - 已转换泳道的缓存键格式变更了一次;各跑一次冷运行重建缓存后,命中率与旧步骤持平。内建缓存键涵盖平台、架构与锁文件哈希,但不含 Node 版本,因此 node-compat 的各个矩阵任务共享同一条 store 缓存记录——这是安全的,因为 pnpm store 与 Node 版本无关。 - `setup-node` 内建的 pnpm 缓存只按精确键恢复,没有 `restore-keys` 前缀回退:`pnpm-lock.yaml` 一旦变更,已转换泳道会从冷 store 起步,而不是利用上一条缓存记录预填充。 -- `pnpm/action-setup` 每次运行都会删除其安装目录,并把默认 store 放在由此产生的 `PNPM_HOME` 下。因此,需要缓存配对或自托管持久化的 Linux 作业会把 `PNPM_CONFIG_STORE_DIR` 设为 `$HOME/.local/share/pnpm/store`,置于 action 目录之外;只恢复不上传的作业与 serial-linux 会解析并共享这一稳定路径及精确键。 +- `pnpm/action-setup` 每次运行都会删除其安装目录,并把默认 store 放在由此产生的 `PNPM_HOME` 下。因此,需要 hosted 缓存恢复或自托管持久化的 Linux 作业会把 `PNPM_CONFIG_STORE_DIR` 设为 `$HOME/.local/share/pnpm/store`,置于 action 目录之外;只恢复不上传的作业会解析这一稳定路径及精确键。 diff --git a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml index d5bc176adc..419e81704b 100644 --- a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml +++ b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md -2026-07-24-web-gui-browser-e2e-lane.md: afe3c937a733ed50073ada5c9c6f40c6c93c6641 -2026-07-24-web-gui-browser-e2e-lane.zh.md: 57a4d3ae5fba7c10211587e36ce9e41e7e985d7e +2026-07-24-web-gui-browser-e2e-lane.md: a477a887e679d455376e69ef0c0fae58dc8dde69 +2026-07-24-web-gui-browser-e2e-lane.zh.md: 1f9504df6d49061a5b4759e65dcbbeeb8d9c7402 diff --git a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md index afe3c937a7..a477a887e6 100644 --- a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md +++ b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md @@ -46,7 +46,7 @@ The lane covers three behavior families. Live-turn scenarios pin ordinary tool e ### CI stance -The lane is a required compare-only gate for Linux pull requests under the [browser snapshot CI decision](2026-07-30-web-browser-snapshot-ci-gate.md). The `node 24 / snapshots and artifacts` consumer job owns the [single Linux build](../process/2026-07-30-independent-ci-consumer-build.md), installs the lockfile-selected Chromium, restores its OS-and-lockfile-keyed cache, and runs the lane with `DSH_SNAPSHOT=replay`. This is an intentional plane split: the host and specs use the [tsx source-launch contract](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md), while the browser consumes `apps/web/dist` and package `lib/client.js` artifacts, so the gate depends on `built-package-invariants` for those client artifacts. The hosted and self-hosted default-branch Linux serial jobs run the same gate; the hosted job produces the browser cache consumed by pull requests, while the persistent self-hosted pool needs no hosted cache. CI never records or refreshes goldens. Scenarios remain POSIX-oriented and stay outside the Windows and macOS matrices. +The lane is a required compare-only gate for Linux pull requests under the [browser snapshot CI decision](2026-07-30-web-browser-snapshot-ci-gate.md). The `node 24 / snapshots and artifacts` consumer job owns the [single Linux build](../process/2026-07-30-independent-ci-consumer-build.md), installs the lockfile-selected Chromium, restores its OS-and-lockfile-keyed cache, and runs the lane with `DSH_SNAPSHOT=replay`. This is an intentional plane split: the host and specs use the [tsx source-launch contract](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md), while the browser consumes `apps/web/dist` and package `lib/client.js` artifacts, so the gate depends on `built-package-invariants` for those client artifacts. The self-hosted default-branch Linux serial standby runs the same gate; there is no hosted Linux serial producer of the browser cache consumed by pull requests, and the persistent self-hosted pool needs no hosted cache. CI never records or refreshes goldens. Scenarios remain POSIX-oriented and stay outside the Windows and macOS matrices. High-cardinality performance diagnostics use the separate opt-in `apps/web/tests/**/*.perf.ts` inventory selected only by `vitest.web.perf.config.ts`. The isolated `complex-history.perf.ts` cases reuse the real scaffold: the workspace case seeds 1,000 compact sessions plus one 500-turn history containing 500 tool calls, exhausts and remounts that history in Chat, and reports Chromium main-thread, DOM, listener, heap, paging, search, and Trajectory measurements. Two continuation cases seed the same long history but compare the default 24-turn Chat window with all 500 turns expanded before each continues eight identical turns through the real composer, agent loop, SSE wire, tools, and persistence; two turns execute a real `bash` call and assert its durable result, while the final turn fills an 8,232-character mixed-language prompt and replays 120 paced text deltas. A separate soak case starts from a blank session, drives 100 consecutive real composer turns with a `bash` call and result every tenth turn, forces GC every ten turns, and reports ten-turn latency windows plus retained browser state. It then submits a 101st text-only turn with a trusted browser click and measures browser-clock send-to-transcript-DOM and send-to-post-paint latency, excluding the composer's draft mirrors, separately from full-turn completion. Per-turn diagnostics cover composer fill, click-to-user-echo, click-to-first-chunk, completion, browser mutations, persisted chunks, and tool events; the synthetic replay model has enough context capacity to keep fixture cardinality stable instead of consuming scripted calls through compaction. Structural assertions pin the intended load, stream, and tool shapes, but timing remains threshold-free because machine speed is not a correctness contract. The required `vitest.web.config.ts` inventory remains limited to `*.e2e.ts` and `*.snapshot.ts`, so neither `test:web:built` nor its CI gate collects performance cases. diff --git a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md index 57a4d3ae5f..1f9504df6d 100644 --- a/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md +++ b/.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md @@ -46,7 +46,7 @@ Web GUI 以一条真实组装链交付——chromium 页面 → client 插件 bu ### CI 立场 -根据[浏览器快照 CI 决策](2026-07-30-web-browser-snapshot-ci-gate.md),该车道是 Linux 拉取请求必需的只比较门禁。`node 24 / snapshots and artifacts` 消费方任务在[消费方独立构建](../process/2026-07-30-independent-ci-consumer-build.md)中负责唯一一次 Linux 构建,安装锁文件选定的 Chromium,恢复以操作系统和锁文件为键的缓存,并用 `DSH_SNAPSHOT=replay` 运行该车道。这是有意的平面切分:host 与 spec 使用 [tsx 源码启动约定](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md),浏览器则消费 `apps/web/dist` 和包的 `lib/client.js` 产物,因此门禁依赖 `built-package-invariants` 提供这些客户端产物。托管和自托管的默认分支 Linux 串行任务运行同一门禁;托管任务生成供 PR 消费的浏览器缓存,持久化自托管池则不需要托管侧缓存。CI 从不录制或刷新预期输出。场景仍面向 POSIX,并继续置于 Windows 和 macOS 矩阵之外。 +根据[浏览器快照 CI 决策](2026-07-30-web-browser-snapshot-ci-gate.md),该车道是 Linux 拉取请求必需的只比较门禁。`node 24 / snapshots and artifacts` 消费方任务在[消费方独立构建](../process/2026-07-30-independent-ci-consumer-build.md)中负责唯一一次 Linux 构建,安装锁文件选定的 Chromium,恢复以操作系统和锁文件为键的缓存,并用 `DSH_SNAPSHOT=replay` 运行该车道。这是有意的平面切分:host 与 spec 使用 [tsx 源码启动约定](../architecture/2026-07-29-dsh-source-launch-tsx-esm.md),浏览器则消费 `apps/web/dist` 和包的 `lib/client.js` 产物,因此门禁依赖 `built-package-invariants` 提供这些客户端产物。自托管的默认分支 Linux 串行热备运行同一门禁;不存在托管 Linux 串行生产者生成供 PR 消费的浏览器缓存,持久化自托管池则不需要托管侧缓存。CI 从不录制或刷新预期输出。场景仍面向 POSIX,并继续置于 Windows 和 macOS 矩阵之外。 高基数性能诊断使用单独按需启用的 `apps/web/tests/**/*.perf.ts` 清单,并且只由 `vitest.web.perf.config.ts` 选中。`complex-history.perf.ts` 的隔离用例复用真实 scaffold:工作区用例播种 1,000 个紧凑会话以及一份包含 500 次工具调用的 500 轮次历史,在 Chat 中穷尽并重新挂载该历史,并报告 Chromium 主线程、DOM、监听器、堆内存、分页、搜索和 Trajectory 测量结果。两个续聊用例播种同一份长历史,但比较默认的 24 轮次 Chat 窗口与展开全部 500 轮次的状态,然后各自通过真实输入框、agent loop、SSE wire、工具和持久化继续进行 8 个相同轮次;其中两轮执行真实 `bash` 调用并断言其持久化结果,最后一轮则填入一条包含 8,232 个字符的混合语言提示词,并回放 120 个带节奏的文本增量。一个单独的 soak 用例从空白会话开始,通过真实输入框连续驱动 100 轮,每第 10 轮执行一次 `bash` 调用并产生结果,每 10 轮强制执行一次 GC,并报告每 10 轮的延迟窗口及保留的浏览器状态。随后它通过受信任的浏览器点击提交第 101 个纯文本轮次,并使用浏览器时钟分别测量发送到 transcript DOM 和发送到绘制后的延迟,排除输入框的草稿镜像,并与完整轮次完成时间分开。逐轮诊断涵盖输入框填入、点击到用户消息回显、点击到首个分片、完成、浏览器变更、持久化分片和工具事件;合成回放模型拥有足够的上下文容量,可使 fixture 基数保持稳定,而不会因压缩(compaction)消耗脚本化调用。结构性断言钉住预期的负载、流和工具形状,但时间仍不设阈值,因为机器速度不属于正确性约定。必需的 `vitest.web.config.ts` 清单仍仅限 `*.e2e.ts` 和 `*.snapshot.ts`,因此 `test:web:built` 及其 CI 门禁都不会收集性能用例。 diff --git a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml index 8557998d06..c0fd339c04 100644 --- a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml +++ b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md -2026-07-30-web-browser-snapshot-ci-gate.md: 72a7e33d0e84105f7680429443df41661ced288a -2026-07-30-web-browser-snapshot-ci-gate.zh.md: 161f99ab98984ca1d938f11c5e3de5176ca4da66 +2026-07-30-web-browser-snapshot-ci-gate.md: abb197f2a8e3488e2ae1edc7c2e77cbdcf919556 +2026-07-30-web-browser-snapshot-ci-gate.zh.md: b665dc90c4bdba969b3cd9a561a3f18f388e27dd diff --git a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md index 72a7e33d0e..abb197f2a8 100644 --- a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md +++ b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md @@ -12,13 +12,13 @@ The [keyless web browser e2e lane](2026-07-24-web-gui-browser-e2e-lane.md) runs For Linux PRs, the `node 24 / snapshots and artifacts` job must run the full web browser replay/compare suite. When `DSH_WEB_SNAPSHOT_WORKERS` is configured, `scripts/run-gates.ts` registers `test:web:ci` as the `ci-consumers` gate and explicitly injects `DSH_SNAPSHOT=replay`; CI never runs in `record` or `refresh` mode, so when the committed goldens disagree with the currently assembled application, the tests fail directly instead of silently rewriting them on the runner and then passing. -The consumer job owns the [single Linux build](../process/2026-07-30-independent-ci-consumer-build.md), so `apps/web/dist` and the package `lib/` directories remain in its workspace for the browser suite. On hosted runners, CI installs Chromium and its system dependencies at the Playwright version in the lockfile. On the persistent failover VM, the image owns the Linux system packages and CI installs only Chromium, avoiding per-run `apt` mutation. The hosted default-branch Linux serial job runs the suite and produces the operating-system-and-lockfile-keyed browser cache; pull requests restore it without paying compression and upload on the required path, with an operating-system prefix fallback across lockfile changes. The self-hosted standby runs the same comparison without hosted cache actions. +The consumer job owns the [single Linux build](../process/2026-07-30-independent-ci-consumer-build.md), so `apps/web/dist` and the package `lib/` directories remain in its workspace for the browser suite. On hosted runners, CI installs Chromium and its system dependencies at the Playwright version in the lockfile. On the persistent failover VM, the image owns the Linux system packages and CI installs only Chromium, avoiding per-run `apt` mutation. Pull requests restore the operating-system-and-lockfile-keyed browser cache without paying compression and upload on the required path, with an operating-system prefix fallback across lockfile changes. No master job produces these hosted caches, so restores hit archived entries until they evict. The self-hosted standby runs the same comparison without hosted cache actions. Local `pnpm run test:web` continues to build first and then run the full browser suite serially; `test:web:built` is the serial entry point for existing build artifacts. Developers explicitly run `DSH_SNAPSHOT=refresh pnpm run test:web` only after confirming that user-visible output changed intentionally, review every expected-output diff, and then verify again in replay mode that no files are written. CI's `scripts/run-web-snapshots.ts` first runs `hmr-live.e2e.ts` and `cordis-tool-round.e2e.ts` as separate serial Vitest invocations. The HMR scenario mutates built workspace state, while the Cordis scenario owns a lifecycle-sensitive approval and steering sequence whose turn grouping is made deterministic by waiting for the initial turn to settle before approval. After both pass, one six-worker Vitest pool runs every remaining file. Every child inherits stdio, and the enclosing gate streams that output through `run-gates`. -For pull requests, the gate runs only in the Linux consumer job: these scenarios target POSIX, and the other PR jobs do not provision Chromium. The hosted and self-hosted default-branch Linux serial aggregates also include the comparison, while the macOS and Windows serial jobs remain browser-free. A PR's `all checks passed` verdict already depends on the consumer job, so a browser compare failure blocks the merge without requiring a new branch-protection check name. +For pull requests, the gate runs only in the Linux consumer job: these scenarios target POSIX, and the other PR jobs do not provision Chromium. The self-hosted default-branch Linux serial standby also includes the comparison, while the macOS and Windows serial jobs remain browser-free (there is no hosted Linux serial aggregate). A PR's `all checks passed` verdict already depends on the consumer job, so a browser compare failure blocks the merge without requiring a new branch-protection check name. Completed local replays measured the six-worker browser command at about 65–71 seconds. A twelve-worker comparison completed in about 50 seconds, so halving the browser worker budget adds about 15–20 seconds rather than doubling wall time. The gate scheduler starts browser snapshots as soon as `built-package-invariants` succeeds and runs independent gates concurrently, so it needs neither a dedicated job timeout nor a manual YAML ordering rule. diff --git a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md index 161f99ab98..b665dc90c4 100644 --- a/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md +++ b/.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md @@ -12,13 +12,13 @@ Status: implemented Linux PR 的 `node 24 / snapshots and artifacts` 必须运行完整 Web 浏览器 replay/compare。配置 `DSH_WEB_SNAPSHOT_WORKERS` 后,`scripts/run-gates.ts` 把 `test:web:ci` 登记为 `ci-consumers` 门禁,并显式注入 `DSH_SNAPSHOT=replay`;CI 永不以 `record` 或 `refresh` 模式运行,因此提交的预期输出与当前组装应用不一致时测试直接失败,不会在 runner 内静默改写后通过。 -消费方 job 在[消费方独立构建](../process/2026-07-30-independent-ci-consumer-build.md)中负责唯一一次 Linux 构建,因此 `apps/web/dist` 和包的 `lib/` 目录会保留在其工作区中,供浏览器套件使用。在托管运行器上,CI 按锁文件中的 Playwright 版本安装 Chromium 及其系统依赖。在持久化故障切换 VM 上,镜像负责预装 Linux 系统软件包,CI 只安装 Chromium,避免每次运行都通过 `apt` 改动系统。托管的默认分支 Linux 串行 job 运行该套件,并生成以操作系统和锁文件为键的浏览器缓存;PR 恢复该缓存,使必需路径无需承担压缩和上传开销,并可在锁文件变化时按操作系统前缀回退。自托管热备运行相同的比较,但不执行托管缓存操作。 +消费方 job 在[消费方独立构建](../process/2026-07-30-independent-ci-consumer-build.md)中负责唯一一次 Linux 构建,因此 `apps/web/dist` 和包的 `lib/` 目录会保留在其工作区中,供浏览器套件使用。在托管运行器上,CI 按锁文件中的 Playwright 版本安装 Chromium 及其系统依赖。在持久化故障切换 VM 上,镜像负责预装 Linux 系统软件包,CI 只安装 Chromium,避免每次运行都通过 `apt` 改动系统。PR 恢复以操作系统和锁文件为键的浏览器缓存,使必需路径无需承担压缩和上传开销,并可在锁文件变化时按操作系统前缀回退。没有任何 master 作业生成这些 hosted 缓存,因此恢复只能命中仍有归档的旧条目,直至其被逐出。自托管热备运行相同的比较,但不执行托管缓存操作。 本地 `pnpm run test:web` 仍先构建,再串行运行完整浏览器套件;`test:web:built` 是已有构建产物的串行执行入口。开发者只在确认用户可见输出有意变化后显式运行 `DSH_SNAPSHOT=refresh pnpm run test:web`,评审每一处预期输出 diff,再以 replay 模式复验不再写文件。 CI 的 `scripts/run-web-snapshots.ts` 先用相互独立的 Vitest 调用串行运行 `hmr-live.e2e.ts` 与 `cordis-tool-round.e2e.ts`。HMR 场景会修改已构建工作区状态;Cordis 场景则拥有一条对生命周期时序敏感的批准与 steering(中途引导)序列,它通过在批准前等待初始轮次结束来确定轮次分组。两者通过后,其余全部文件进入同一个 6-worker Vitest 池。所有子进程都继承 stdio,外围门禁再通过 `run-gates` 流式传递输出。 -对 PR 而言,门禁仅在 Linux 消费方 job 中运行:这些场景面向 POSIX,其他 PR job 不安装 Chromium。托管和自托管的默认分支 Linux 串行聚合作业也包含该比较,而 macOS 和 Windows 串行 job 仍不使用浏览器。PR 的 `all checks passed` 已依赖消费方 job,因此浏览器比较失败会阻止合并,无需新增 branch-protection check 名称。 +对 PR 而言,门禁仅在 Linux 消费方 job 中运行:这些场景面向 POSIX,其他 PR job 不安装 Chromium。自托管的默认分支 Linux 串行热备也包含该比较,而 macOS 和 Windows 串行 job 仍不使用浏览器(不存在托管的 Linux 串行聚合)。PR 的 `all checks passed` 已依赖消费方 job,因此浏览器比较失败会阻止合并,无需新增 branch-protection check 名称。 完整本地 replay 中,6-worker 浏览器命令耗时约 65–71 秒。12-worker 对比约为 50 秒,因此把浏览器 worker 预算减半只增加约 15–20 秒,而不是让墙钟时间翻倍。门禁调度器会在 `built-package-invariants` 成功后立即启动浏览器快照,并发运行彼此独立的门禁,因此既不需要专用 job 超时,也不需要手动制定 YAML 顺序规则。 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cab0cf791..3dce0d2c0e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,8 +42,11 @@ env: jobs: - # TODO(hosted-serial-ci): Re-enable the three hosted serial reference jobs before release. - # The self-hosted standby remains active on every master push. + # TODO(hosted-serial-ci): Re-enable the one remaining disabled hosted serial + # reference job (serial-macos) before release. The self-hosted standby lane + # below remains active on every master push. Re-enabling serial-macos does not + # restore a Linux hosted-cache producer: decide whether to add a master seeder + # or remove the restore-only steps if cold starts become a concern. # Three enterprise jobs isolate coverage, static analysis, and the # build-backed consumer tail. The consumer job owns the only Linux build so @@ -211,7 +214,11 @@ jobs: store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent) echo "path=$store_path" >> "$GITHUB_OUTPUT" - # Skipped under failover — see the coverage lane's identical rationale. + # Pull requests restore the pnpm store and Playwright caches without paying + # compression and upload on the required path. No master job saves these + # hosted cache keys, so each restore-keys fallback hits the matching archived + # entry until it evicts, after which the store is cold. Skipped under failover + # — the self-hosted VM's persistent store is already warm. - uses: actions/cache/restore@v4 if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]' with: @@ -220,8 +227,7 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- - # Pull requests restore the cache normally produced by serial-linux on - # master; they do not pay compression and upload on the required path. + # Skipped under failover: the VM's persistent browser cache is already warm. - uses: actions/cache/restore@v4 if: vars.DSH_CI_FAILOVER_LINUX != 'selfhosted' || github.event.pull_request.user.login == 'dependabot[bot]' with: @@ -492,76 +498,6 @@ jobs: shell: pwsh run: pnpm run check:ci:windows-complete - # The hosted reference jobs below are temporarily disabled; the self-hosted - # standby remains active. Each enabled host executes the complete, unsharded - # primary Node aggregate with one gate worker, giving reviewers a simple - # cross-platform oracle for completeness and timing. - serial-linux: - if: false - name: serial / linux - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 2 - - - uses: pnpm/action-setup@v4 - with: - dest: ${{ runner.temp }}/setup-pnpm - - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.PRIMARY_NODE_VERSION }} - - - name: Configure pnpm store path - id: pnpm-store - run: | - store_root="$HOME/.local/share/pnpm/store" - echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV" - store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent) - echo "path=$store_path" >> "$GITHUB_OUTPUT" - - # Master refreshes the pnpm store cache that pull requests restore without saving. - # The store cache stays a hand-rolled actions/cache step rather than - # setup-node's `cache: pnpm`: the enterprise pull-request jobs above - # restore exactly this key and path, and setup-node's built-in cache - # uses its own key format — converting this producer would silently - # starve their documented restore-only optimization. - - uses: actions/cache@v4 - with: - path: ${{ steps.pnpm-store.outputs.path }} - key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- - - # Master produces the hosted Chromium cache restored by pull requests. - - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-playwright- - - - name: Install (immutable) - run: pnpm install --frozen-lockfile - - - name: Install Playwright Chromium and system dependencies - run: pnpm --filter @deepseek-ai/dsh-web-frontend exec playwright install --with-deps chromium - - - name: Prepare bubblewrap (unrestrict userns) - run: bash scripts/prepare-ci-bubblewrap.sh - - - name: Run complete unsharded primary Node CI serially - env: - DSH_ARCHIVE_BASE_REF: ${{ github.event.before }} - DSH_COVERAGE_MAX_WORKERS: '1' - DSH_E2E_MAX_WORKERS: '1' - DSH_GATE_CONCURRENCY: '1' - DSH_OXLINT_THREADS: '1' - DSH_PUBLINT_CONCURRENCY: '1' - DSH_SNAPSHOT_MAX_CONCURRENCY: '1' - run: pnpm run check:ci:linux-primary - # Hot-standby drill for the in-house self-hosted pool: every master move # re-runs the complete unsharded aggregate on the persistent 64-core VM, # continuously proving that environment can take over a required lane if @@ -580,8 +516,8 @@ jobs: name: serial / linux (self-hosted standby) runs-on: [self-hosted, linux, x64, vm-backup] steps: - # Full history + DSH_ARCHIVE_BASE_REF below: same frozen-archive - # comparison as serial-linux. Depth 2 would miss github.event.before + # DSH_ARCHIVE_BASE_REF below compares the frozen-archive gate against + # github.event.before, so full history is required: depth 2 would miss it # on multi-commit or force pushes; full fetch is cheap here because # checkout resolves against the VM's local mirror. - uses: actions/checkout@v6 @@ -621,6 +557,8 @@ jobs: DSH_SNAPSHOT_MAX_CONCURRENCY: '1' run: pnpm run check:ci:linux-primary + # The one remaining disabled hosted serial reference job; see + # TODO(hosted-serial-ci) above. serial-macos: if: false name: serial / macos