Split out of #331, where four review rounds showed the check cannot be done correctly by regex.
What is unguarded
Every workflow pins node-version: 24.20.0 and package.json declares engines.node: ">=24.20.0". Nothing enforces that they agree. A workflow could pin a different major, pin a version below the declared minimum, or lose its pin entirely and fall back to the runner default, and no check would notice.
Why #331 stopped trying
tests/repo/node-runtime-types-alignment.test.ts attempted this by scanning the YAML with regexes. Review found four ways it passed while broken, each surfacing only after the previous was fixed:
- Lexical version comparison —
"24.9.0" >= "24.20.0" is true, and "26.10.0" >= "26.4.1" is false. Wrong in both directions.
- An aggregate count across files stayed positive when one file lost its pin.
- A per-file count stayed positive when one of
release.yml's two setup-node steps lost its pin.
- A commented-out pin —
# node-version: 24.20.0 — satisfies every regex while GitHub ignores it.
Each fix was correct and produced the next hole. The pattern is not sloppiness: associating a pin with its step requires knowing which with: block belongs to which step, which is YAML semantics, and this repo has a record of what happens when it hand-rolls a parser for a grammar it does not own (#197, six rounds).
The guard now asserts only that the resolved @types/node major matches engines.node, read from the lockfile. That covers the risk the dependabot ignore rule exists for, and cannot be fooled by comments, ranges, or indentation.
What a real fix needs
A YAML parse, so each actions/setup-node step is checked for its own active with.node-version. That means a parser dependency, which is ask-first here, and the trade should be stated rather than assumed: one small YAML dependency against a class of drift nothing currently detects.
Worth noting the exposure is modest — all seven workflows carry correct pins today, and a wrong pin would likely surface as a CI failure rather than silently. The reason to fix it is that "likely" is doing real work in that sentence.
Not blocking
Recorded so the gap is known rather than forgotten. #331 is deliberately narrower than the problem.
Split out of #331, where four review rounds showed the check cannot be done correctly by regex.
What is unguarded
Every workflow pins
node-version: 24.20.0andpackage.jsondeclaresengines.node: ">=24.20.0". Nothing enforces that they agree. A workflow could pin a different major, pin a version below the declared minimum, or lose its pin entirely and fall back to the runner default, and no check would notice.Why #331 stopped trying
tests/repo/node-runtime-types-alignment.test.tsattempted this by scanning the YAML with regexes. Review found four ways it passed while broken, each surfacing only after the previous was fixed:"24.9.0" >= "24.20.0"istrue, and"26.10.0" >= "26.4.1"isfalse. Wrong in both directions.release.yml's twosetup-nodesteps lost its pin.# node-version: 24.20.0— satisfies every regex while GitHub ignores it.Each fix was correct and produced the next hole. The pattern is not sloppiness: associating a pin with its step requires knowing which
with:block belongs to which step, which is YAML semantics, and this repo has a record of what happens when it hand-rolls a parser for a grammar it does not own (#197, six rounds).The guard now asserts only that the resolved
@types/nodemajor matchesengines.node, read from the lockfile. That covers the risk the dependabot ignore rule exists for, and cannot be fooled by comments, ranges, or indentation.What a real fix needs
A YAML parse, so each
actions/setup-nodestep is checked for its own activewith.node-version. That means a parser dependency, which is ask-first here, and the trade should be stated rather than assumed: one small YAML dependency against a class of drift nothing currently detects.Worth noting the exposure is modest — all seven workflows carry correct pins today, and a wrong pin would likely surface as a CI failure rather than silently. The reason to fix it is that "likely" is doing real work in that sentence.
Not blocking
Recorded so the gap is known rather than forgotten. #331 is deliberately narrower than the problem.