Skip to content

test(gate): assert the doc ledger's Node claims against the workflows - #6450

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-6400-anchor-derived-from-workflows
Aug 26, 2026
Merged

test(gate): assert the doc ledger's Node claims against the workflows#6450
os-warren merged 2 commits into
mainfrom
claude/issue-6400-anchor-derived-from-workflows

Conversation

@os-warren

@os-warrenos-warren commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Fixes#6400

Disposition B, as ruled: the node-version set is derived from .github/workflows and the doc literal is asserted against it, so the anchor is machine-checked rather than restated.

The measurement, re-taken on this branch's own base (c8ea8af9c)

$ grep -rhoP 'node-version:\s*\K\S+' .github/workflows/ | sort | uniq -c
1 '22'
27 '22.x'
$ grep -rhoP 'node-version:\s*\K\S+' .github/workflows/ | wc -l
28
$ grep -rlP 'node-version:\s*\S+' .github/workflows/ | wc -l
23
$ grep -rnP "node-version:\s*'22'" .github/workflows/
.github/workflows/half-state-patrol.yml:196: node-version: '22'

28 declarations across 23 files (of 30 workflow files, all .yml). 27 spell it '22.x'; one spells it '22'.

Control, same sweep, same directory: 20 corepack enable steps across 14 files — a non-zero second reading, so this is not a grep that found nothing and was read as agreement.

The entry claimed 14. The half-state-patrol lane's '22' is a spelling that sentence does not admit at all.

The entry's second half, measured"this page already has its own pin test (ci-cd-pipeline-doc.test.ts)":

$ grep -cP '\b22\b' scripts/__tests__/ci-cd-pipeline-doc.test.ts -> 0
$ grep -c 'node-version' scripts/__tests__/ci-cd-pipeline-doc.test.ts -> 0
$ grep -c 'ci\.yml' scripts/__tests__/ci-cd-pipeline-doc.test.ts -> 28 (control)

True of the page, false of the line. Nothing re-measured either half.

Was B feasible, and why — in terms of what the entry shape can express

Yes, and the entry shape needed one new field, not a new mechanism.KNOWN_CLAIMS already had the precedent: skeletonDep (objectui#3855) is exactly "name the machine-readable truth and have the comparison RUN here rather than re-verified by a reviewer". What it could not express is a truth that is not a dependency range in a manifest. So this adds the sibling field workflowVersionKey, naming the GitHub-Actions key whose declarations across .github/workflows are the anchor, plus describe('doc version claims - the workflow-version assertion').

The two conditions the file's own header sets for anchoring were both measurable here:

  1. the tree states exactly one value — 28 declarations, all major 22, unanimous; and
  2. the doc's line is the same KIND of statement — all three pages state what CI exercises, which is precisely what a node-version: declaration is. (This is the test react fails in the header: the repo pins 19.2.8 exactly for test determinism while the page states a consumer's caret range.)

What made it feasible rather than merely desirable is that the anchor is unanimous on the version. It is not unanimous on the spelling — see below, which is where the design decision actually sat.

The '22' outlier — handled by normalising on the MAJOR

majorOf reduces every declaration to its major, and the comparison happens there. A spelling comparison has no correct branch available to it:

  • demanding '22.x' paints half-state-patrol.yml red for agreeing with every other lane;
  • accepting whichever spelling the docs happen to use makes the anchor mean whatever the docs mean.

The major is the fact the doc sentence states; the spelling is how one YAML file happened to write it.

This is load-bearing rather than cosmetic, and mutation A below proves it: the reverse-verification was run by moving that lane. A derivation admitting only '22.x' would have stayed green while the outlier drifted anywhere at all — a new blind spot, shipped by a card about blind spots.

Two further anti-blind-spot measures, both because a line regex's real risk is not matching at all:

  • a value majorOf cannot read (a matrix expression, an lts alias) is reported, not skipped — a parser that silently drops what it cannot read is how an anchor goes vacuous while every gate stays green;
  • the parser is cross-checked against a counter that knows only the key, over the same files, and the two must name the same lines. A regex that quietly stops matching some spelling would otherwise subtract a lane from the anchor and look exactly like a smaller CI.

Scope: three entries, not one — declared

