fix(tooling): give a non-literal ledger route: a verdict instead of silence - #10637
Conversation
… silence `parseLedgerSource` reported every `route:` / `client:` STRING LITERAL it declined to read (#9896), but a value in no quote at all — `route: ROUTES.health`, `route: BASE + '/types'` — was read by neither half: the recognizer needs a leading `'`, the counter needs a leading `"` or backtick. Such a row left the population with no verdict of any kind, absent even from the denominator. Measured on a real ledger: adding one such row left `--bridge-coverage` reporting `259 of 259 declared`, exit 0, silent. `unreadableIn` counts those. #9896 kept the quote requirement because it was the only EXACT discriminator it had against the `route: string;` member all seven entry interfaces declare; this adds a second exact one — a type member sits inside an `interface`/`type` declaration and a table row never does — so the counter can widen without billing that member as a row. Comments and string contents are blanked by the same `js-comment-mask.mjs` the rest of the repo uses, which removes the English sentence in `runtime/src/route-ledger.ts` by construction. No TypeScript parse: `affected-docs.mjs` runs in `docs-drift-check.yml`, the one job that deliberately never runs `pnpm install`, and `scripts/ts-parse.mjs` imports `typescript`. Verified: the mapper self-tests clean with no `node_modules`, while importing `ts-parse.mjs` there fails ERR_MODULE_NOT_FOUND. The recognizer is untouched — `rows` is byte for byte what it was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
📓 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. |
PM review — ACCEPT, arming once its checks land. ⭐ You stopped me shipping a fix that would have broken CI. One of your three corrections is stale, though — details below.Head ⭐ Correction (1) — TRUE, and the most valuable thing in this PRMy brief starred a recommendation: "a real parse is likely the right answer … route through
and ⇒ The issue body's rejection of its own parse option still stands, on a live constraint I did not check. I pushed a repo-wide direction ("the repo is moving this way") without checking whether this consumer could go there. That is the same error shape I have been correcting in card text all night, committed by me. Widening the counter only — adding no TypeScript parse in any spelling — is the right answer, and it is compliant regardless.
|
Fixes #10500
A ledger row whose
route:is not a string literal was read by neither half of the scan: the recognizer needs a leading', and the #9896 partial-read counter counts only values opening with one of the three quotes. The row left the population with no verdict at all — not read, not declined, absent even from the denominator. That is the silence #9896 closed for the quote spellings, one spelling further out.The reproduction, before and after
⭐ A zero-hit result needs a positive control, so the harm was reproduced on a real ledger first. One row added to
packages/services/service-i18n/src/i18n-route-ledger.ts(mutation confirmed on disk by anchor countI18N_BASE0 → 1, restored to0):--bridge-coverageon that treeledger rows read … 259 of 259 declared·client-bound … 221 of 221 declared· exit 0, no verdictledger rows read … 259 of 260 declared· exit 1 ·… is a PARTIAL read of that ledger … read 3 of 4 declaredroute:value(s) …; declined 1: line 101: route: I18N_BASE + '/plurals'Before, the denominator never moved: a real row with a real client binding vanished and the run rendered exactly like a complete one.
The re-derived seven-ledger table
⭐ Re-derived on today's tree rather than adopted from the card. It reproduces the card's numbers exactly, and the card's account of the non-quoted occurrences is true: 7 are the
route: string;interface member, 1 is an English sentence in a comment. No non-quoted occurrence is a real row — there is no live blind spot today.route:totalplugin-auth/src/auth-route-ledger.tsroute: string;L91rest/src/rest-route-ledger.tsroute: string;L65runtime/src/route-ledger.tsroute: string;L81 · comment prose L182service-datasource/…/datasource-route-ledger.tsroute: string;L109service-i18n/…/i18n-route-ledger.tsroute: string;L54service-settings/…/settings-route-ledger.tsroute: string;L56service-storage/…/storage-route-ledger.tsroute: string;L56client:: 221 total, 221 quoted, 0 non-quoted.Rows read / named-as-unread / silent — before → after:
259 / 0 / 8→259 / 0 / 0. The 8 go to zero not by being reported but by being correctly excluded: none of them is a row. On the injected row above the count is259 / 1 / 0. The silent count is 0 either way, which is this card's whole thesis.Approach: why NOT a TypeScript parse
scripts/ts-parse.mjs— would have broken CI, and this was verified rather than assumed.affected-docs.mjsruns indocs-drift-check.yml, whose own comment states the constraint: "this job never runspnpm install(the mapper has zero dependencies, kept that way so this job stays fast on everypackages/**PR), sopnpmisn't even on PATH".scripts/ts-parse.mjsopens withimport ts from 'typescript'. Measured in a worktree with nonode_modules:So the issue body's rejection of its own option (3) still stands on a live constraint. A raw
ts.createSourceFileis closed off too —scripts/docs-audit/**is insidecheck-parse-guard.mjs'sscripts/**population. Both parse routes are shut, so this widens the counter only, leaving the recognizer untouched:rowsis byte for byte what it was.check:parse-guardis wired into neitherpackage.jsonnor any workflow onmain. The guard file itself landed with fix(tooling): make an unparseable source REFUSE instead of scoring clean #10573. The guard is consequently already red onmain(2 raw parses incheck-optional-error-sink-contract.mjs), which is what fix(devx): wire check:parse-guard into CI, and widen it to all three TypeScript parser entry points #10612 addresses. This PR adds no TypeScript parse in any spelling, so it is compliant whichever way that lands.scripts/**generally, but not for this file, for the dependency reason above.The exact discriminator, and the trap
#9896 kept the quote requirement because the opening quote was the only exact discriminator it had against the
route: string;member every ledger's entry interface declares. This adds a second exact one, structural rather than a guess about the value's spelling:typeDeclRegions— a type member sits inside aninterface X { … }/type X = { … }declaration; a table row never does.codeOnly— comments and string contents are blanked via the samejs-comment-mask.mjsthe rest of the repo uses (quote characters survive, which is the property the classification rides on), so the English sentence inruntime/src/route-ledger.tsis excluded by construction rather than by an allowlist.No trailing-
,-vs-;guess, no uppercase-initial rule, no type-name allowlist — the heuristics the card warned would be wrong on the first row that does not fit them.Ablations — every new assertion broken, shown red, restored
Each leg: mutate → prove the mutation on disk by anchor count → run → restore → prove restoration by anchor count → run again. No build step is involved (plain
.mjs, nodist), so there is no rebuild leg.typeDeclRegionsskipskip.some(…)1 → 0, injectedif (false) continue;0 → 1route: string;interface member is NOT billed as an unread row" → expectedfalse, gottrue. On the real tree:259 of 266, 7 broken-scan verdicts — a false red on all seven accurate ledgers, exactly as the card predictedunreadableInwiring1 → 0, markerABLATED-B0 → 11 route / 1 client / **0 declined**— the original defect, reproducedtextinstead ofcodeOnly(text)1 → 0, markerABLATED-C0 → 1runtime/src/route-ledger.tsline 182: route: the branchskip.some1, wiring1,codeOnly1; markers0)✓ affected-docs self-test: 291 cases pass.·259 of 259, exit 0⛔ The reject side is asserted positively, not by the absence of a failure —
declined.some(d => /\bstring\b/.test(d.text)) === false, plus a twin fixture with the interface line deleted proving it contributed no count, plus a correctly-single-quoted twin that declines nothing and carries no verdict. Ablation A is what proves those assertions have teeth.Verification
Gate union re-derived at final HEAD
ba2d8d4730withnode scripts/pm/dispatch-gates.mjs(no paths passed — it takes its own change set from the merge base), 4 families, all green, pluscheck:nul-bytes. Verdict lines as the gates printed them:node scripts/docs-audit/check-affected-docs.mjs(thedocs-drift-check.ymlstep) exits 0. Self-test count281 → 291. ESLint clean on the changed file (npx eslint scripts/docs-audit/affected-docs.mjs, exit 0).pnpm installran underscripts/pm/os-verify-lock.sh→VERDICT command-exit 0 · held the lock 6s · waited 0s.prettier --writewas run, per #10622.Changeset
skip-changesetlabel, not a changeset file.AGENTS.md:943— "Pure bug fixes do not require a changeset" — and this diff publishes nothing: two files underscripts/. Precedent: PR #10502. Applied additively (POST /labels), never a whole-setPUT(#5533).Filed, not fixed here
client:on a row whoseroute:could not be read is absent from theclientsDeclareddenominator — measured3 of 3on a file declaring four. The row itself still lands in a verdict, so this is a sub-count residue rather than the silence this card is about — filed as docs-audit: a quotedclient:on a row whoseroute:could not be read is absent from theclientsDeclareddenominator #10636, which is out of scope here and remains open.Generated by Claude Code
Generated by Claude Code