Uh oh!
There was an error while loading. Please reload this page.
fix(docs-audit): a PARTIAL ledger read is a verdict, not a smaller number - #10501
Merged
os-zhuang merged 1 commit intoAug 21, 2026
Merged
Conversation
…mber `parseLedgerSource` recognizes a route-ledger row through a single-quote-only regex. The pre-existing broken-scan guard is `rowsParsed === 0`, so it catches only the all-or-nothing case; a ledger where SOME rows are spelled in a quote the recognizer declines parsed fine and reported a smaller population with no verdict and exit 0. Measured on a718ee3, respelling one row of `i18n-route-ledger.ts`: - backtick-quoted `route:` -> 221 -> 220 client-bound rows, no verdict, exit 0 - backtick-quoted `client:` -> 221 -> 220, `rowsParsed` still 3, exit 0 - backtick-quoted `route:` in `rest-route-ledger.ts` -> clientRows still 221, but `meta.getTypes` moved onto the server-only `GET /api/v1/docs`: the row window is delimited by the same single-quote-only lead, so a declined row does not close the previous row's window The last case is why the verdict keys on the DECLINED SPELLING rather than on a count shortfall - the count is blind to it. `parseLedgerSource` now returns what it declined beside what it read, and `bridgeCoverageFrom` turns any declined declaration (or any declared row value it did not read) into a `brokenScan` verdict naming the file, the line and the literal. Both halves of the fraction print on every run. The recognizer is untouched: today's seven ledgers are wholly single-quoted (259 route, 221 client, delta 0), so no verdict can fire on this tree, and `--self-test` pins the population at its old value so a silent widening fails there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. |
os-zhuang
marked this pull request as ready for review
August 21, 2026 02:09
This was referenced Aug 21, 2026
Uh oh!
There was an error while loading. Please reload this page.
os-zhuang
deleted the
claude/issue-9896-docs-audit-partial-ledger-parse
branch
August 21, 2026 02:46
This was referenced Aug 21, 2026
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 freeto 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#9896
The defect
parseLedgerSourceinscripts/docs-audit/affected-docs.mjsrecognizes a route-ledger row through a single-quote-only regex. The pre-existing broken-scan guard isrowsParsed === 0, so it catches only the all-or-nothing case. A ledger where some rows are spelled in a quote the recognizer declines parsed fine and reported a smaller population with no verdict and exit 0 — a partial result that renders exactly like a complete one.1. The spelling census, with its positive control
Every route ledger on
a718ee3dd,route:/client:declarations by quote spelling vs. what the recognizer read:route:declared'"`plugins/plugin-auth/src/auth-route-ledger.tsrest/src/rest-route-ledger.tsruntime/src/route-ledger.tsservices/service-datasource/.../datasource-route-ledger.tsservices/service-i18n/.../i18n-route-ledger.tsservices/service-settings/.../settings-route-ledger.tsservices/service-storage/.../storage-route-ledger.tsclient:: 221 declared, 221 bound, Δ 0 — the same 221 the bridge reports.⛔ That is a zero-hit, so it is claimed only with a positive control. Two of them:
i18n-route-ledger.tsmoves the census to258 of 259and the gate to exit 1 — the counter demonstrably fires (§4).--self-test. The all-single-quoted fixture is pinned todeclined = 0and toroutesDeclared === rows.length; the partial fixture beside it is pinned todeclined = 3. A zero from a counter that cannot fire is not evidence, so the pair is written as a pair.The census also finds the delta is not only about
route:. Three distinct drops, all measured, all exit 0 before this PR:rowsParsedroute:client:(route left single-quoted)route:inrest-route-ledger.tsRow 3 is the shape the
rowsParsed === 0guard cannot see by construction: the row keeps its seat and loses its binding.Row 4 is worse, and it is the one that decided the design. The row window is delimited by the same single-quote-only lead, so a declined row does not close the previous row's window — the row before it inherits the declined row's client. Backtick-quoting
GET /api/v1/metamovedmeta.getTypesonto the server-onlyGET /api/v1/docs, andclientRowsstayed 221. A count comparison is blind to it.2. What "loud" is here, and why
A
brokenScanverdict — exit non-zero — not a warning and not a new verdict word. Argued from what consumes the output, which is three things:scripts/docs-audit/check-affected-docs.mjs, run as a step in.github/workflows/docs-drift-check.yml. Its exit code is the verdict; a warning here is a line in a job log that nothing reads.brokenScanas⛔ N broken-scan verdict(s): ….bridge.brokenScanoutside its collapsed what this run could not see section.All three paths already existed and already carried
brokenScan, so joining that array is the change that reaches every consumer at once — no new field to half-wire (#9433's failure mode).Verdict and not report on the test
bridgeCoverageFromalready applies to itself: a verdict "cannot fire on a tree where the scan works at all". Today's tree is 259 of 259, Δ 0 — this cannot fire. It is a break in the scan, not a property of the route surface, which is exactly what separates it from the 45-of-221 reach ratio: that ratio is a fact about the repo and stays reported, because failing on it would be widening-by-CI, which the #9747 family declines. Respelling a ledger row is neither — it is cheap to fix in either direction (respell the row, or widen the recognizer with the before/after numbers #9432 asks for).Keyed on the declined spelling, not on the shortfall, because of row 4 above: the shortfall is zero in the case that produces a wrong binding. The numerator is reported beside it, not instead of it.
Concretely:
parseLedgerSourcereturns{ rows, declined, routesDeclared, clientsDeclared }.rowscomes out of the loop it always did, byte for byte — the recognizer is not widened, and--self-testpins the population at its old value so a silent widening fails there (verified by ablation, §4).bridgeCoverageFromraises one verdict per ledger that declined anything, naming file, line and literal, capped at three named entries plus a count so a wholly respelled 96-row ledger cannot flood a job log:Both halves of the fraction print on every run, so the denominator no longer has to be known by heart:
.github/workflows/docs-drift-check.yml: the broken-scan banner used to promise the scan "came back structurally empty". That wording would let a reader skip exactly the new case, so it now reads "is broken, not clean".3. Blast radius — what else reads this ledger
Measured, not assumed.
grep -rn "route\s*:" --include='*.mjs' --include='*.mts' --include='*.ts'over the tree returnsscripts/docs-audit/affected-docs.mjsand nothing else.*.conformance.test.ts,packages/client/src/*-route-ledger-coverage.test.ts, …); none reads them as text (grep readFileSyncover that set: zero ledger reads). They see real TypeScript strings, so a quote respelling is invisible to them by construction.--bridge-coveragegate, and the advisory run'sbridgeCoveragerendered in the drift PR comment. Both now carry the verdict.api/client-sdk.mdx,kernel/runtime-services/data-service.mdx, and two release-owned pages. Dropping one of those 6 removes a real doc nomination, including from the page the bridge exists to put back on the list. Dropping any of the other 215 removes no nomination today but silently shrinks the denominator both the gate and the PR comment report — the "smaller number reported as complete" this card is about.One boundary this does not close, filed rather than absorbed as #10500: a
route:whose value is not a string literal at all (route: ROUTES.health) is invisible to the recognizer and to the counter. The only exact discriminator against theroute: string;member every ledger's own entry interface declares is the opening quote — measured: 8 non-quotedroute:occurrences across the seven ledgers, 7 of them that interface member and 1 an English sentence in a comment — so any heuristic separating them is a guess that would red on accurate files today. The boundary is pinned in--self-testand stated in the README instead.4. Ablations
Every mutation was confirmed on disk by grepping for both the injected and the removed text — never by an editor's exit code — and every restore was confirmed byte-clean with
git diff --quiet. No rebuild is involved:affected-docs.mjsis executed directly by node, with nodist/between the edit and the run.A. Reintroduce the dropping spelling (real tree). Backtick-quote one
route:ini18n-route-ledger.ts— on-disk: injected spelling ×1, removed spelling ×0:Before this PR the same input printed
220, no verdict, exit 0.B. The
client:case. Backtick-quote oneclient:, leave itsroute:single-quoted:rowsParsedis still 3 — the old guard stays blind, as designed — yet the verdict fires and names the entry.C. The window-inheritance case. Backtick-quote
GET /api/v1/metainrest-route-ledger.ts:D. Ablate the new verdict itself (
if (false && (l.declined.length || unreadRows > 0)); on-disk: ablated form ×1, original ×0). The self-test reds on 4 pins, each naming what it lost:Restored: original form ×1, ablation residue ×0,
✓ 281 cases pass.E. Ablate in the opposite direction — silently widen the recognizer (
routeReaccepts all three quotes). 5 pins red, including the two that exist to catch exactly this:Restored byte-clean:
✓ 281 cases pass.5. Gates
All run at the final commit
4e7229a6(working tree clean), verdict lines quoted as each gate printed them. Family derived bynode scripts/pm/dispatch-gates.mjswith no path arguments — 3 paths, 10 matched families:affected-docs.mjs --self-test✓ affected-docs self-test: 281 cases pass.check-audit-scope.mjs --self-test✓ check-audit-scope self-test: 24 cases pass.check-audit-scope.mjs✓ docs-accuracy-audit scope is in sync with content/docs/: 181 hand-written doc(s).check-affected-docs.mjs(check:docs-audit-scope+ the drift gate step)ledger rows read 259 of 259 declaredcheck-node-version.mjscheck-node-version: OK (29 setup-node step(s) across 26 workflow(s), all on Node 22).check-workflow-status-functions.mjs --self-test+ run✓ … 34 assertions;OK (scanned 26 workflow file(s), 45 job(s) …)check-required-contexts.mjs --self-test+ run✓ … 124 assertions; exit 0check-shard-attestation.mjs --self-test+ run✓ … 92 assertions;✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) …check-cross-package-test-inputs.mjs --self-test+ runAll 60 self-test cases passed.;OK: 12 package(s) read outside themselves, all declared …check-nul-bytes.mjsOK (scanned 6146 text file(s) … no raw ASCII control bytes).npx eslint scripts/docs-audit/affected-docs.mjsExit codes were captured before any pipe (
cmd > file 2>&1; EXIT=$?), never after atail.No changeset: the diff is
scripts/docs-audit/**and.github/workflows/**— repo tooling, no publish surface at diff time.skip-changesetapplied.Generated by Claude Code