The card names ci-cd-pipeline.md :: Node 22.x. Two more entries state the same literal against the same anchor, and both of their counts were also stale, measured on this base:

entryreason saidtree holds
ci-cd-pipeline.md :: Node 22.x14 declarations, all 22.x28 across 23 files, 27 as '22.x'
building-crud-app.md :: Node 22.x"of the 27 …, 26 read '22.x' and the 27th reads '22'"27 of 28
quick-start.md :: Node 22.x"(26 of 27 … read '22.x')"27 of 28

All three now carry workflowVersionKey: 'node-version' and none of their reasons states a count any more. Building a guard and then leaving two members of its own domain hand-written would have left the identical defect one line from its own repair — the sample-misfiling failure this file's header already records. Same defect class, same file, same gate family, one-line mechanical change each, evidence pinned by the derivation itself.

Two-direction proof

The assertion must be able to go red, or it is prose with extra steps. Both mutations restored via git checkout HEAD -- ABSOLUTE-PATH under trap … EXIT INT TERM, and restore proven by hash, not by exit code.

HEAD = 2bc962f7e
HEAD blob .github/workflows/half-state-patrol.yml = ae3e427aa3ff0aa45476c6af468bddd111ea5623
0. BASELINE vitest exit 0 (25 passed)
A. OUTLIER LANE half-state-patrol.yml '22' -> '24'
injected "node-version: '24'" = 1 (expect 1) <- mutation proven on disk
deleted "node-version: '22'" = 0 (expect 0) both directions, anchored text
vitest exit 1 RED
-> content/docs/guide/ci-cd-pipeline.md :: .github/workflows does not agree on
node-version, so there is no single version for the docs to state: "22.x" in 27 …
restore: hash-object = ae3e427aa3ff0aa45476c6af468bddd111ea5623 MATCHES HEAD blob
git diff HEAD over .github/workflows: EMPTY
B. EVERY LANE node-version -> '24.x' across 23 files
injected "node-version: '24.x'" = 28 (expect 28) <- mutation proven on disk
deleted "node-version: '22…'" = 0 (expect 0)
vitest exit 1 RED
-> content/docs/guide/ci-cd-pipeline.md:215 the page states "Node 22.x" (major 22);
every node-version declaration in .github/workflows states major 24 - "24.x" in 28 …
(same for building-crud-app.md:12 and quick-start.md:12)
restore: hash-object = ae3e427aa3ff0aa45476c6af468bddd111ea5623 MATCHES HEAD blob
git diff HEAD over .github/workflows: EMPTY
FINAL git status --porcelain: empty · git diff HEAD: 0 lines · vitest exit 0 (25 passed)

The two mutations exercise different branches: A the unanimity check (and, decisively, that the '22' lane is inside the anchor set — note "22.x" in 27, i.e. the 28th was the mutated outlier), B the page-vs-anchor comparison the card is actually about.

The red direction is also pinned permanently, not just demonstrated once: it('treats two spellings as one anchor, and goes red when a lane and a page disagree') plants a temp workflow directory with '22.x' + '22' (one anchor), then a third major (split), then an unreadable value (reported, not dropped). A green anchor assertion looks exactly like an assertion that compared nothing — the failure this card was filed about — so the fixture is the witness, in the same spirit as objectui#6307's and objectui#6409's.

Nothing here is a build/dist ablation: this gate reads source files directly, so there is no dist leg to rebuild or preflight.

Gates run — all at the final commit 2bc962f7e

gateresult
pnpm exec vitest run scripts/__tests__/ (whole tree, as instructed)81 files / 2323 tests passed, exit 0
pnpm type-check:scriptsexit 0 — and --listFiles confirms doc-version-claims.test.ts is in the compiled set (1 hit), so this is not the "typecheck excludes tests" non-measurement
pnpm lint:rootexit 0
pnpm exec eslint scripts --format json144 files linted, my file present with 0 errors; the 2 files carrying warnings are pre-existing and untouched
pnpm check:control-bytesOK (scanned 5326 tracked text file(s)), exit 0
control-byte self-scan on changed fileszero hits (control, same files: 2663 lines match [\x20-\x7e])
node scripts/check-changeset-presence.mjsexit 0

