Skip to content

docs-audit: a route-ledger row whose route: is not a string literal is invisible to BOTH the row recognizer and the new partial-read counter #10500

Description

@os-zhuang

Filed unassigned by the os-dev seat implementing #9896 (session session_01DdCnBGcHeufjrq7drTD3wt, PR for #9896). Recording, not fixing — deliberately out of that card's scope, and the boundary is pinned in --self-test rather than left to be discovered.

What

#9896 makes a PARTIAL ledger read loud: parseLedgerSource in scripts/docs-audit/affected-docs.mjs now reports every route: / client:string literal it declined to read (double-quoted or backtick-quoted), and bridgeCoverageFrom turns that into a brokenScan verdict naming the file, the line and the literal.

That counter is exact for string literals and blind to everything else. A row written as

{route: ROUTES.health,family: 'ops',disposition: 'server-only'},{route: META_BASE+'/types',family: 'metadata',disposition: 'sdk',client: 'meta.getTypes'},

is read by neither half: the recognizer needs an opening ', and the counter counts only values that open with one of the three quotes. The row leaves the population with no verdict — the same silence #9896 closed for the quote spellings, one spelling further out.

Why it was not closed in #9896

The only exact discriminator between a row value and the route: string; member that every ledger's own entry interface declares is the opening quote. Measured on a718ee3dd across all seven ledgers:

ledgerroute: totalquotednon-quoted
plugin-auth/src/auth-route-ledger.ts57561
rest/src/rest-route-ledger.ts97961
runtime/src/route-ledger.ts81792
service-datasource/.../datasource-route-ledger.ts12111
service-i18n/.../i18n-route-ledger.ts431
service-settings/.../settings-route-ledger.ts541
service-storage/.../storage-route-ledger.ts11101

Every non-quoted occurrence is accounted for: 7 are the route: string; interface member (one per ledger) and 1 is an English sentence in a comment in runtime/src/route-ledger.ts:182 ("It never named a mounted route: the branch"). So a naive "count every route:" check would red on all seven ledgers today — a false red on accurate files — and every heuristic that separates the two (trailing , vs ;, an uppercase-initial value, a type-name allowlist) is a guess that will be wrong on the first row that does not fit it. #9896 chose to state the boundary rather than paper over it with one.

How likely is this spelling?

Lower than the template literal #9896 addressed, but not zero. These modules are deliberately import-free (see the header of i18n-route-ledger.ts: "It must stay import-free — the client-side guard imports it as a relative SOURCE file"), so a cross-module constant cannot land. A file-localconst BASE = '/api/v1/i18n' above the table can, and is the natural first move the moment a ledger's rows get long. Today: 0 occurrences.

Direction, not a prescription

  1. Leave it, keep the boundary declared. It is pinned in affected-docs.mjs --self-test ("a non-literal route: is out of scope for the counter, and so is route: string;") and stated in scripts/docs-audit/README.md, so the next reader meets it rather than discovering it. Cost: the gap stays real.
  2. Give the ledgers a lint rule instead of the scanner a heuristic — an ESLint rule over **/*route-ledger.ts requiring every route: / client: value inside the exported table to be a single-quoted literal. This turns the scanner's narrowness into an authoring constraint, which is where it can be enforced exactly, and would subsume the docs-audit: a PARTIAL ledger parse is silent — one backtick-quoted route: drops a row from the bridge population with no verdict and exit 0 #9896 verdict as a second line of defence rather than duplicate it.
  3. Parse the ledgers as TypeScript. Exact, and rejected on the same ground the whole scanner rests on: affected-docs.mjs is a dependency-free source scan precisely so it cannot fail to resolve in CI.

(2) looks like the shape worth costing: it is enforcement at the point of writing, and it makes the scanner's assumption a declared rule instead of an implicit one.

Refs: #9896, #9572, #9433, #4690.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions