cleanup: drop the sqlite flag every launcher passes, which Node 24 does not read (#463)
Third PR of the cleanup pass, one class: a runtime flag that does nothing on the
only Node this repo supports.
`--experimental-sqlite` was on all five paths that start the service — `npm start`,
`npm run dev`, the Dockerfile `CMD`, and both service boots in `test/e2e.sh`. It is
a no-op there: `node --help` on 24.x lists only the NEGATIVE form,
`--no-experimental-sqlite`, so `node:sqlite` is on by default and the flag exists to
turn it OFF. The repo already proved it without saying so — `npm test` passes no such
flag while 14 test files import `src/store/db.ts`, whose line 1 is
`import { DatabaseSync } from "node:sqlite"`.
Three comments credited the flag, and `code-review.yml:923` was wrong BEFORE this
change: "Node 24 runs the TypeScript sources directly (`--experimental-sqlite`, no
build step)". Built-in type stripping does that; the flag never did. The two
`setup-node` comments gave the flag as the reason CI must match `.nvmrc`; the reason
survives without it.
The pin is on the LICENCE, not just the flag. The removal is correct BECAUSE
`engines.node` is `>=24`, so `test/runtime-flags.test.ts` reads the floor and every
place that restates it: `.nvmrc` and every `FROM node:` stage must CLEAR the floor
(testing or shipping on a newer Node is allowed), while README.md's "Requires
**Node.js 24+**" and CONTRIBUTING.md's "Requires **Node 24+**" must EQUAL it, because
"24+" IS the floor claim rather than a runtime choice. The three workflow comments
naming "Node 24" are out of scope with the reason stated in the test.
Five review rounds, and rounds 2-4 each found a different escape from the one rule
this test exists to hold — a failure must not hide another:
- `Number()` read one of the four forms `node-version-file` accepts; `24.16.0`,
`v24` and `lts/*` all gave NaN and would have blamed a floor that was fine
- a no-`g` `.match()` read only the first `FROM`, so a multi-stage Dockerfile whose
runtime stage dropped below the floor stayed green
- `majorOf` and then `at()` asserted in place, inside the array literals, so one
unreadable or reworded member hid every member after it
- `readFileSync` threw ENOENT for a deleted member file — and the mutation for that
showed the same escape open in the launcher test
Four fault kinds are collected in both tests now, each with its own wording because
each has its own fix: the FILE is gone, the LINE is gone, the version names NO MAJOR,
or the major DISAGREES. `engines.node` keeps the throwing form, being the floor every
other member is compared against.
Swept the class: `--use-system-ca`, `--env-file-if-exists`, `--watch` and the three
`--test-reporter` flags are all options node still reads. `--experimental-sqlite` was
the only no-op.
Also swept and clean, recorded because a claim of absence has nothing to re-derive:
411 exports across 60 `src/` files, zero unread; 58 of 60 files import-reachable from
`src/index.ts` (the two that are not are the documented calibration CLI); all 9
runtime dependencies imported; all three non-`.test.ts` files in `test/` reached.
`@types/node`'s `^22.9.0` pin under an `engines.node` of `>=24` is left for its own
change: it moves `package-lock.json`, and no case was found where the gap hides an
error.
Gates: `tsc --noEmit` clean, `npm test` 1698/1698, `./test/e2e.sh` exit 0 with
150 ✓ / 0 ✗ — and that e2e run is the executable proof, since it boots the service
twice with no flag.
Refs #460
Co-Authored-By: bbertucc <bbertucc@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>