Skip to content

fix(docs-audit): read the route bridge’s handler window off code, not prose — a leaf symbol named in a comment is not a route’s implementation - #9502

Merged
os-steve merged 1 commit into
mainfrom
claude/issue-9432-comment-text-route-anchors
Aug 18, 2026
Merged

fix(docs-audit): read the route bridge’s handler window off code, not prose — a leaf symbol named in a comment is not a route’s implementation#9502
os-steve merged 1 commit into
mainfrom
claude/issue-9432-comment-text-route-anchors

Conversation

@claude

@claudeclaudeBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Fixes#9432

The residue is real, and it is bigger than the specimen that named it

#9431 barred container symbols from the route bridge, which closed the card's measured
specimen. The card predicted the residue one layer down — a leaf symbol named only in
comment prose bridges just the same — and asked for that to be measured before anything was
stripped. It reproduces on current main, twice over.

Structural, on today's tree. Across the 19 files the bridge reads as registrars, the
handler-window scan sees 7,649 identifier slots raw and 3,320 with comments masked: 57%
of what the scan reads is prose. Filtering to code-shaped, non-SCREAMING_SNAKE names that
would mint a route anchor matching at least one page: 99 identifiers reachable from
comment text alone.

Live specimen, 40d5b2d4c (#9405, metadata-protocol, batch publish returns per-draft
advisories). Both route anchors that run produces come from prose and nothing else:

promoteDraftForPublish -> /:type/:name/publish rest-server.ts:5324,5376 comments only
publishPackageDrafts -> /:name/state/:field rest-server.ts:5694,5722 comments only

Neither handler calls the symbol its comment names — the publish handler calls
publishMetaItem, which this diff never touched. Six pages listed, three of them on those
two ghost routes, including protocol/objectql/state-machine.mdx on a diff that goes nowhere
near a state machine.

Both sentences are ordinary, careful implementation commentary about a neighbouring door.
That is the point: this is not a smell anyone should be asked to stop writing.

Fix

parseRegistrarSource reads maskComments(text) instead of text. Everything that function
claims is a claim about what a handler does, and prose is not a handler doing anything.

The mask covers the path: scan too, which is the same rule rather than a second one: a
route registration inside a JSDoc @example is an illustration, not a registration.
route-manager.ts has exactly one, and it mints a phantom /api/users/:id today — route
tails go from 43 to 42 and the one that leaves is that phantom. A commented path: line also
stops truncating the previous handler's window; measured on today's tree that widening adds
0 identifiers to any window, but the mechanism is worth knowing about.

Projection: maskComments, not stripComments. This scan reasons in line positions (a
window is 150 lines past a site) and in what precedes path: on a line; blanking keeps both,
deleting moves columns. The #9367 lazy-matcher hazard does not apply — the one lazy
quantifier here is (.*?) inside a per-line regex, bounded by a line, never dragged
across a file. Measured: 115 ms to mask the 19 registrar files (888 KB), a whole run going
387 ms to 480 ms.

The card's other question: should comment text be a weaker doc anchor?

No, because there is nothing to keep. #9431's container carve-out had something to preserve —
RestServer (symbol) is a correct row. Here the comment mention feeds only the bridge: a
symbol named in a comment is already a doc anchor if the diff touched its own declaration
(§3 derives symbolAnchors from the declaration side and never looks at handler windows), and
it is not one if the diff didn't. Masking removes a false bridge hop and no anchor.

Measured, before and after — verified at a6b134644

main = origin/main at 017c27e73 · fixed = this branch. Same tool run twice per commit,
against a worktree checked out at that commit.

