test(gate): see a version behind a name-version: key in the doc ledger - #6429
Merged
Conversation
`doc-version-claims.test.ts` could not see `node-version: 20` — the canonical spelling of a Node version in these docs, and the shape every workflow example uses. The word `version` stands between the toolchain name and the number, and `SEP` is a character class: it cannot cross a word, and widening it until it could would weld any name to any nearby number. Adds a second recogniser for the `name-version: N` shape rather than a `node-version` alias in `TOOLCHAIN`. The alias cannot see the literal that produced the finding: it reuses `VERSION`, and `VERSION` refuses a bare integer on a measurement this file already records. Measured on the alias shape — `node-version: '22.x'` and `node-version: 20.11.1` match, `node-version: 20` does not. A second recogniser carries its own value pattern, legal only inside this shape because the key declares what the value is; `VERSION` is untouched. Measured over the 241 files the three scan roots resolve to: 37 matched literals before, 38 after — one new, none lost. The new one is inventoried `anchored`, not repaired: the sentence cites `node-version: 20` as the value a fossilised YAML block had drifted to and states that every workflow declares 22 instead, which `.github/workflows` adjudicates. Also adds the permanent fixture: it rebuilds the pre-fix recogniser set and asserts it FAILS on the same lines the new set matches, pins the alias counterfactual with a live control, and records the boundaries this does not cross — action pins (`actions/setup-node@v4`, `pnpm/action-setup@v4`), foreign runtime keys, and the spaced prose spelling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6409
The version-literal ledger could not see
node-version: 20— the canonical spelling of a Node version in these docs, and the shape every workflow example uses. The wordversionstands between the toolchain name and the number, andSEPis a character class: it cannot cross a word. The gate reported green over a literal it never examined.The shape chosen: a SECOND RECOGNISER, not an alias in
TOOLCHAINBoth shapes were on the table. The alias cannot see the literal that produced the finding, and that is a measurement rather than a preference.
TOOLCHAINfeeds the claim regex whose value pattern isVERSION, andVERSIONdeliberately refuses a bare integer — its own comment records why (a bare-integer rule reads the coverage table's| coerceCell | Vitest | 100% |as "Vitest 100", and loosening it flags 37 of the corpus's 38 hits, mostly prose numbers).20is a bare integer. Measured on the alias shape:So the alias would have matched the NAME, failed on the VALUE, and left this card's own line invisible with the gate still green. Buying it back means loosening
VERSIONfor every claim in the file — the widening that file already measured and rejected.A second recogniser is entitled to its own value pattern, and only inside this shape, because here the key declares what the value is. Nothing reads
node-version: 20as anything but a version, so the "Vitest 100" ambiguity does not arise, andKEYED_VERSION_VALUEadmits a bare integer for keyed values alone.VERSIONis untouched.The alias would also have mis-keyed the entry: the inventory key is the matched TEXT, and an alias hands it whatever
SEPconsumed — the same pathology the@vitejsplugin-react entry records, where the key is the TAIL of a package name. The keyed recogniser captures the optional quote and closes it by backreference, so the key is the whole shape (node-version: '22.x'), not a fragment ending at the opening quote.What this does NOT cover
Stated so it is not mistaken for covered, and pinned as assertions in the fixture:
actions/setup-node@v4@, a separator CHARACTER, so it is aSEPquestion and not this one.pnpm/action-setup@v4@obstacle.python-version:,java-version:,go-version:TOOLCHAINname. This repository states no version for those runtimes anywhere, so a claim about one could not be re-measured against anything here. Measured across the three scan roots: ZERO of the three (control, same sweep: onenode-version:line).Node version 20(space, not hyphen)Node 22.xclaims). Recorded as a boundary rather than repaired by a widening nothing asked for — the stance #6307 took with underscore emphasis.Both action pins were verified to produce no claim after the change, not merely assumed to.
Gate output: blind → seeing
Before (at
9602dc820) — wholescripts/__tests__tree green, and the literal is simply absent from the scan:Census over the 241 files the three roots resolve to: 37 matched literals, 0 structurally exempt, 37 flagged. Nothing in that output distinguishes "examined and fine" from "never matched".
With the recogniser widened, before the ledger entry — the ratchet now names it:
After — green with the literal inventoried:
Census: 38 matched literals, 38 flagged — one new, none lost. Control, same sweep: the three
Node 22.xclaims stay matched, and no existing entry went stale.Triage of what the widened scan surfaces
Exactly one literal, decided individually — no bulk
KNOWN_CLAIMSadditions, and nothing deleted to pass.content/docs/guide/ci-cd-pipeline.md :: node-version: 20→ inventoriedanchored.The line is prose inside the "Adding a New Workflow" section:
It is not a version this page teaches — it CITES
node-version: 20as the value the page's own copied YAML block had fossilised at, and says in the same breath that every workflow declares 22 instead. Deleting it would delete true, useful documentation, which is why it is inventoried rather than repaired.Anchor re-measured against
.github/workflowsat this cut: 28node-versiondeclarations across 23 files, ZERO reading 20, all 28 reading 22 (control, same sweep: 19corepack enablesteps). The surrounding claim checks out too — rootengines.nodeis>=22.11, so 20 does sit below the declared floor. The sentence therefore goes false exactly when the workflows move off 22, which is when it should.The
whystring deliberately carries no declaration count. Counts in this ledger's reason strings are what #6400 is open about, and this PR does not touch that entry — see below.Fixture: the two-direction evidence
it('reads a version behind a `name-version:` key, so the second recogniser is not decorative')copies #6307's shape: it writes a workflow-shaped fixture, then rebuilds the pre-#6409 recogniser set inline and asserts it FAILS on the same three lines the current set matches. It also pins the alias counterfactual (with a live control, so the miss is aboutVERSIONand not about a pattern that never matched anything) and the four boundaries in the table above.The fixture is needed even though the corpus has an instance: that instance is a single line of prose, which any docs PR can reword out of existence without knowing it is load-bearing. #6308 had already deleted the live YAML instance before this card was written.
Proven in both directions, mutation and restore, under
trap … EXIT INT TERMwith absolute paths:The second failure is worth noting: the ledger entry itself is a second, independent witness today — under a revert it goes stale and the downward ratchet fires. The fixture is what survives the prose being reworded.
Verification
All at the final commit,
ff2221420:npx vitest run --project unit scripts/__tests__— the whole tree:Test Files 81 passed (81),Tests 2318 passed (2318)(2317 before; the +1 is the new fixture).tsc -p tsconfig.scripts.json --noEmit— exit 0. Confirmed the edited file is genuinely in the checked set via--listFiles(1 hit), so "typecheck clean" is a measurement of this change and not of a set that excludes it.eslint .with the root ignore patterns, the lane that coversscripts/: exit 0, 0 errors, 28 pre-existing warnings, none in the edited file (--format json: 1 file linted, 0 errors, 0 warnings). Package lint lanes are untouched — this diff changes no package source.node scripts/check-control-bytes.mjs—check-control-bytes: OK (scanned 5314 tracked text file(s); skipped 85 binary).node scripts/check-changeset-presence.mjs—No source of a released package changed in this range, so no changeset is owed.An empty-frontmatter changeset is added anyway, as the declaration the gate's header asks for.Not touched
ci-cd-pipeline.md :: Node 22.xreason states 14node-versiondeclarations; the tree has 27 #6400 remains open and untouched. Its subject is theNode 22.xentry's reason string, which states 14 declarations against a tree that now has 28 — my measurement corroborates it and drifted further since it was filed (27 → 28 in the hours between). Different defect form on the same file; the two cards were ruled serial and itsKNOWN_CLAIMSreason string is left exactly as it is.content/docs/releases/edits. No labels or assignees changed.The fence explanation is not carried anywhere here: this gate SCANS fences on purpose, as its own header says, and the real cause is the one measured above.
Generated by Claude Code