Summary
package.json supports only Node ^20.19.0, ^22.13.0, or >=24, while the runtime guard and its tests accept every major version >=20, including earlier 20/22 releases and all Node 21/23 releases.
Impact: On Node 20.0–20.18, Node 21, Node 22.0–22.12, or Node 23, shouldRejectNodeVersion returns false. The entrypoint therefore omits its promised early actionable failure and continues on a runtime the package and documentation declare unsupported, potentially failing later with the cryptic runtime errors the guard was introduced to prevent.
Code path
Checked against current main at commit 60d55e47883e924ff1a5f0ab1fe67a25bc3b0877.
package.json:30-32
src/version-guard.ts:13-14
src/version-guard.ts:38-40
src/version-guard.test.ts:31-40
src/index.ts:25-31
Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; const versions=['20.0.0','20.18.0','20.19.0','21.9.0','22.12.0','22.13.0','23.9.0','24.0.0']; console.log(JSON.stringify(Object.fromEntries(versions.map(v=>[v,shouldRejectNodeVersion(v)]))));"
Observed output:
{"20.0.0":false,"20.18.0":false,"20.19.0":false,"21.9.0":false,"22.12.0":false,"22.13.0":false,"23.9.0":false,"24.0.0":false}
Neighboring control:
node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; console.log(JSON.stringify({below_documented_floor:shouldRejectNodeVersion('18.20.0'),documented_20_floor:shouldRejectNodeVersion('20.19.0'),documented_22_floor:shouldRejectNodeVersion('22.13.0'),documented_24_floor:shouldRejectNodeVersion('24.0.0')}));"
Control output:
{"below_documented_floor":true,"documented_20_floor":false,"documented_22_floor":false,"documented_24_floor":false}
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
The major-only runtime guard accepts Node releases excluded by the declared semver range and causes doctor to report them as supported.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Make the shared startup/doctor guard enforce the full package engine range, then update its messages and tests.
Suggested tests
- Add a regression test for the reproduction above.
- Add a neighboring control assertion so the intended non-bug path remains covered.
Submitted with Codex.
Summary
package.json supports only Node ^20.19.0, ^22.13.0, or >=24, while the runtime guard and its tests accept every major version >=20, including earlier 20/22 releases and all Node 21/23 releases.
Impact: On Node 20.0–20.18, Node 21, Node 22.0–22.12, or Node 23, shouldRejectNodeVersion returns false. The entrypoint therefore omits its promised early actionable failure and continues on a runtime the package and documentation declare unsupported, potentially failing later with the cryptic runtime errors the guard was introduced to prevent.
Code path
Checked against current
mainat commit60d55e47883e924ff1a5f0ab1fe67a25bc3b0877.package.json:30-32src/version-guard.ts:13-14src/version-guard.ts:38-40src/version-guard.test.ts:31-40src/index.ts:25-31Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; const versions=['20.0.0','20.18.0','20.19.0','21.9.0','22.12.0','22.13.0','23.9.0','24.0.0']; console.log(JSON.stringify(Object.fromEntries(versions.map(v=>[v,shouldRejectNodeVersion(v)]))));"Observed output:
Neighboring control:
node --experimental-strip-types --input-type=module -e "import { shouldRejectNodeVersion } from './src/version-guard.ts'; console.log(JSON.stringify({below_documented_floor:shouldRejectNodeVersion('18.20.0'),documented_20_floor:shouldRejectNodeVersion('20.19.0'),documented_22_floor:shouldRejectNodeVersion('22.13.0'),documented_24_floor:shouldRejectNodeVersion('24.0.0')}));"Control output:
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
The major-only runtime guard accepts Node releases excluded by the declared semver range and causes doctor to report them as supported.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Make the shared startup/doctor guard enforce the full package engine range, then update its messages and tests.
Suggested tests
Submitted with Codex.