commitmainfixeddelta
9e2e68206 (#9294's specimen)3 docs / 2 anchors3 / 2none
30b1c636a (#9192 headline)4 docs / 2 anchors4 / 2none
3851f87f0 (#9192 headline)18 docs / 36 anchors18 / 35removed /forms/:slug/lookup/:field (route); no page moved
07ad42463 (#9192 headline)1 doc / 3 anchors1 / 3none
40d5b2d4c (this card's specimen)6 docs / 9 anchors3 / 33 pages, 6 anchors removed

3851f87f0 is the card's own specimen file: SecurityPlugin is already barred by #9431, and
what leaves now is the second, leaf-carried instance in the same fileFieldSchema
reaching /forms/:slug/lookup/:field off one comment line (7996).

Recall cost: measured at ZERO over 60 commits

The card said stripping "looks mechanical but should be measured first", because a comment
often names the very symbol its handler calls. So: the last 60 commits touching
packages/
, both tools, whole output diffed. 57 identical. 3 changed. 0 anchors gained.
Every row that moved is a wrong row:

commitwhat leftwhy it was wrong
19539b4b2 (strict Field.inlineColumns schemas)ui/forms.mdx via /forms/:slug/lookup/:fieldFieldSchema off one comment line; the page has 0 occurrences of inlineColumns, relatedListColumns or InlineGridColumn
0668f02a6 (REST error responder)shares.revoke (sdk), /:object/:id/shares/:shareId (route)sendError off one comment line at 8849; its genuine code-derived routes all survive; no page moved
40d5b2d4c (batch publish advisories)client-sdk.mdx, kernel/contracts/metadata-service.mdx, protocol/objectql/state-machine.mdxsee below

The 40d5b2d4c row is the one that looked like a genuine loss and is not, which is worth
spelling out because the transitive chain is real: publishMetaItemdoes call
promoteDraftForPublish. But the two hunks that made promoteDraftForPublish an anchor are
doc-comment-only — they rewrite a paragraph about what the batch caller now does. That
method's behaviour never moved, so publishItem's does not either. And the pages agree:
state-machine.mdx and metadata-service.mdx contain zero occurrences of any of the
three changed symbols, and client-sdk.mdx names only publishItem, a sibling door this
diff did not touch. Three wrong pages, and the tool keeps the three it had right.

Self-test — 212 to 220, both directions plus two counterfactuals

The fixture is the real specimen's shape: a publish handler whose comment names
promoteDraftForPublish while its code calls publishMetaItem, followed by a JSDoc
@example registering /api/users/:id.

  • the comment-named leaf does not enter the window, and selects no route end to end;
  • the called leaf does, and still selects its own route — an exclusion-only test would
    pass on a fix that broke the bridge outright, which trades a precision bug for the recall
    hole the bridge exists to fill;
  • @example mints no tail, and a // inside a string literal (a URL) blanks nothing — that
    last one goes red if this scan is ever rewired to a private //-to-end-of-line regex
    instead of the shared scanner;
  • counterfactuals: the pre-fix bare-token pass, verbatim, still reads
    promoteDraftForPublish out of that window, and that @example line still matches the
    registration-site regex. Without them this block would pass just as happily on a fixture
    that drifted into carrying no comment at all.

Reverse-verified from the committed state — restoring text.split turns exactly the three
expected cases red (220 pass to 3 case(s) failed), no other movement.

Verification

Run after the final commit, at a6b134644:

node scripts/js-comment-mask.mjs --self-test -> 15 cases pass
node scripts/docs-audit/affected-docs.mjs --self-test -> 220 cases pass
node scripts/docs-audit/check-affected-docs.mjs -> 220 cases pass
pnpm check:docs-audit-scope -> 220 + 22 cases, scope in sync (179 docs), 9 release pages read-only
pnpm check:nul-bytes -> 75 assertions; 6130 files, no raw control bytes

Gate family re-derived from the actual changed path with
node scripts/pm/dispatch-gates.mjs scripts/docs-audit/affected-docs.mjs; it named the two
check families above and nothing new.

Scope

One file. skip-changeset: scripts/docs-audit/ is repo tooling, not a published package,
so this PR releases nothing.

#9331 is not addressed here and nothing in this diff goes near it — that card validates an
anchor against its target page at report time; this one is about which identifiers the
source side is entitled to mint an anchor from. Different hop, no overlap.

One thing deliberately left alone: literalAnchorsFromLines reads route literals off changed
lines including comment lines. That is not the same defect and is not obviously a defect
at all — its promise is "the diff touched a line naming this route", which a changed comment
satisfies honestly, unlike the bridge's "this symbol IS this route's implementation". Left
untouched rather than swept in.


Generated by Claude Code

@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 18, 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.

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

Development

Successfully merging this pull request may close these issues.

docs-drift's route bridge counts identifiers found inside COMMENT text — an English sentence in a handler can mint a route anchor

2 participants

@os-steve@claude