diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml index 18ab194298..79e7511b3d 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.i18n.yaml @@ -9,11 +9,11 @@ en: d7bf1b1699ce0dc8 zh: f10dfd0e4787bb44 /agent-note-raise-the-node-lts-engine-floor-to-22-19/decision: - en: 0051c0d8c1948f63 - zh: cbab7aa0f53f7200 + en: 355366989e655caf + zh: 51ac6466f65e6d58 /agent-note-raise-the-node-lts-engine-floor-to-22-19/consequences: - en: 0b9fbe932d9a8b1f - zh: 642e19d06caecef2 + en: cf076eee91c2ec6a + zh: d4dea1eb9535ed98 /agent-note-raise-the-node-lts-engine-floor-to-22-19/alternatives-considered: en: c433389b2064eaf9 zh: 57de2af812def1c4 diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md index e2fe7661f4..bfcc5e7afd 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.md @@ -10,7 +10,11 @@ The Node 22 branch of the root `engines.node` range is a contract for the instal ## Decision -Set `engines.node` to `^22.19.0 || >=24.0.0` and test keyless CI on `['22.19', 24, 26]`. The primary Node 24 jobs own the complete typecheck and unit coverage inventory; every version runs focused source-worker, Zstandard, source-launch, and [jsdom storage](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) smokes without repeating that inventory. The real-API e2e workflow stays on Node 24 because it exercises API integration rather than the runtime floor. +Set `engines.node` to `^22.19.0 || >=24.0.0` and test keyless CI on `['22.19', 24, 26]`. The primary Linux Node 24 jobs own the complete typecheck and unit coverage inventory; every version runs focused source-worker, Zstandard, source-launch, and [jsdom storage](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) smokes without repeating that inventory. The real-API e2e workflow stays on Node 24 because it exercises API integration rather than the runtime floor. + + + +Windows PR coverage uses Node 26.10.0, which includes the [cppgc Realm cleanup fix](https://github.com/nodejs/node/pull/65778). Windows build and native-process checks use Node 24. Moving coverage back requires a Node 24 release containing the [pending backport](https://github.com/nodejs/node/pull/66168); the full test inventory, isolation and coverage thresholds remain required. Two Node features gate the source runtime: @@ -24,7 +28,7 @@ Those source features clear on the 22.x line at **22.18**, but the installed Pi ## Consequences - The advertised LTS branch no longer undercuts the Pi adapter dependency floor. -- CI proves the Node 22 LTS floor directly with Node 22.19, keeps primary coverage on `node: 24`, and exercises Node 26 as the next even line; focused compatibility smokes run on all three versions. +- CI proves the Node 22 LTS floor directly with Node 22.19, keeps primary Linux coverage on `node: 24`, and exercises Node 26 as the next even line; focused compatibility smokes run on all three versions. - The built-mode smoke needs no version-conditional flag: at 22.19 type-stripping is already the default, so the example-owned TypeScript driver stays a plain `node fixture.ts` path. - A future dependency or source API that raises the runtime floor must move `engines.node`, the compatibility matrix, and this Agent Note in the same change. diff --git a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md index e7d41e8128..47e414d105 100644 --- a/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md +++ b/.agents/notes/implemented/process/2026-07-06-node-engine-floor.zh.md @@ -10,7 +10,11 @@ Status: implemented ## 决策 -将 `engines.node` 设为 `^22.19.0 || >=24.0.0`,并在 `['22.19', 24, 26]` 上运行 keyless CI。主要的 Node 24 任务负责整套类型检查和单元测试覆盖率任务;三个版本均运行 source-worker、Zstandard、source-launch 和 [jsdom 存储](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) 专项冒烟测试,不重复这套类型检查和覆盖率任务。真实 API 的 e2e 工作流保持在 Node 24 上,因为它验证的是 API 集成而非运行时下限。 +将 `engines.node` 设为 `^22.19.0 || >=24.0.0`,并在 `['22.19', 24, 26]` 上运行 keyless CI。主要的 Linux Node 24 任务负责整套类型检查和单元测试覆盖率任务;三个版本均运行 source-worker、Zstandard、source-launch 和 [jsdom 存储](../../archived/testing/2026-07-30-vitest-jsdom-webstorage-ownership.md) 专项冒烟测试,不重复这套类型检查和覆盖率任务。真实 API 的 e2e 工作流保持在 Node 24 上,因为它验证的是 API 集成而非运行时下限。 + + + +Windows PR 覆盖率使用 Node 26.10.0,其中包含 [cppgc Realm 清理修复](https://github.com/nodejs/node/pull/65778)。Windows 构建和原生进程检查使用 Node 24。将覆盖率切回 Node 24 需要采用包含[待合入回移](https://github.com/nodejs/node/pull/66168)的正式版本;完整测试清单、隔离和覆盖率阈值仍是必需项。 两个 Node 特性决定了源码运行时的门槛: @@ -24,7 +28,7 @@ Status: implemented ## 后果 - 宣传的 LTS 分支不再低于 Pi 适配器依赖的下限。 -- CI 通过 Node 22.19 直接验证 Node 22 LTS 下限,将主要覆盖率任务保留在 `node: 24`,并用 Node 26 验证下一个偶数线;三个版本均运行聚焦的兼容性冒烟测试。 +- CI 通过 Node 22.19 直接验证 Node 22 LTS 下限,将主要 Linux 覆盖率任务保留在 `node: 24`,并用 Node 26 验证下一个偶数线;三个版本均运行聚焦的兼容性冒烟测试。 - 构建模式冒烟测试无需版本条件标志:在 22.19 上类型剥离已是默认行为,因此示例自有的 TypeScript driver 保持使用纯 `node fixture.ts` 路径。 - 未来若依赖或源码 API 提高运行时下限,必须在同一变更中同步调整 `engines.node`、兼容性矩阵和本 Agent Note。 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 112e9c18a6..0f79dab45b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -570,7 +570,7 @@ jobs: && github.event.pull_request.user.login != 'dependabot[bot]' && fromJSON('["self-hosted", "dsh-win-ci", "windows"]') || 'dsh-windows-2025-16core' }} - name: windows node 24 / coverage + name: windows node 26 / coverage timeout-minutes: 120 env: # Shared hosts expose host CPUs, not an isolated allocation. @@ -606,9 +606,12 @@ jobs: - uses: pnpm/action-setup@v4 with: dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }} + # Node 24 still awaits the cppgc Realm-lifetime backport (#66168). + # 26.10.0 contains nodejs/node#65778; an exact version prevents a + # runner cache from selecting an older Node 26 with the same crash. - uses: actions/setup-node@v6 with: - node-version: ${{ env.PRIMARY_NODE_VERSION }} + node-version: '26.10.0' - name: Install (immutable) shell: pwsh # See 2026-08-30-windows-refs-store-block-clone-install for the diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index f8163a8781..b1f2bc7351 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -62,6 +62,20 @@ describe('CI workflow', () => { expect(wine.steps).toContainEqual(expect.objectContaining({ name: 'Shut down wineserver', if: 'always()' })) }) + it('runs Windows coverage with the released Realm cleanup fix while retaining Node 24 native checks', () => { + const workflow = loadWorkflow('.github/workflows/ci.yml') + const coverage = workflowJob(workflow, 'windows-coverage') + expect(coverage.name).toBe('windows node 26 / coverage') + expect(coverage.steps).toContainEqual(expect.objectContaining({ + uses: 'actions/setup-node@v6', with: { 'node-version': '26.10.0' }, + })) + for (const name of ['windows-build', 'windows-native-tests']) { + expect(workflowJob(workflow, name).steps).toContainEqual(expect.objectContaining({ + uses: 'actions/setup-node@v6', with: { 'node-version': '${{ env.PRIMARY_NODE_VERSION }}' }, + })) + } + }) + it('isolates every pnpm action setup destination per runner', () => { const files = ['.github/workflows/ci.yml', '.github/workflows/ci-master.yml'] const setups: Array<{ jobName: string; step: unknown }> = [] @@ -230,7 +244,7 @@ describe('CI workflow', () => { expect(install!.run).not.toContain('$cloneFlag') } - expect(windowsCoverage.name).toBe('windows node 24 / coverage') + expect(windowsCoverage.name).toBe('windows node 26 / coverage') expect(windowsCoverage.env).toMatchObject({ DSH_COVERAGE_PARTITIONS: "${{ vars.DSH_CI_FAILOVER_WINDOWS == 'selfhosted' && github.event.pull_request.user.login != 'dependabot[bot]' && '4' || '' }}" }) const coverageSteps = windowsCoverage.steps as unknown[] const coverageCommands = coverageSteps.filter((step): step is Record & { run: string } => (