Skip to content

fix(docs-audit): give the route:/client: lead one character class - #11558

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-11494-route-newline-double-count
Aug 24, 2026
Merged

fix(docs-audit): give the route:/client: lead one character class#11558
os-steve merged 1 commit into
mainfrom
claude/issue-11494-route-newline-double-count

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#11494

scripts/docs-audit/affected-docs.mjs had eight scans that ask "is a route: / client: declaration written here?". Seven spelled the run between the colon and the value \s*; declarationsIn spelled it [ \t]*. One character class, one character of difference — a newline — and a declaration whose value sits on the next line was seen by both, in two different buckets, so one declaration reached the denominator twice.

Re-derived on this base, not quoted from the card

The card's numbers are from 951b025e. #11500 (2d1290ae) has since rewritten declinedIn in this exact file, so "byte-identical on origin/main" is a statement about a tree that no longer exists. Re-derived on cd932772:

beforeafter
A card fixture — double-quoted value on the next linerows 1 · routesDeclared 3 · declined 2 · brokenScan 1rows 1 · routesDeclared 2 · declined 1 · brokenScan 1
Bsingle-quoted value on the next linerows 1 · routesDeclared 2 · declined 1 · brokenScan 1rows 1 · routesDeclared 1 · declined 0 · brokenScan 0
C wrapped client:clientsDeclared 2 · declined 1 · brokenScan 1clientsDeclared 1 · declined 0 · brokenScan 0
D wrapped non-literaldeclined 1, named route: declined 1, named route: ROUTES.health

A reproduces the card exactly, second entry and all: { key: 'route', line: 2, text: 'route: ' } — an empty value, naming nothing a reader can act on.

B is the louder half the card did not measure, and it is what settles which class wins. The row recognizer already spells \s*, so a wrapped single-quoted value is read as a row — and was then billed unread by the same file, raising a PARTIAL-read verdict with exit 1 on a wholly accurate ledger. That is the false-red direction, which this file prices as costing the same trust a false green does. Making [ \t]* win instead would have had to move the recognizer too — a change to the measured population, which the header of --bridge-coverage attaches a before/after standard to — and would have kept the empty-valued entry as the surviving one.

Note that rows + declined === routesDeclaredkept balancing throughout (1 + 2 === 3). The partition is blind to this by construction: what moved was the population, not the arithmetic.

Route taken: (2), not (3)

Route 3 (claim once by index) is the larger change and strictly weaker here. It suppresses the symptom at each site — the double bill in A is declinedIn + unreadableIn, but in B and C it is the recognizer + unreadableIn, so three separate claim registrations would be needed — and it leaves the two scans still disagreeing about the character class. Route 2 removes the disagreement at the root and fixes all four shapes with one line.

Written once

declLead is now the only place the class is spelled, and all eight lead scans are built from it. The other seven are byte-identical in source and flags — verified mechanically, 1 of 8 changed:

