ci: matrix over Node 22 and 24, add the bench and packaging tiers (task 0040)
All checks were successful
CI / test (22) (pull_request) Successful in 1m8s
CI / test (true, 24) (pull_request) Successful in 1m12s
CI / test (22) (push) Successful in 47s
CI / test (true, 24) (push) Successful in 1m4s

The workflow pinned Node 20, which reached end-of-life in April 2026, while
the manifest promised support down to it — so the entire claimed range below
the single tested version went unverified and its floor was unsupported.

Matrix over the two supported long-term-support majors and narrow the engine
range to `^22 || ^24`, naming exactly what is tested. Narrowing is free now
because nothing has been published and no tags exist.

The benchmark harness tier joins every leg: it is deterministic and needs no
network, and its non-default runner configuration made it easy to believe it
was running when it was not. The end-to-end and packaging tiers run on the
highest leg only, conditioned on a flag attached to that leg through a matrix
`include` entry rather than on a version number restated at each site. The
benchmark smoke tier stays out, since it would pass by skipping.

`@types/node` follows the new floor; it was the last Node 20 reference in the
manifest, and the typecheck runs on every leg.
This commit was merged in pull request #49.
This commit is contained in:
2026-07-19 23:45:55 -04:00
parent 6a9d4d4770
commit ccc8dbe998
6 changed files with 80 additions and 18 deletions

View File

@@ -39,7 +39,7 @@ It ships as both an installable npm CLI and a bundled Agent Skill, so any agent
### Language and Runtime
TypeScript on Node 20+, matching the `gh-axi` reference implementation.
TypeScript on the supported Node long-term-support majors — currently 22 and 24, as declared in the manifest's engine range and matrixed over by continuous integration.
ESM module format.
### Implementation Strategy

View File

@@ -24,11 +24,38 @@ The workflow keeps its container-and-npm shape and its GitHub Actions compatibil
## Acceptance criteria
- [ ] The workflow runs a matrix over the two supported Node versions, and no leg runs an end-of-life Node.
- [ ] The manifest's declared engine range names exactly the versions the matrix tests.
- [ ] The packaging tier's engine assertion matches the narrowed range and passes.
- [ ] The live end-to-end tier runs on the highest leg only.
- [ ] The benchmark harness tier runs on every leg, under its own runner configuration.
- [ ] The packaging tier runs on the highest leg only.
- [ ] The benchmark smoke tier does not run.
- [ ] The workflow syntax stays GitHub-Actions-compatible.
- [x] The workflow runs a matrix over the two supported Node versions, and no leg runs an end-of-life Node.
- [x] The manifest's declared engine range names exactly the versions the matrix tests.
- [x] The packaging tier's engine assertion matches the narrowed range and passes.
- [x] The live end-to-end tier runs on the highest leg only.
- [x] The benchmark harness tier runs on every leg, under its own runner configuration.
- [x] The packaging tier runs on the highest leg only.
- [x] The benchmark smoke tier does not run.
- [x] The workflow syntax stays GitHub-Actions-compatible.
## Implementation Notes
The supported majors are 22 and 24 — the two current long-term-support lines, and the pair that brackets the nixpkgs default the flake already builds against (24.18.0).
The engine range became `^22 || ^24` rather than `>=22`, so it names those two majors and nothing else; a bare floor would have re-promised the odd-numbered 23, which is itself end-of-life.
The single matrix job was kept rather than split into a matrixed job plus a separate single-version job for the once-only tiers.
The acceptance 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 container-and-npm shape.
The cost is that the Gitea service container starts on the Node 22 leg without the end-to-end tier consuming it — cheap next to running that tier twice, which is what the spec's rationale was actually guarding against.
The once-only steps condition on `matrix.highest`, a flag attached to the `24` leg through a `strategy.matrix.include` entry, rather than on `matrix.node == '24'` at each site.
An `include` entry whose keys match an existing combination augments that leg rather than adding a new one, on both GitHub Actions and Gitea Actions, and an undefined context property is falsy on the other leg.
Two consequential changes beyond the criteria, both surfaced by review:
`@types/node` moved from `^20.19.0` to `^22.20.1`.
It was the last Node 20 reference left in the manifest, and the typecheck runs on every leg.
It tracks the *floor* rather than the highest leg deliberately: typings for 24 would let source compile against APIs the Node 22 leg does not have.
`.claude/spec/gitea-axi.md`'s "Language and Runtime" section still read "TypeScript on Node 20+", which this change falsified.
Verified by running the whole matrix's worth of tiers locally through `nix develop`: typecheck, the fast tier with coverage thresholds (410 tests), the benchmark harness tier under its own runner configuration (117), and the full packaging tier (7), plus `nix build .#gitea-axi` after the lockfile moved.
The workflow was parsed with `yq` to confirm the matrix, the container expression, and the two `if:` conditions land where intended.
The live end-to-end tier was not run locally — it needs a disposable Gitea instance, and the diff only gates it.
One follow-up worth flagging: `vitest.bench.config.ts` sets `passWithNoTests: true`, so 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 left alone here rather than widened into a test-configuration change.