extractEngines put every known runtime under engines on the dashboard. resolveEngineVersion knew which registry to ask for each. bumpEngineConstraint knew how to raise a constraint without changing its shape. Both were tested and exported from the public API.
No scan step called either. So an engine was listed and never proposed.
Had one been proposed, it would not have been written: generateAllFileUpdates rewrites the four dependency sections and nothing else, so an engines update entered that loop, logged Package node not found, and produced no change.
What's wired
packages: { engines: true } // opt-in, default off| Scan | checkEnginesForUpdates beside the Docker/Zig checks; npm engines via the registry client (shared cache), Node/Deno via GitHub releases |
| Constraint | shape preserved at the author's precision — >=20 → >=22, never 22.3.0 |
| Write | applyEngineUpdates, scoped to the engines object |
| Config | packages.engines typed, validated as a boolean, documented |
Off by default because the module's own docstring already said why: "bumping it is a deployment decision". Raising node: '>=20' to '>=22' is a statement about every environment the project runs in.
Two things worth your eye
Scoping the rewrite. npm, pnpm and yarn are often both an engine and a devDependency in the same file. A rewrite matching by name alone would move the wrong one — there's a test where both are present and only the engine moves.
Precision, not satisfies. bumpEngineConstraint compares at the author's precision, so ^9.0.0 follows 9.1.0 to ^9.1.0 the way a dependency's caret would, while >=9 isn't moved by a 9.x release at all. That's the existing function's behaviour — its docstring says "null when it already admits latest", which isn't quite what it does. I kept the behaviour and made the tests state it explicitly rather than silently pick a side.
Not addressed
Node's latest release is the current line, not LTS — so >=20 is offered >=23 when 23 is current. Preferring even majors would be an opinion the resolver hasn't expressed; the opt-in makes the proposal deliberate either way. Flagging rather than deciding.
Also cosmetic: an engines row lands in the npm table with Mend badges keyed on the npm package name, which for node is a different package. Harmless, wrong-looking.
Tests
2127 → 2143.
Lint ✓ · typecheck ✓ · check:docs ✓ (222 config keys now) · 0 fail
🤖 Generated with Claude Code