Heavy runs went through the container's shared verify lock; VERDICT command-exit 0 · held the lock 57s · waited 0s.

Declared narrowing: repo-wide pnpm lint (turbo run lint, the per-package fan-out) was not run locally. My diff is entirely under scripts/ and .changeset/, which turbo run lint structurally cannot reach — scripts/ is not a workspace package, which is the same fact tsconfig.scripts.json's own header records. The scope that does cover it, lint:root, was run to completion, and eslint scripts was run separately with the file list read from eslint's own resolution rather than from my guess about which files count. Type-aware linting is not enabled in eslint.config.js (no project, projectService or parserOptions entry), so this diff cannot move the verdict on any file it does not touch. CI runs the full farm regardless.

Out-of-scope findings, filed unassigned

Deliberately left uncovered, recorded in the header

ci-cd-pipeline.md :: node-version: 20 (objectui#6409's entry) sits one line above and cannot share this field: it cites a value the workflows do not declare, so its sentence is true exactly when this comparison would be false. It stays reviewer-checked on purpose — and its why already carried no count, which is the instinct this card generalised. objectui#6409's keyed recogniser and its pre-fix-recogniser fixture are untouched.

Changeset

.changeset/6400-workflow-version-anchor.md, empty frontmatter — the explicit "not releasing" declaration. No package source changed. (This repo has no skip-changeset label; the empty-frontmatter changeset is the first-class equivalent here.)


Generated by Claude Code

The version-claim ledger's `ci-cd-pipeline.md :: Node 22.x` entry was
`anchored` -- the strongest class this file has -- and for an entry carrying no
machine-checked field the `why` string IS the anchor: a reviewer re-runs the
command it describes. Both halves of that sentence had gone false, and nothing
in this repository was in a position to notice.
Measured on c8ea8af:
$ grep -rhoP 'node-version:\s*\K\S+' .github/workflows/ | sort | uniq -c
1 '22'
27 '22.x'
$ grep -rlP 'node-version:\s*\S+' .github/workflows/ | wc -l
23
The reason named "the 14 node-version: 22.x declarations across
.github/workflows" -- half the tree's 28, in a spelling one lane does not use
(`half-state-patrol.yml:196` writes `'22'`). It then credited this page's own
pin test with covering the line: `ci-cd-pipeline-doc.test.ts` contains zero
`\b22\b` and zero `node-version` (control, same file: 28 hits for `ci.yml`).
Control for the census, same sweep and same directory: 20 `corepack enable`
steps across 14 files.
The CLAIM stayed true -- the page says Node 22.x and CI runs Node 22. What
rotted is the sentence a reader re-measures it by.
Correcting 14 to 28 reinstalls the same mechanism with a newer number, and the
number moves whenever a lane is added -- twice on the day this was written. So
`workflowVersionKey` joins `skeletonDep` as the second way an entry names its
anchor and has the comparison RUN: it names the GitHub-Actions key whose
declarations across `.github/workflows` are the anchor, and the new
workflow-version assertion reads them on every run and demands the page state
their major.
Three entries carry it, not one. `building-crud-app.md` and `quick-start.md`
state the same literal against the same anchor and both counts were stale too
(each said 26 of 27; the tree holds 27 of 28). None of the three reasons states
a count any more.
The comparison is on the MAJOR, not the spelling. A spelling comparison has no
correct branch available: demanding `'22.x'` paints the `'22'` lane red for
agreeing with every other lane, and accepting whichever spelling the docs use
makes the anchor mean whatever the docs mean. A value `majorOf` cannot read is
REPORTED rather than skipped, and the line parser is cross-checked against a
counter that knows only the key -- a regex that quietly stops matching a
spelling would otherwise subtract a lane and look like a smaller CI.
Left uncovered on purpose, each recorded in the header: the neighbouring
`node-version: 20` entry, whose sentence is true exactly when this comparison
would be false; the two `pnpm 10.x` reasons, same class but anchored on the root
`packageManager` field rather than a workflow key; and the page's own prose
absolute at ci-cd-pipeline.md:1744.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CZgmFFzqA9cX8tBMhvpFe
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(gate): the version-claim ledger's ci-cd-pipeline.md :: Node 22.x reason states 14 node-version declarations; the tree has 27

2 participants

@os-warren@claude