Skip to content

fix(majors): anchor the @types/node plants on the major, not the patch - #119

Merged
kschlt merged 1 commit into
mainfrom
claude/majors-plant-major-anchor-bjdse8
Oct 4, 2026
Merged

kschlt merged 1 commit into
mainfrom
claude/majors-plant-major-anchor-bjdse8

Conversation

@kschlt

@kschlt kschlt commented Oct 4, 2026

Copy link
Copy Markdown
Owner

Requested by Kornelius · project thread

Before: the two node-26 plants on package.json in tests/protections/majors.ts searched for the literal "@types/node": "^26.6.2". Every patch bump of @types/node removed that text, so majors.test.ts ("every plant's text is still in its subject, exactly once") went red for a reason unrelated to Node. Dependabot #118 (^26.6.3) fails on exactly that, 1 test.

After: both plants find "@types/node": "^26". A patch or minor bump within 26 stays green; a major bump still turns the static check red, which is wanted, because a new major has to re-plant.

How: only the find text changes. The replacements leave the tail of the version behind (^27.6.2, latest.6.2), and each one carries a comment saying why that is still the violation the guard must refuse. The other version-bearing plants in the file (node:26-slim, node-version: 26, >=26) already name only the major.

Measured (Node 26.10.0, on main 2afb042 + this commit)

Plant Before After
package.json at ^26.7.1 (patch bump) majors.test.ts red green
package.json from #118's head (^26.6.3) red (as in CI) green
package.json at ^27.0.0 (major bump) red still red

npm run majors -- node-26: both @types/node mutations are still killed at repo-config.test.ts > … > every place that pins a Node version pins the same major, as are the other seven. The one refusal (process.test.ts, SIGTERM) is identical on main and comes from the sandbox having no DATABASE_URL, so that suite is skipped.

npm run quality: 94 files, 1312 passed, 46 skipped.

Afterwards #118 only needs a branch update.

🤖 Generated with Claude Code

https://claude.ai/code/session_011ZssYS3sP4qmk6SmMEvNdt


Generated by Claude Code

Both node-26 plants in package.json found the literal "^26.6.2", so any
patch bump of @types/node moved the text out from under them and the
static check in majors.test.ts went red. Dependabot #118 (^26.6.3) fails
on exactly that. Anchor on '"@types/node": "^26' instead.

A patch bump now stays green, a major bump still goes red, and both
plants are still killed at "every place that pins a Node version pins
the same major".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011ZssYS3sP4qmk6SmMEvNdt
@kschlt kschlt self-assigned this Oct 4, 2026

kschlt commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

PR Review Verdict

Result: PASS

Deterministic: Measured on head 051292c — tree 19cd3cd. main at 2afb042 is an ancestor and the branch is one commit on top of it, so the head is the merge result and there is nothing to merge; GitHub reports clean and all 22 check runs green. In a fresh worktree with npm ci run before anything else, npm run quality exits 0 against a real PostgreSQL 16 under Node 26.10.0: tsc --noEmit clean, biome ci . over 204 files with 5 warnings and no errors — the same five that stand on main — and 94 files, 1379 passed / 4 skipped. The diff is one file, +11/-4, and only find and replace text changes; the one subject is 70 characters and conventional. The fix was measured against a control rather than read. With this head's anchors, a patch bump of @types/node to ^26.7.1 leaves majors.test.ts and repo-config.test.ts green at 49 of 49; with main's anchors restored into the same worktree and the same bump planted, every plant's text is still in its subject, exactly once is red naming both @types/node plants as find-absent. That is the before and after of the defect, in one tree, one plant apart. #118's declared ^26.6.3 is green by the same measurement. A real major bump to ^27.0.0 is still red, at the disagreement assertion listing every pin. npm run majors -- node-26 with a database kills 10 of 10, both @types/node mutations reported at tests/unit/repo-config.test.ts > slice0/container-builds-and-runs > every place that pins a Node version pins the same major — the assertion their marker names, not a neighbour. Each mutation's own output was planted separately: ^27.6.2 is red at the disagreement, and latest.6.2 is red at the fail-closed package.json declares no @types/node caret range of the form ^N, which is the sentence that plant's name claims. Every plant restored with git reset --hard plus git clean, git status --porcelain empty after each. — Tree: 19cd3cd0955d066629feb40dd4f0a8bcce1d2c56 — Head: 051292c

