refactor(docs-audit): a route SOURCE of two kinds — admit spec contract declarations behind a runtime-registration guard - #15274
Merged
Conversation
… declarations Rename the "registrar" concept in affected-docs.mjs to a route SOURCE with two kinds -- a registration call site (the existing filename convention) and a spec contract declaration (admitted by evidence) -- and implement the evidence route behind a runtime-registration guard. The guard is the HTTP method the declaration answers on, read beside the path, rather than a hand-kept exclusion list: it keeps out the whole class of data payloads that merely carry a `path:` key, of which the connector-action input in packages/spec/src/conversions/registry.ts is today's only instance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
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. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
…n them Adds the #11857 self-test battery (21 cases): the guard admits a contract declaring its verb beside its path, declines a connector-action input, and the counterfactual proves the parser reads that payload's tail so the guard is what declines it. Live pins for the real registry.ts, the five admitted declarations, and the measured no-op of excluding route ledgers from kind (a). Roster 28 -> 29 batteries, floor raised with the cases. Renames the concept in scripts/docs-audit/README.md to match the script. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…annot pass vacuously Ablation B (evidence route disabled) left the battery green: `every()` over an empty list is true, so the pin that says WHAT kind (b) admits said nothing about WHETHER it admits. Adds the size floor and names the declaration the storage ledger's closure rests on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 4, 2026
The `route` anchor's own `from` note still read "its registrar handler names it" -- the one place the old word reached a user-visible string rather than a comment. Found by running the tool in normal mode on a real diff. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 4, 2026
baozhoutao
marked this pull request as ready for review
September 4, 2026 08:44
baozhoutao
enabled auto-merge
September 4, 2026 08:44
This was referenced Sep 4, 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 free
to 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 #11857
Implements maintainer ruling A (comment
5537060359, 2026-09-04 decision batch #31): a spec contract declaration IS a route source for doc nomination — admitted under a renamed concept, behind a runtime-registration guard.All figures below are measured on this branch at
f0dd5ab79(baseorigin/main460134af8), re-measured rather than copied from the card.1. The control, re-derived FIRST
The card's rule: if the control does not reproduce, the delta means nothing.
a6eca9223)460134af8)The file count reproduces exactly; tails and reachable have each moved by tree content since 2026-08-25. That drift is not mechanism drift, and it was isolated rather than assumed: running today's tool against the card's own ref
a6eca9223reproduces12 / 43 / 45exactly, so nothing in PR #12146's ceiling work moved this reading. (Ledger files went 7 → 11 and rows 269 → 290 over the same window — the tree grew, the recognizer did not change.) Control reproduces; proceeding.2. What changed: one word, two constructs
Before this card
REGISTRAR_FILE_RE, its docblock and its--self-testall meant the file that registers the route, while the widening the card measured admitspackages/specZod contract declarations, which register nothing. Per the ruling the rename is a condition of admitting them, not a tidy-up after.A route source is a file whose source declares a route, of two kinds:
CALL_SITE_FILE_RE, the existing filename convention — unchangedpackages/**/*.tswhose masked source declares a route beside the HTTP method it answersRenamed throughout:
REGISTRAR_FILE_RE→CALL_SITE_FILE_RE,parseRegistrarSource→parseRouteSource,REGISTRAR_HANDLER_WINDOW→ROUTE_SOURCE_HANDLER_WINDOW,registrarByTail→routeSourceByTail, theno-in-repo-registrarcause →no-in-repo-declaration, and the user-visiblerouteanchor provenance string. One forced caller change, declared:scripts/docs-audit/README.mddocuments this concept and would otherwise have kept the retired word with the retired meaning — the exact "one term, two things" the ruling forbids.3. The guard is a signal, not a file name
The ruling allowed "a runtime-registration signal or an explicit, self-tested exclusion" for
packages/spec/src/conversions/registry.ts. This implements the signal: a route declaration names the HTTP verb it serves; a data payload carrying apath:key does not.Measured on
460134af8— ofregistry.ts's 61 literalpath:sites (its/api/v1/healthis a connector-action input inside an automation fixture), zero carry an HTTP method; the five contract declarations carry one at 61 of their 62 sites. The 62nd has itsmethod:five lines but zero properties up, behind a JSDoc thatmaskCommentsblanks — the lookaround skips blank lines for exactly that reason. The separation is total at every lookaround from 1 to 6, so the constant is a margin, not a threshold. A name-based exclusion would have kept one file out and taught the recognizer nothing; the signal keeps out its whole class.⛔ The fixture and the benchmark are not re-excluded by name.
isTestFilegrew those arms in #12965, so the walk never offers them to either kind — pinned in--self-testover the two real repo paths, per the ruling.4. Before/after ledger table (re-measured)
node scripts/docs-audit/affected-docs.mjs --bridge-coverageon460134af8→f0dd5ab79:discovery-gaprowsstorageledger unreachablei18nledger unreachablerestledger unreachableruntimeledger unreachableplugin-authunreachableNo reach regression is possible by construction:
selectsFromis asome()over the tail list, so added tails can only add selections.discovery-gap → 0is the load-bearing reading — every row the ceiling said a widened discovery could reach is now actually reached.One narrowing, declared: route ledgers are excluded from kind (a). Most ledger names match the call-site convention (
route-ledger.tscarriesroute), so every ledger used to be parsed as a registrar too. All 11 live ledgers declare zero route tails, so this moved nothing — asserted live in--self-testrather than argued, so it reds the day one starts.5. Do the newly reachable rows actually nominate docs? (reported, not required to fix)
Tried on a real diff:
bd4aa4e49("name the terminally-failed-but-repairable run on AutomationResult.status as 'stranded'", #14636), which touchespackages/spec/src/api/automation-api.zod.ts— a newly admitted contract declaration.The new anchor is
route/api/automation/:name/trigger, "bridged from symbolTriggerFlowResponseSchema" — produced through the newly admitted declaration, and absent before.⇒ Reach is necessary, not sufficient, and this diff shows exactly that. The evidence route did fire on a real change and produced a route anchor the run did not previously have; every page that anchor selects was already nominated by other anchors, so the page set did not move. On this diff the gain is in the advisory's stated reach and in anchor provenance, not in the doc list. A diff whose only bridgeable symbol lives in a contract declaration is where the page set should move; this one had two other symbol anchors carrying the same pages.
Verification
--self-test: 545 → 568 cases, roster 28 → 29 batteries,SELF_TEST_BATTERY_FLOOR28 → 29, new battery floored at 23. The verdict handshake is unchanged; the new cases register through the existingbattery()helper.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack) — 23 commands, all rc=0, re-derived on the final headf0dd5ab79and identical to the first derivation; plus the always-runs tail (check:pm-dispatch-gates,check:self-test-wired,check:declared-population-live,check:ratchet-remedy-authority,check:nul-bytes) all rc=0. The script's own suite —--self-test,--bridge-coverage, normal mode on a real diff,check-affected-docs.mjs,check-drift-comment.mjs,check:docs-audit-scope— all rc=0.requireMethodSignal: false):registry.tsis admitted (contract declarations 5 → 6, tails 78 → 79) and--self-testnames it by path.--self-testnames the missing kind.every()that passes vacuously on an empty list, so the whole battery stayed green with kind (b) switched off. Fixed by flooring the population size (commitfa12687d1) before the ablation was re-run; that is the only reason the pin exists.EXIT INT TERMtrap with absolute paths, proved the mutation landed on disk by blob hash before reading any result, and restored to blob-hash equality withHEADafterwards. One mutation attempt matched zero anchors and was caught by that check rather than being read as a clean ablation. No rebuild leg applies — this script is run directly as.mjs, with nodist/.eslint --no-inline-configover the changed files — 0 errors. The narrowing is safe by three readings: the population comes from eslint's own config (it reports the.mdas "ignored because no matching configuration was supplied", identically onorigin/main), the count comes from--format json(2 entries, 1 linted), and type-aware linting is not enabled (noparserOptions.project), so this diff cannot move any untouched file's verdict.Figure restatement posted per the ruling: #10534 · #9572. Both read back byte-identical.
skip-changeset: this publishes nothing from any package (scripts/**only). Label applied and read back.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code