ci: matrix over Node 22 and 24, add the bench and packaging tiers (task 0040) #49
Reference in New Issue
Block a user
Delete Branch "task-0040-ci-node-matrix-and-new-tiers"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Task:
.claude/tasks/0040-ci-node-matrix-and-new-tiers.mdSummary
Continuous integration moves off end-of-life Node 20, matrixes over the two supported long-term-support majors, and picks up the two tiers it never ran.
testjob matrixes over Node 22 and 24 (fail-fast: false), with the container image taken from the matrix.>=20to^22 || ^24, naming exactly the majors the matrix tests — a bare>=22would have re-promised the odd-numbered, already end-of-life 23. The packaging tier's engine assertion moves with it.The workflow keeps its container-and-npm shape and its GitHub Actions compatibility; nothing migrates to building via Nix (that is task 0041).
Deviations
One matrix job, not a split. The once-only tiers are gated with a step-level
if:rather than moved into a separate single-version job. The criteria are phrased in terms of matrix legs, one job keeps the version list in exactly one place, and the task asked that the workflow keep its shape. The cost is an idle Gitea service container on the Node 22 leg — cheap next to running the end-to-end tier twice, which is what the rationale was actually guarding against.matrix.highest, notmatrix.node == '24'. The flag is attached to the24leg via astrategy.matrix.includeentry, so the two once-only steps share one named condition. Anincludeentry whose keys match an existing combination augments that leg rather than adding a new one, on both platforms, and an undefined context property is falsy on the other leg.Two changes beyond the acceptance criteria, both surfaced by review:
@types/nodemoved^20.19.0→^22.20.1(the last Node 20 reference in the manifest, and the typecheck runs on every leg — it tracks the floor, since typings for 24 would let source compile against APIs Node 22 lacks), and.claude/spec/gitea-axi.md's "Language and Runtime" section no longer reads "TypeScript on Node 20+", which this change falsified.Verified locally through
nix develop: typecheck, the fast tier with coverage thresholds (410 tests), the benchmark harness tier (117), the full packaging tier (7), andnix build .#gitea-axiafter the lockfile moved. The workflow was parsed withyqto confirm the matrix, the container expression, and bothif:conditions land where intended. The live end-to-end tier was not run locally — it needs a disposable Gitea instance, and this diff only gates it.Review
Risk
Overall: MEDIUM
engines, and a packaging assertion move together; the narrowing propagates to every consumer's install.test/packaging/tarball.test.tsasserts the new range directly, and the matrix is the guard for the majors named.enginesis a runtime-facing contract on the shipped package.Unaddressed findings
Standards —
vitest.bench.config.tssetspassWithNoTests: true. If its include glob ever broke, the new benchmark step would go green while running nothing — precisely the failure mode this task cites as the reason to add the step. Pre-existing and not introduced here; left alone rather than widening the change into a test-configuration one.Standards — the matrix names
"24"twice. Theincludeentry must be kept in sync with the last element of thenodearray by hand. Inherent to theinclude-augments-a-leg idiom; a comment on the entry says so, and the alternative (repeating the version at eachif:site) is worse.Spec: no findings — all eight acceptance criteria verified individually.