Acceptance Criteria

  • [met] A patch bump of @types/node within major 26 no longer reddens the plant-anchor guard — Evidence: ^26.7.1 planted on this head leaves both guards green at 49 of 49; the identical plant with main's anchors in the same worktree is red with both plants named find-absent. The control is what separates a fix from a tree that was already green — proof: protections/the-major-plants-still-describe-this-tree > every plant's text is still in its subject, exactly once
  • [met] chore(deps-dev): bump the development-dependencies group across 1 directory with 4 updates #118 is unblocked by this and by nothing else — Evidence: its declared ^26.6.3 planted here is green, and the only test that failed on its head was this guard. Nothing else in its 27-package lockfile delta is implicated — proof: same assertion, planted at ^26.6.3
  • [met] A real major bump still turns the comparison red — Evidence: ^27.0.0 is red at these disagree about the Node major: Dockerfile=26, Dockerfile.runtime=26, …, so the property the plants exist to defend is untouched by the anchor change — proof: slice0/container-builds-and-runs > every place that pins a Node version pins the same major
  • [met] Both plants are still killed, and at their own named assertion — Evidence: npm run majors -- node-26 is 10 killed, 0 survived, 0 inconclusive, 0 refused, and prints the resolved assertion for each. The expensive half of this file's trade is the part a static green cannot speak to, so it was run rather than inferred
  • [met] Each plant's replacement is still the violation its name describes — Evidence: planted individually, ^27.6.2 fails at the disagreement and latest.6.2 fails at the fail-closed definedness check. The second matters because the plant is named "the caret range is gone, so its major cannot be read": had the odd tail made it red at the disagreement instead, the plant would have been passing for a neighbouring reason
  • [met] The class is closed in this repository, not only this instance — Evidence: no other guard, plant or fixture names a three-part dependency version. grep over src tests scripts docs .github for ^N.N.N returns only prose: three comments in the two files above, two ADR sentences about the TypeScript caret, and the dependabot.yml note. typescript: "^5.7.0" is named in ADR-0028 and ADR-0034 but anchored by no test, so a minor bump of it cannot repeat this

Findings

  • [advisory][reproduced] The PR's own numbers are the database-less ones, in both reports, and in one of them that understates the result. The body reports npm run quality: 1312 passed, 46 skipped. I measure 1379 passed / 4 skipped with a database and 1312 / 46, exit 0 without one, so the 46 are the persistence, dbq and wire suites stepping aside with nothing in the output saying the database was why. The same gap moves the second report: the body says nine of ten plants killed with one refusal, the SIGTERM plant, correctly attributed to the missing DATABASE_URL. With a database that refusal is a kill and the run is 10 of 10. Nothing here rests on the weaker number — CI sets DATABASE_URL on the four jobs that need it and all 22 checks are green — and this is the third PR body in a row where the test count is the tell. Naming it for the next thread, not asking for a change.
  • [advisory][reasoned] Nothing in the tree now holds the convention this PR establishes. "An anchor names the major, not the patch" is a rule the two comments explain well and no assertion enforces: an edit putting "@types/node": "^26.6.2" back is green until the next bump. It would cost about three lines in majors.test.ts — no mutation's find carries a dotted version — and I am not asking for it, on the test that decides severity here: the failure mode is loud. A reverted anchor reddens the gate at the next Dependabot pull request, which is how this defect was found in the first place. That is the opposite of the silent-green shape ADR-0029 exists for, and a fourth guard layer over a test fixture is the kind of polish this project has decided against. Worth knowing before someone copies the anchor style, not worth a commit.

Not Checked

  • Whether a ~26.6.2 or 26.x style declaration would keep the anchors matching. It would not, and the plants would report find-absent as they did for the patch — but a range-operator change is a human edit in a reviewed diff, not a bot's schedule, so it is a different problem from the one this PR fixes and I did not plant it.
  • Anything about the installed major as opposed to the declared one. repo-config.test.ts says in its own comment that it reads the declaration and that drift above the caret is named rather than caught; this PR does not touch that boundary and I did not test across it.
  • The zod-4 and hono-node-server-2 harnesses, and the other eight node-26 plants beyond confirming they are still killed. The diff touches none of their text.
  • The 1379 tests outside the two files this change can reach. The gate ran them green; I did not reason about them.

Routed

none


Generated by Claude Code

@kschlt
kschlt merged commit d5c2888 into main Oct 4, 2026
22 checks passed
@kschlt
kschlt deleted the claude/majors-plant-major-anchor-bjdse8 branch October 4, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants