Skip to content

docs(gate): two doc-gate workflow headers point at the gate's own reading instead of copying its count - #7827

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-7448-doc-gate-header-counts
Sep 6, 2026
Merged

docs(gate): two doc-gate workflow headers point at the gate's own reading instead of copying its count#7827
os-sam merged 1 commit into
mainfrom
claude/issue-7448-doc-gate-header-counts

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #7448

The deliverable is not "change 222 to 228"

doc-fence-languages.yml described its scan surface as "the same 222 documents check-doc-snippet-types covers"; doc-component-types.yml as "184 pages (144 .mdx + 40 .md)". Refreshing those literals would only reset the rot clock — nothing goes red when they drift again, which is the card's own diagnosis. So each header now states its population and points at where the live figure is printed, following the two precedents this repo has already applied: check-doc-fence-languages.test.ts's header (counts "deliberately NOT asserted… a hand-copied enumeration in a test drifts by construction") and check-doc-snippet-types.mjs's UNGATED_DOCS header, repaired this way into "a pointer to the list now rather than a copy of its length".

Acceptance question — after this change, if someone adds or deletes a document, is the header still correct? Yes, and mechanically so: the ablation below reintroduces a today-correct literal and the new pins still go red.

The numbers, measured on this branch, not copied from the card

⚠️ The card's table (228 / 189) was read on the #6600 branch at eba0a9b7a. Read today from each gate's own verdict line, my readings differ and win — which is the defect this card exists to end, demonstrating itself one more time:

gate card said its verdict line today
pnpm check:doc-fences 228 every TypeScript block in **227** document(s)
pnpm check:doc-types 189 Scanned **188** doc file(s) (.mdx + .md)

One detail worth the reviewer's attention

184 pages (144 .mdx + 40 .md) is not merely stale. Measured today, content/docs/** holds exactly 184 files, split exactly 144 .mdx + 40 .md. The literal is a precisely correct count — of the population this gate stopped having. objectui#6600 added apps/*/docs/** (3 files) and objectui#7115 added README.md (1), taking the gate to 188. A reader who spot-checked that number would have confirmed it and still been wrong about the scan surface. A copied count does not only go stale; it can keep answering a question the gate no longer asks.

The pin

One it in each gate's existing test file — one gate, one home, so each workflow's header is asserted beside its own gate rather than in a shared sweep that would own neither. It fails when a numeral directly qualifies a document-population noun anywhere in that workflow's header comments.

Deliberately narrow: issue references, node-version, timeout-minutes and "the fifth instance of the shape" are all numbers these headers legitimately carry, and none of them rots when a document is added. The consequence, recorded in the pin's own docblock: a header must not quote another header's stale literal verbatim either — the pin cannot tell a quotation from a claim, and refusing both is the safe direction for a check on prose accuracy. Only the negative half is asserted; a positive "the header names the verdict line" assertion would pin a wording, which is the thing these files already decline to do.

Ablation — the pin fails loudly, twice, in two different ways

(a) Against the genuine pre-fix content. With the pins in place and both workflows at their unfixed origin/main state, both went red and each named itself, the exact offending literals and the card:

× its header states the population and never counts it — no count can rot here
AssertionError: doc-fence-languages.yml's header states a document count (222 documents). …
  expected [ '222 documents' ] to deeply equal []
AssertionError: doc-component-types.yml's header states a page count (184 pages, 144 `.mdx, 40 `.md). …
  expected [ '184 pages', '144 `.mdx', '40 `.md' ] to deeply equal []
Tests  2 failed | 78 skipped (80)

(b) Against a today-CORRECT literal — the leg that proves the pin governs the class rather than the wrong numbers. 227 documents and 188 pages (144 .mdx + 44 .md) were injected; both are accurate today, and both are still refused:

expected [ '227 documents' ] to deeply equal []
expected [ '188 pages', '144 `.mdx', '44 `.md' ] to deeply equal []
Tests  2 failed | 78 skipped (80)

Mutation proven on disk before running (blob hash flip 179f1c9eb to fbf7df954 and 18e8bbdbb to b619002eb, plus an anchor-count flip per file); restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and restoration proven by state: each file's hash back to its HEAD blob and git diff HEAD empty. No build or dist/ is on this path — the tests read the workflow files from the checkout.

⚠️ Worth recording, because it nearly cost the implementation: the first ablation attempt ran before the fix was committed. Its trap fired on the precondition guard's own non-zero exit and reverted both workflows to HEAD — which was still the unfixed content. Nothing errored. The guard is what made this visible (FAILURE: worktree differs from HEAD before mutation), and it is why the ablation legs above were only run once the implementation was committed.

Verification, all on 14748055c

check result
pnpm exec vitest run scripts/__tests__ (whole dir) Test Files 104 passed (104) · Tests 3058 passed (3058)
node scripts/check-doc-fence-languages.mjs --self-test ✓ … self-test: 26 cases pass
node scripts/check-doc-fence-languages.mjs ✅ check:doc-fences — every TypeScript block in 227 document(s) …
node scripts/check-doc-component-types.mjs ✅ Every documented component type is registered.
node scripts/check-changeset-presence.mjs ✅ No source or published contract of a released package changed in this range, so no changeset is owed.
node scripts/check-governed-queue-guard.mjs --test (each workflow) ✅ NOT GOVERNED — 1 path(s) checked against 5 governed surface(s); none matched.
pnpm run type-check:scripts exit 0; --listFiles confirms both edited test files are in the program (so the green measures this diff)
control-byte self-scan over all four edited files no match

ci-cd-pipeline-doc.test.ts — which carries objectui#3653's by-command pin on the CI page's table — is inside that green run. Confirmed, not assumed: this change adds and alters no run: step, so the obligation does not fire.

ESLint, narrowed and the narrowing proven (repo-wide pnpm lint is CI's run): the two edited .ts files lint clean — --format json reports 2 files, 0 errors, 0 warnings. The two workflow edits are outside ESLint's population entirely: asked with the repo's own config, .github/workflows/*.yml resolves to 0 files. And the config enables no type-aware linting (tseslint.configs.recommended, no parserOptions.project/projectService), so this diff cannot move the verdict on any untouched file.

Scope

Clause-② stayed no. No gate predicate, scan population or verdict changed — comments, plus one test each. The prose was checked against the gates' behaviour, not just their counts, and it was right about behaviour: the fence gate's walk and check-doc-snippet-types's walk return identical 227-element lists today, so "exactly the documents check-doc-snippet-types covers" is a true claim and remains the pinned one.

Out of scope, filed as #7825 (finding + domain:devx, unassigned, after a REST dedup over the finding and domain:devx label listings paged to exhaustion with #7448 as a known-hit control): check-links.yml's header carries the same class twice — "15 INTERNAL documents" (17 today) and "the 183 files … in content/docs/**" (184 today). That is not addressed here; #7825 remains open.


Generated by Claude Code

…ding instead of copying its count

`doc-fence-languages.yml` described its scan surface as "the same 222
documents `check-doc-snippet-types` covers" and `doc-component-types.yml`
as "184 pages (144 `.mdx` + 40 `.md`)". Both had drifted, and no check went
red while they did — a count copied into a comment rots by construction.

Refreshing the literals would only have restarted that clock, so each header
now states its POPULATION (which trees, which extensions, which walk derives
it) and points at where the live figure is printed: the gate's own verdict
line, on every run.

A pin in each gate's existing test file makes the class fail loudly: the
header may state the population, never count it. The regex is deliberately
narrow — a numeral directly qualifying a document-population noun — so issue
references, `node-version` and "the fifth instance of the shape" stay legal,
since none of those rots when a document is added.

No gate predicate, scan population or verdict changed; comments only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(gate): two doc-gate workflow headers carry hand-copied document counts that have drifted from what the gates report

2 participants