Repository navigation
Description
Summary
Every PR gate in this repo validates on Node 20, which reached end-of-life on 2026-04-30 (~3 months ago). Meanwhile the release and publish workflows run Node 22. So code is verified on one runtime and shipped from another, and the verifying one no longer receives security patches.
The split is not random — it is exactly gates vs. release
| Node | Workflows | Role |
|---|---|---|
| 20 (12 sites) | ci.yml:93,195,322,436,494 · lint.yml:33,122 · coverage-nightly.yml:29 · docs-drift-check.yml:32 · pr-automation.yml:66 · spec-liveness-check.yml:36 · validate-deps.yml:35 |
All PR gates — Build Core, Test Core, TypeScript Type Check, Dogfood, ESLint, spec liveness, dep validation |
| 22 (6 sites) | release.yml:30 · publish-smoke.yml:123,188 · scaffold-e2e.yml:44,219 · showcase-smoke.yml:25 |
Release + end-to-end |
release.yml and publish-smoke.yml being on 22 is the tell: whatever we publish has only ever been gated on 20.
Node release schedule (from nodejs/Release schedule.json)
| Version | End |
|---|---|
| v20 (Iron) | 2026-04-30 — passed |
| v22 (Jod) | 2027-04-30 |
| v24 (Krypton) | 2028-04-30 |
How this surfaced
While adding a WASM-SQLite conformance test in #3812, an earlier revision imported better-sqlite3 in a test. It killed the vitest worker on CI with a process-level abort — no JS error, so the suite reported Test Files 22 passed (23) while 17 cases silently did not run. Reproduced locally with nvm exec 20.
better-sqlite3@13 declares engines: >=22. Note it currently does load fine on Node 20 in CI (I checked a passing Test Core log: zero WASM step-down warnings, so the native binding is genuinely in use) — engines is a declaration, not enforcement. This is a latent mismatch, not an active outage. #3812 sidestepped it by using sql.js (pure WASM, no ABI), so nothing is blocked on this issue.
But better-sqlite3@13 is unlikely to be the last dependency to require Node 22.
Recommendation: raise CI to 22, do not downgrade the dependency
Downgrading better-sqlite3 to v12 (engines: 20.x || … || 26.x) would work, but it treats the symptom and pins the repo to an EOL runtime — and the next dependency that requires 22 starts the same conversation.
Proposed:
- Move the 12 Node-20 sites to
'22', matching what release already uses. This also removes the gates-vs-release split, so PRs are verified on the runtime we publish from. - Consider 24 instead if a longer window matters (EOL 2028-04-30 vs 2027-04-30). 22 is the lower-risk move since 6 workflows already run it.
Low risk of surprises: Node 22 is already exercised by scaffold-e2e, showcase-smoke and publish-smoke. The change is mechanical; the verification is simply that the full gate set stays green on the bump PR.
Separate, related decision — do not bundle
Root package.json declares engines: { node: '>=18.0.0' } (uniform across all 49 packages). Node 18 went EOL 2025-04-30, so the published compatibility promise covers two EOL runtimes.
That is a promise to users, independent of what CI tests, and tightening it is a breaking change requiring a major. It should be decided on its own rather than folded into a CI bump.
Notes
lint.yml:33uses"20"(double quotes) andshowcase-smoke.yml:25uses bare22— a naivenode-version: '\d+'grep misses both. Worth normalizing quoting while touching these.- No
.nvmrc/voltapin exists, so local dev runtime is unconstrained; contributors on Node 24/25 will not reproduce Node 20 gate failures. Adding a pin is worth considering alongside the bump.
Activity
ci: validate on the runtime we publish from — pin every workflow to N…
ci: fail the Node pin guard when the pinned runtime is out of support (…
ci: assert every test vitest counted actually ran (#3825) (#3852)
feat!: require Node.js 22 — promise the runtime we actually test (#3825…
docs(releases): curate the v17 platform release page and wire it into…