CHANGED declarationsIn built=/\b(route|client)\s*:\s*/g orig=/\b(route|client)\s*:[ \t]*/g
IDENTICAL declinedIn /(route|client)\s*:\s*(["`])([^\n]{0,120})/g
IDENTICAL routeRe · nextRouteRe · windowClientRe · routesDeclared · codeLeads · outsideCode

Priced free on today's tree

Zero live instances re-checked here, not inherited: seven ledgers (unchanged), and grep -nE '\b(route|client)[ \t]*:[ \t]*$' over all seven returns 0 hits (grep exit 1). --bridge-coverage delta across the change:

rowsParsed 268→268 · routesDeclared 268→268 · clientsDeclared 222→222
reachable 45→45 · clientRows 222→222 · unreachable 177→177 · leadsOutsideCode 0→0
brokenScan 0→0 · unreachableRows byte-identical: true

Non-vacuity — both directions, mutation proven on disk

--self-test: 366 → 381 cases. Two ablation legs, each with the mutation confirmed on disk by grepping for the removed and injected text (not an editor exit code), and each restored byte-identically by git hash-object:

  • Leg A — the faithful pre-fix state (declarationsIn reverted to its inline [ \t]*): 8 checks fail, reproducing the card's numbers verbatim — got "1 row / 3 route / 2 declined" and got "line 2: route: \"GET /api/v1/gone\", line 2: route: ". The partition check stays green throughout, which is the point.
  • Leg B — the shared class reverted in declLead (all eight scans at once): 8 checks fail, including row → got "undefined → undefined" — which proves every one of the eight call sites really does read the helper rather than keeping an inline copy.
  • Restore leg for both: hash-object byte-identical to pristine, --self-test exit 0, 381 pass.

Boundary deliberately not crossed

declLead unifies what may sit between the colon and the value. The key part is still each call site's own: declarationsIn anchors with \b, the other seven do not, so subroute: 'GET /x' is a declaration to seven of eight and mints a silent phantom row (rows 2 on a file declaring one, brokenScan 0). Unifying that moves the measured population, so it is filed as #11542 and pinned here — the card that closes it moves the pin instead of finding none.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script derived the change set itself from the merge base; provenance line asserted objectstack). Run at e8a9b04e:

gateverdict line
check:docs-audit-scope✓ affected-docs self-test: 381 cases pass. / ✓ docs-accuracy-audit scope is in sync
check:cross-package-test-inputsOK: 14 package(s) read outside themselves, all declared
check:entry-guard✓ check:entry-guard: 139 scripts/ file(s)
check:parse-guardpass
check:pm-governed-merges✓ check-governed-merges --self-test: 129 assertions
check:pnpm-filter-targets✓ check-pnpm-filter-targets --self-test: 40 assertions
check-ci-filter-parity.mjsOK: all 89 declared cross-package glob(s) (77 unique) are covered
check-affected-docs.mjspass
check-nul-bytes.mjscheck-nul-bytes: OK (scanned 6460 text file(s) … no raw ASCII control bytes)

Repo-wide pnpm lint (eslint . --no-inline-config) was run in full, not narrowed: os-verify-lock: VERDICT command-exit 0 · held the lock 65s. pnpm exec eslint --format json on the changed file alone reports 1 file, 0 errors, 0 warnings.

No changeset: this edits a CI-internal script and releases nothing — the textbook skip-changeset case, in lint.yml's own words. Label applied additively and read back.


Generated by Claude Code

`declarationsIn` spelled the run between the colon and the value `[ \t]*`
while the other seven scans in the file spelled it `\s*`. One character of
difference -- a newline -- and a declaration whose value sits on the NEXT
line was seen by BOTH scans, in two different buckets, so one declaration
reached the denominator twice.
The partition kept balancing while it happened (`rows + declined ===
routesDeclared`), which is why no count comparison could see it: what moved
was the population, not the arithmetic. The reader was shown two entries for
one line, the second reading `route: ` with an empty value.
`\s*` wins because the ROW RECOGNIZER already spells it: a wrapped
single-quoted value is read as a row and was then billed unread by the same
file, raising a PARTIAL-read verdict with exit 1 on a wholly accurate ledger.
Making `[ \t]*` win instead would have had to move the recognizer -- a change
to the measured population -- and would have kept the empty-valued entry.
`declLead` is now the one place the class is spelled, and all eight lead
scans are built from it; the other seven regexes are byte-identical in
source and flags. Priced free on today's tree: 268 of 268 route / 222 of 222
client / 177 UNREACHABLE, delta 0, `unreachableRows` byte-identical.
The KEY part stays each call site's own -- `declarationsIn` anchors with `\b`
and the other seven do not, so `subroute:` mints a silent phantom row. That
moves the measured population, so it is filed separately.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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-steve
os-steve marked this pull request as ready for review August 24, 2026 03:40
@os-steve
os-steve added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 0320a52Aug 24, 2026
33 checks passed
@os-steve
os-steve deleted the claude/issue-11494-route-newline-double-count branch August 24, 2026 03:57
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-steve@claude