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 } => (