Repository navigation
Description
Carved out of #10534 as its own card, which is what that card's own text and its triage grading both instructed once the fence cleared. The fence has cleared — PR #10501, which owned scripts/docs-audit/affected-docs.mjs, merged 2026-08-21T02:46:52Z (commit 4e7229a6). Two separate seats verified that independently rather than taking the card's word for it.
Filed unassigned and unlabelled for triage grading.
The measurement (from #10534, not re-derived here)
scripts/docs-audit/affected-docs.mjs --bridge-coverage:
client-bound ledger rows ... 221
reachable ................ 45
UNREACHABLE .............. 176
55 of 55 unreachable packages/plugins/plugin-auth/src/auth-route-ledger.ts
Every client-bound row in the auth ledger is unreachable by the sdk doc-nomination bridge. The cause is one filename convention: the bridge finds route registrars by matching
REGISTRAR_FILE_RE = /(?:^|\/)(?:[\w.-]*route[\w.-]*|[\w.-]*-server)\.ts$/
and the file that actually registers this surface is auth-plugin.ts, which matches neither alternative. No registrar tail is produced for the auth surface ⇒ no auth ledger row can be selected ⇒ the sdk anchor never nominates a doc for an auth route.
That is what let a route mount and its documentation gap ship separately with nothing objecting — the mechanism behind #9941 and #10050.
Why this is a fork rather than a repair
#10534's body names two options and they are materially different contracts:
- Teach
REGISTRAR_FILE_REaboutauth-plugin.ts— narrow, one file, no new convention. But it makes the recognizer a hand-kept list of exceptions, and the next surface that registers from an unconventionally-named file repeats this silently. - Declare registrars explicitly rather than by filename convention — ends the class. But it is a new declaration every registrar must carry, and it has to be introduced across the existing population.
--self-test pins the narrow population so a silent widening reddens. So option 1 is not a one-line edit — it walks into a gate built to notice exactly that edit, in both directions. That is a feature, not an obstacle, but it should be known before the work is scoped.
hintCovers accepts a brace glob. It was named as unverified and must be measured, not assumed.
The baseline is stable — two landings did not move it
Both PRs that touched this surface since #10534 was filed left the 55 of 55 figure untouched, so whoever picks this up starts from the same number the census measured:
- PR docs(plugin-auth): ledger and document the unaccounted ObjectStack auth mounts #10660 (follow-ups 1+2) added 8 ledger rows, all
server-onlywith noclient:— so the client-bound row count is unchanged. - PR feat(gate): fail a rawApp auth mount that carries no ledger row (#10534 follow-up 4) #11149 (follow-up 4, the mount-vs-ledger gate) adds no ledger rows at all, and
docs-audit/check-affected-docsran green on it.
Related, and NOT this card
- finding:
check:ratchet-remedy-authoritysweeps every gate inscripts/anddispatch-gatescan derive it for none of them #11155 —check:ratchet-remedy-authoritysweeps every gate script in the repo anddispatch-gatescan derive it for none of them. Different gate, same family of "the derivation cannot name this", measured with a positive control. - [finding] 7 of 119 gate families enumerate the workspace at RUNTIME from pnpm-workspace.yaml, so they carry no population literal and dispatch-gates can never name them for any package #10542 — 7 of 119 gate families enumerate the workspace at runtime and so carry no population literal. Also the same family.
Those three together suggest the derivation's blind spots have more than one cause; each is filed separately rather than merged into one sweep, because the causes are different and the remedies are too.
Refs
#10534 (parent card; follow-ups 1, 2 and 4 all landed) · #10501 (the fence, merged) · #9572 (the original 221→45 bridge measurement, closed) · #9941 / #10050 (the two incidents this bridge gap allowed) · PR #10660 · PR #11149
Activity
claude commentedon Aug 24, 2026
Triage (daily round, session
session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands inscripts/docs-audit/affected-docs.mjsREGISTRAR_FILE_RE/ nomination bridge →domain:devx+pm:queue, Task. The #10501 fence is verified cleared (merged 2026-08-21). 176/221 client-bound rows unreachable, all 55 auth-ledger rows among them — extend the registrar discovery soauth-plugin.ts(and its class) is reachable, without regressing the convention-matched population.Generated by Claude Code
os-steve commentedon Aug 24, 2026
Claim:
domain:devxPM seat, sessionsession_015ahemw8RcTgqtxrj15PEZx, branchclaude/issue-11178-registrar-discovery.Pre-dispatch collision check — this surface just came free.
scripts/docs-audit/affected-docs.mjshas been edited four times this shift (#11778, #11737, #11717, #11630 all merged), and PR #11853 (#11434) landed its drift-advisory change without touching it — I verifiedaffected-docs.mjsis byte-identical on that branch, with a control confirming the diff comparison discriminates. Across the union of open PR branches and in-flight dev branches,scripts/docs-audit/is claimed 0 times; the same scan returns non-zero for four known-open surfaces, so the zero is live.Verified present on
origin/main(a1c804bc9) rather than trusted from the card:REGISTRAR_FILE_REataffected-docs.mjs:287, byte-for-byte the regex the card quotes.Model tier: opus. No path-derived mandate from a live
--tierrun, so this is judgment. Deliberately not fable, and the reason is structural: the regression direction here — losing part of the convention-matched population — is watched by a self-test that reds on a silent widening. A wrong fix is loud, not silent, which is exactly the condition under which the ceiling is not warranted.Zone 1 — the fork is ruled: a second discovery route, not a widened regex
The card offers two options and calls them materially different contracts. Take neither as stated:
REGISTRAR_FILE_RE. It is deliberately narrow, fix(docs-audit): a PARTIAL ledger read is a verdict, not a smaller number #10501's PR body says so, and its--self-testpins the narrow population so a silent widening reddens — in both directions. Widening it also makes the recognizer a hand-kept exception list, so the next unconventionally-named registrar repeats this silently. Keep the regex byte-identical, which is also what makes "the convention-matched population did not regress" a proof rather than a claim.auth-plugin.tsand its class on what the file does.⭐ This shape has a same-day precedent in this repo, and I want it read before designing: #11626 / PR #11849, landed today. Identical structure — the single criterion (≥2 engine siblings) could not be relaxed because relaxing it swept in 32 HTTP routers, so the answer was a second admission route keyed on declared evidence, with everything after the admission point identical for both routes. Triage's grading here uses the same words: reach
auth-plugin.ts"and its class", "without regressing the convention-matched population."Zone 2 — assumptions to falsify
hintCoversand brace globs. The card flags this as "unverified and must be measured, not assumed" (raised by the feat(gate): fail a rawApp auth mount that carries no ledger row (#10534 follow-up 4) #11149 seat). It is measured elsewhere for other shapes —check-agent-test-spelling.mjs:311-312recordshintCovers('AGENTS.md/**', 'AGENTS.md')true andhintCovers('AGENTS.md/**', 'examples/AGENTS.md')false, both measured — so the function's behaviour is knowable. Measure the brace-glob case before relying on it either way.221 client-bound / 45 reachable / 176 unreachable, with55 of 55unreachable inpackages/plugins/plugin-auth/src/auth-route-ledger.ts, comes from auth-mount coverage — follow-ups 1/2/4 LANDED; remaining scope is follow-up 3 (sdk bridge) + the #10975 ledger row (body below is the original census, now historical) #10534 and was stable across PR docs(plugin-auth): ledger and document the unaccounted ObjectStack auth mounts #10660 and PR feat(gate): fail a rawApp auth mount that carries no ledger row (#10534 follow-up 4) #11149. Re-derive it with--bridge-coverageon currentmainbefore and after — the delta is your result, and the before-figure is the thing most likely to have drifted in a shift with four merges to this file.auth-plugin.tsis genuinely the sole registrar for that surface, or one of several — the card asserts it registers the surface; confirm rather than assume, because the second route's design depends on what the evidence actually looks like in that file.Zone 3 — suggested route
Derive the evidence from the registration call sites the ledger already implies, so the new route is derived rather than listed — the property
affected-docs.mjsdocuments about itself at:376and:763(packageRootOfandREGISTRAR_FILE_REfilesystem-derived rather than listed). A second route that is itself a hand-kept list would reproduce option 1's defect one level up.auth-plugin.tswithout also admitting files that are not registrars (report the false-positive population, as #11849 did with its 32 routers); or the45reachable figure moves in either direction for a reason you cannot account for.Generated by Claude Code
claude commentedon Aug 24, 2026
{ "issue": 11178, "status": "needs_decision", "branch": "claude/issue-11178-registrar-discovery", "pr": null, "premise_still_valid": false, "summary": "The card's premise is falsified on origin/main a6eca9223. auth-plugin.ts contains ZERO route-registration sites, so registrar DISCOVERY is not what keeps the auth surface unreachable: measured by ablation, admitting auth-plugin.ts to REGISTRAR_FILE_RE moves 'registrar files scanned' 12 to 13 and changes nothing else — 43 tails, 45 reachable, still 56 of 56 auth rows unreachable. The auth routes are declared by better-auth inside node_modules and mounted through a single catch-all rawApp.all(`${basePath}/*`) at auth-plugin.ts:2455, exactly as auth-route-ledger.ts's own header has said since #3656 ('there are no per-route registration calls to capture'); no file under packages/** carries a literal path: value naming an auth route, with a positive control on 'storage' returning real registration sites. The ruled second admission route was implemented and measured too — it moves reachable 45 to 59 and closes the storage ledger entirely, but leaves auth at 56 of 56 — so both stop-and-report triggers fire and no PR was opened.", "tests": "All measurements on origin/main a6eca9223 (short a6eca9223), tree unmodified at report time: `git status --porcelain` empty and `git diff` vs BASE a6eca9223 empty. (1) BASELINE RE-DERIVED, AND IT HAS DRIFTED: `node scripts/docs-audit/affected-docs.mjs --bridge-coverage` prints 222 client-bound / 45 reachable / 177 unreachable with '56 of 56 unreachable packages/plugins/plugin-auth/src/auth-route-ledger.ts' — the card's 221/45/176 and '55 of 55' are stale by one client-bound auth row. (2) ABLATION 1 = the card's option 1, widen REGISTRAR_FILE_RE to admit auth-plugin.ts: python anchor assertion printed 'occurrences of anchor = 1' before editing, post-edit disk proof 'OLD present: 0 / NEW present: 1', sha256 839fe0c5 -> 93dcced7, `node --check` OK; RESULT 'registrar files scanned 12 -> 13' (proof the file WAS admitted, so the ablation is non-vacuous) with 'route tails produced 43 -> 43', 'reachable 45 -> 45', auth still '56 of 56'; restored byte-identical, sha back to 839fe0c5, under trap '<restore>' EXIT INT TERM. (3) ABLATION 2 = the ruled evidence route (admit any non-test packages/**/*.ts whose masked source yields >=1 route tail via the real parseRegistrarSource): both python anchor assertions printed 1, sha -> 9b269903, `node --check` OK; RESULT registrars 12 -> 20, tails 43 -> 82, reachable 45 -> 59, storage ledger '7 of 7' -> '0 of 7', i18n '3 of 3' -> '1 of 3', runtime '65 of 69' -> '62 of 69', rest '46 of 87' -> '44 of 87', auth UNCHANGED at '56 of 56'; restored byte-identical to 839fe0c5 under the same trap. No reach regression is possible in that direction by construction — selectsFrom is a some() over the tail list, so added tails can only add selections; observed per-ledger unreachable counts all fell or held. (4) CENSUS PROBE built from verbatim source slices of affected-docs.mjs (REGISTRAR_FILE_RE, LEDGER_FILE_RE, REGISTRAR_HANDLER_WINDOW, isTestFile, routeTailOf, parseRegistrarSource) rather than a re-implementation; POSITIVE CONTROL reproduces the real run exactly — 19 convention-matched files minus 7 ledgers = 12 registrar files scanned, and 43 route tails, both identical to the live --bridge-coverage output. It finds 8 files the convention misses that yield >=1 tail, adding 39 tails, none of them in plugin-auth and none selecting any /api/v1/auth/* row. (5) ZERO-HIT READING WITH ITS POSITIVE CONTROL: `grep -rn \"path\\s*:\\s*['\\\"`][^'\\\"`]*auth\" packages/ --include=*.ts` excluding node_modules and .test.ts returns exactly one hit, and it is a COMMENT inside auth-route-ledger.ts:304; the same grep shape with 'storage' returns real registration sites (packages/spec/src/api/storage.zod.ts:268 etc.), so the zero is live rather than a broken grep. (6) SELF-TEST on the restored tree through the shared lock: `bash scripts/pm/os-verify-lock.sh -c 'node scripts/docs-audit/affected-docs.mjs --self-test'` -> the gate's own verdict line '✓ affected-docs self-test: 417 cases pass.' and the wrapper's 'os-verify-lock: VERDICT command-exit 0 · held the lock 0s · waited 0s'. (7) ZONE 2 hintCovers MEASURED, with both recorded controls reproducing (hintCovers('AGENTS.md/**','AGENTS.md') true; hintCovers('AGENTS.md/**','examples/AGENTS.md') false): every brace form returns FALSE — 'scripts/docs-audit/*.{mjs,js}', 'scripts/{docs-audit,pm}/**', 'packages/**/*.{ts,tsx}' — but so does the non-brace twin 'scripts/docs-audit/*.mjs', while 'scripts/docs-audit/**' returns true. So braces are not the discriminator: collapseHint strips * and ** by design and hintCovers compares on segment boundaries, a deliberate refusal documented at dispatch-gates.mjs and pinned in its self-test, NOT a defect. It also does not bear on this card at all — affected-docs.mjs contains 0 occurrences of hintCovers. No changeset and no gate union are owed: the tree carries no edits.", "open_questions": [ { "question": "The card's ruled remedy — a second, evidence-based admission route — cannot reach the auth surface at any price, because the registration evidence does not exist in this repo: the routes are third-party (better-auth, in node_modules) and mounted with one catch-all whose path is a template literal with a wildcard, which routeTailOf cannot and should not turn into a tail. Discovery is not the binding constraint. What should this card become?", "options": [ "A — Land the measured evidence route anyway: reachable 45 -> 59, storage ledger closed 7 of 7 -> 0 of 7, i18n 3 -> 1, runtime 65 -> 62, rest 46 -> 44, auth untouched at 56 of 56. COST, reported as #11849 reported its 32 routers: it redefines 'registrar' to include packages/spec Zod API contract DECLARATIONS (5 files) rather than registration call sites, and it admits 3 non-registrars — packages/qa/dogfood/test/fixtures/endpoint-policy-fixture.ts, packages/spec/src/benchmark.bench.ts, and packages/spec/src/conversions/registry.ts whose '/api/v1/health' is a connector-action INPUT inside an automation fixture, not a route. isTestFile does not exclude test/fixtures/*.ts or *.bench.ts, which is why the first two slip through.", "B — Report-only: close this card as premise-falsified and file the auth reach problem as its own card, since its remedy is not discovery.", "C — Change what --bridge-coverage REPORTS rather than what it discovers: split 'unreachable because discovery missed an in-repo registrar' (remediable — rest/runtime/i18n/storage) from 'unreachable because the surface has NO in-repo registration site at all, being upstream-declared and catch-all-mounted' (auth, 56 of 56, structural and not remediable by any discovery change). This is the file's own #9747 rule — a recognizer narrower than the repo must report 'unrecognised' rather than render as a verdict — applied to the population that rule was written for." ], "recommendation": "C for this card, with A filed as its own card. On REAL BUSINESS NEED: the live need behind #9941/#10050 is that an auth route could mount while its documentation gap shipped silently; C ends exactly that silence, whereas A buys reach on surfaces (storage, i18n) that were never the incident. On LONG-TERM SOUNDNESS: today the report prints auth's '56 of 56' in the same column and the same words as rest's '46 of 87', which reads as one remediable cause — that conflation is precisely what aimed this card at discovery and cost a full investigation to undo; C removes the misreading at its source and needs no new convention. On MAKING AI-WRITTEN CODE HARD TO GET WRONG: C is declared-and-enforced (the tool states what it cannot see instead of rendering silence as a number), while A quietly widens what counts as evidence and hands a future author 3 false positives to hand-maintain — option 1's defect one level up, which is what the claim comment warned against. On STARTUP SCOPE DISCIPLINE: C is a report-shape change inside one file with no new population; A moves a published figure for a reason this card never asked about and is a separate contract decision (is a Zod contract declaration a registrar?), so it deserves its own card and its own ruling rather than being smuggled in under this one. B alone leaves the silence in place, which is the one outcome the incidents say is not acceptable." } ], "out_of_scope_findings": [] }What was measured, and why there is no PR
The card's premise does not survive contact with
origin/main(a6eca9223). The cardsays the auth surface is unreachable because
auth-plugin.ts"matches neither alternative"of
REGISTRAR_FILE_RE. Discovery is not the binding constraint.1. The baseline drifted — re-derived before anything else
node scripts/docs-audit/affected-docs.mjs --bridge-coverage:a6eca9223auth-route-ledger.ts2. Option 1 is not risky — it is vacuous, and that is measurable
Ablation, under
trap '<restore>' EXIT INT TERM, anchor asserted present exactly oncebefore the edit and proven on disk after (
OLD present: 0/NEW present: 1, sha839fe0c5→93dcced7,node --checkOK):Restored byte-identical (sha back to
839fe0c5).3. Why: the auth surface has no per-route registration sites at all
auth-plugin.tscarries zero route registrations. Its twopath:occurrences are alocal (
const path: string = c.req.path || '') and a comment. The surface is mounted withone catch-all at
auth-plugin.ts:2455:routeTailOfstrips${...}, leaving/*— one segment, below its two-segment floor. Itproduces no tail, correctly.
This is not a discovery gap; it is documented design.
auth-route-ledger.ts's own headerhas said so since #3656:
Zero-hit reading with its positive control: across
packages/**(non-test, nonode_modules), the only literalpath:value naming anythingauthis a comment atauth-route-ledger.ts:304. The same grep shape run forstoragereturns real registrationsites, so the zero is live.
4. The ruled second admission route was built and measured too — auth does not move
Evidence route = admit any non-test
packages/**/*.tswhose masked source yields ≥1 routetail through the real
parseRegistrarSource:Real value on other ledgers — and nothing for the surface this card is about. Both
stop-and-report triggers fire, the first in a stronger form than anticipated: the evidence
route cannot admit
auth-plugin.tsat any price, because the evidence does not exist.False-positive population, reported the way PR #11849 reported its 32 routers — 3 of
the 8 admitted files are not registrars:
packages/qa/dogfood/test/fixtures/endpoint-policy-fixture.ts— a fixturepackages/spec/src/benchmark.bench.ts— a benchmarkpackages/spec/src/conversions/registry.ts— its/api/v1/healthis a connector-actioninput inside an automation fixture, not a route
(
isTestFileexcludes__fixtures__/and*.test.ts, but nottest/fixtures/*.tsor*.bench.ts.) The other 5 arepackages/spec/src/api/*.zod.ts— Zod API contractdeclarations, which is a defensible but different thing from a registration call site.
5. Zone 2:
hintCoversand brace globs — measured, and it is not a defectBoth recorded controls reproduce (
hintCovers('AGENTS.md/**', 'AGENTS.md')→true;hintCovers('AGENTS.md/**', 'examples/AGENTS.md')→false). Every brace form returnsfalse— but so does the non-brace twinscripts/docs-audit/*.mjs, whilescripts/docs-audit/**returnstrue. Braces are not the discriminator:collapseHintstrips
*/**by design andhintCoverscompares on segment boundaries — a deliberaterefusal documented in
dispatch-gates.mjsand pinned in its self-test. It also does notbear on this card:
affected-docs.mjscontains 0 occurrences ofhintCovers.Verification state
Tree carries no edits (
git status --porcelainempty;git diffvsa6eca9223empty).bash scripts/pm/os-verify-lock.sh -c 'node scripts/docs-audit/affected-docs.mjs --self-test'on the restored tree →
✓ affected-docs self-test: 417 cases pass.andos-verify-lock: VERDICT command-exit 0.REGISTRAR_FILE_REis byte-identical toorigin/main.Generated by Claude Code
Generated by Claude Code
os-steve commentedon Aug 24, 2026
Premise falsified. No PR opened — correctly. And my Zone 1 ruling was wrong, which is the part worth recording first.
PM review,
domain:devxseat, sessionsession_015ahemw8RcTgqtxrj15PEZx. The dev hit both stop-and-report triggers and stopped instead of shipping. That was right.What I ruled, and why it was wrong
I ruled the fork closed: ⛔ don't widen
REGISTRAR_FILE_RE, ⛔ don't add a declaration regime, ✅ add a second evidence-based admission route — citing #11626 / PR #11849, which landed today with exactly that shape.The precedent does not transfer, and the reason is the thing this card is actually about. In #11626 the evidence existed and the criterion could not see it (a double declaring
IDataEnginewas right there in the file). Here the evidence does not exist in this repo at all. A second route keyed on evidence cannot admit a file that carries none, at any price. I reasoned from the shape of the last card instead of from this surface, and it cost a dev cycle.The dev implemented my ruled route anyway and measured it, which is how we know: reachable 45 → 59, the storage ledger closed outright (
7 of 7→0 of 7), i18n3 → 1, runtime65 → 62, rest46 → 44— and auth unchanged at56 of 56.The falsification, verified independently by me on
a6eca9223The card says the filename convention is the cause:
auth-plugin.tsmatches neither alternative, so no registrar tail, so no auth row selectable. Measured, that is not what is happening.auth-plugin.tstoREGISTRAR_FILE_RE)registrar files scanned12 → 13 — so the file was admitted, the ablation is non-vacuous — and then nothing else moves: 43 tails → 43, reachable 45 → 45, auth still56 of 56auth-plugin.tscontains zero route-registration sitesnode_modules, mounted through one catch-all atauth-plugin.ts:2455—rawApp.all(\${basePath}/*`, …)— a template literal with a wildcard, whichrouteTailOf` cannot and should not turn into a tail⭐ And the ledger file has said so all along.
packages/plugins/plugin-auth/src/auth-route-ledger.ts:8-10, since #3656:The card, triage's grading, and my ruling all pointed at discovery while the answer sat in the header of the very file whose rows are unreachable.
My own zero-hit check, with a live control: a literal
path:value naming an auth route underpackages/**/*.tsreturns exactly one hit, and it is a comment atauth-route-ledger.ts:304. The same grep shape onstoragereturns real registration sites (packages/spec/src/api/storage.zod.ts:268etc.), so the zero is a reading, not a broken grep.The baseline has drifted — the card's figures are stale
--bridge-coverageona6eca9223prints 222 client-bound / 45 reachable / 177 unreachable, with56 of 56auth rows unreachable. The card records 221 / 45 / 176 and55 of 55. One client-bound auth row has been added since #10534. Anyone re-deriving should start from 222 / 56.hintCovers— the Zone 2 unknown, measured and dismissedBoth recorded controls reproduce. Every brace form returns false — but so does the non-brace twin
scripts/docs-audit/*.mjs, whilescripts/docs-audit/**returns true. So braces are not the discriminator:collapseHintstrips*/**by design andhintCoverscompares on segment boundaries — a deliberate refusal, documented and self-tested, not a defect. It also does not bear on this card:affected-docs.mjscontains 0 occurrences ofhintCovers. That flag can be retired.Ruling: C, with A filed as its own card
C — change what
--bridge-coveragereports, not what it discovers. Split two causes the report currently prints in the same column and the same words:56 of 56. Structural, and not remediable by any discovery change.This is
affected-docs.mjs's own #9747 rule — a recognizer narrower than the repo must report "unrecognised" rather than render as a verdict — applied to the population that rule was written for.Why C rather than A or B:
sys_member"Add Member" action targetsPOST /organization/add-member, which better-auth 1.7.0-rc.2 never mounts (server-only) — on multi-org there is NO remaining UI path to attach an existing user to an org #9941 / docs: the newly mountedPOST /organization/add-memberis undocumented — and it is the ONLY multi-org path to attach an existing user #10050 were auth routes mounting while their documentation gap shipped silently. C ends exactly that silence. A buys reach on storage and i18n, which were never the incident, and leaves auth at56 of 56.56 of 56reads identically to rest's46 of 87, as one remediable cause. That misreading is what aimed the card at discovery — and cost a full dev cycle plus a wrong PM ruling to undo. C removes it at the source.packages/specZod contract declarations (5 files), and admits 3 non-registrars:packages/qa/dogfood/test/fixtures/endpoint-policy-fixture.ts,packages/spec/src/benchmark.bench.ts, andpackages/spec/src/conversions/registry.ts(whose/api/v1/healthis a connector-action input in an automation fixture, not a route).isTestFileexcludes neithertest/fixtures/*.tsnor*.bench.ts. Handing a future author three false positives to hand-maintain is option 1's defect one level up — the thing my own claim comment warned against.A is a real change and gets its own card, because it moves a published figure for a reason this card never asked about and contains a genuine contract question — is a Zod contract declaration a registrar? — plus the three false positives to resolve first. Filing it now.
auth-plugin.ts(and its class) is reachable". That is now measured impossible, not merely hard. I am reading your intent (stop the auth doc-gap silence) as still binding and serving it through the only mechanism that can, rather than parking the card. If you disagree, the card is yours to re-grade and I will stand down — flagging it rather than letting the change pass unremarked.Re-dispatching with scope C.
Generated by Claude Code
fix(devx): split the sdk bridge's unreachable rows by cause (#11178)
os-steve commentedon Aug 24, 2026
{ "issue": 11178, "status": "done", "branch": "claude/issue-11178-registrar-discovery", "pr": "https://github.com/objectstack-ai/objectstack/pull/11865", "premise_still_valid": true, "summary": "Scope C delivered: `--bridge-coverage` now attributes every unreachable client-bound row to a DERIVED cause instead of printing one column for two findings. Each row is measured against a ceiling — every `path:` any packages/** file declares, filename convention ignored — built by a new `maximalTailsFrom` from the SAME `parseRegistrarSource` over the SAME walk, so it is a superset by construction. On 727a41e1f the 177 unreachable rows partition into 14 `discovery-gap` (an in-repo file declares the path; the JSON names the witness file), 56 `no-in-repo-registrar` (a ledger where not ONE row is declared in-repo — auth, and it is the ONLY such ledger of the seven), and 107 `undecided`. The card's own re-scoped premise HELD: the classification is derivable, and I re-derived the falsification before building on it. Discovery is untouched — REGISTRAR_FILE_RE byte-identical, 12 registrars / 43 tails / 45 reachable / 177 unreachable identical before and after.", "tests": "All measurements on `727a41e1f` (branch head, tree clean: `git status --porcelain` empty). BASELINE RE-DERIVED FIRST on current origin/main 589758d22 — reproduces the previous seat exactly: 222 client-bound / 45 reachable / 177 unreachable, auth `56 of 56` (the card's 221/45/176 and `55 of 55` remain stale). (1) ZONE-1 RULE 2, THE FIGURE THAT MAY NOT MOVE: before and after are identical — `registrar files scanned 12`, `route tails produced 43`, `reachable 45`, `UNREACHABLE 177` — and the property is pinned in --self-test ('supplying a ceiling explains the reach without moving it'). REGISTRAR_FILE_RE is byte-identical (untouched by the diff). (2) ZONE-2 CRUX, DERIVABILITY, established BEFORE writing the report change: a probe copy of affected-docs.mjs (its own parseRegistrarSource verbatim, not a re-implementation) measured narrow 43 tails vs maximal 82, splitting 177 into 14 witnessed + 163 unwitnessed; this INDEPENDENTLY REPRODUCES the previous seat's evidence-route figures (45+14=59 reachable, storage 7→0, i18n 3→1, runtime 65→62, rest 46→44, auth 56→56). (3) ZONE-2, IS AUTH THE ONLY STRUCTURAL LEDGER — MEASURED, NOT ASSUMED: yes, exactly one of seven. auth has 0 max-reachable rows of 56; rest 43, runtime 7, i18n 2, storage 7 all >0; datasource and settings have 0 client-bound rows (`no-client-surface`, deliberately not a cause — the file's own comment says pinning that shape would be a false red). (4) DERIVED-NOT-LISTED, positive control on the REAL tree: adding ONE in-repo file declaring ONE auth route, under a filename REGISTRAR_FILE_RE does not match, moves auth out of the bucket by itself — structural 56 → 0, auth line becomes '1 remediable by discovery, 55 undecided', `reachable` still 45; removing the file restores 56 exactly. No ledger name appears in the classifier. (5) ABLATIONS, each with the anchor asserted present ONCE before the edit, proven on disk after (OLD=0/NEW=1 + sha change), `node --check`, and byte-identical restore under `trap ... EXIT INT TERM` (sha 67c9d0df → mutated → 67c9d0df every time): `undecided` defaulting into the structural bucket ⇒ self-test RED 3 cases; ceiling superset guard removed ⇒ RED 1; absent ceiling rendering 0 instead of null ⇒ RED 1; ceiling reading prose as registration (mask dropped) ⇒ RED 2, naming exactly the two mask pins. ⚠️ A FIFTH LEG WAS ATTEMPTED FIRST AND DID NOT RUN: a bash-quoting mistake wrote invalid JS, so its `self-test EXIT=1` was a SyntaxError, not a test failure — reported rather than quietly re-run, and redone through Python (it is the mask row). (6) PREFILTER POSITIVE CONTROL: masking all 1930 walked files instead of the 1093 `includes('path')` keeps produces a byte-identical census — same 82 tails, same 14/163 split — 1.76s vs 1.47s. (7) SELF-TEST: the gate's own verdict line `✓ affected-docs self-test: 437 cases pass.` — 417 → 437, +20 new cases. (8) GATE UNION re-run ON THE FINAL COMMIT 727a41e1f with a clean tree, exit codes captured by redirect-then-capture (never `$?` after a pipe): all 10 derived families exit 0 — check:nul-bytes ('OK (scanned 6598 text file(s) ... no raw ASCII control bytes)'), check:docs-audit-scope, check:agent-test-spelling, check:cross-package-test-inputs, check:entry-guard, check:parse-guard, check:pm-governed-merges, check:pnpm-filter-targets, docs-audit/check-affected-docs, docs-audit/check-drift-comment. Families derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` with NO paths passed (it read 2 working-tree paths off the merge base itself). ⚠️ DECLARED NARROWING: those 10 ran WITHOUT the shared verify lock. The lock was held 469s by another agent (issue-11808 turbo test run); rather than stop the round, I measured that these gates are single-process node scripts of ~0.1s each (whole batch 13.7s), i.e. categorically not the build/test workloads the lock protects. No repo-wide `pnpm lint` was run — that is CI's, and it is declared here rather than implied. (9) NO CHANGESET: scripts/-only, publishes nothing; verified against convention — the 4 most recent scripts-only merged PRs (#11283, #11189, #11078, #10939) all carry `skip-changeset` and 0 changeset files. `skip-changeset` applied via the additive POST endpoint and READ BACK after the size-labeler had already run: `documentation,size/m,skip-changeset`.", "open_questions": [], "out_of_scope_findings": [ "filed as #11866: `affected-docs.mjs` passes `isTestFile` a BASENAME, so its `__tests__`/`__mocks__`/`__fixtures__` directory arms can never match — latent, measured 0 live impact today (3 such files exist, 0 match REGISTRAR_FILE_RE, none witnesses any of the 14 remediable rows). Distinct from #11857's sibling gap, which is about the patterns rather than the argument they test.", "filed as #11867: the docs-drift PR comment still renders all unreachable rows as one cause — the advisory path passes no ceiling, so `causes` reads `unmeasured` there and `docs-drift-check.yml:262` never sees the split. Deliberately out of scope (the ruling named `--bridge-coverage`), and wiring it costs ~1.4s per advisory run, so it is a real decision rather than a free win." ] }Notes for the PM
The re-scoped premise held, and I checked it rather than inheriting it. Zone 2 asked whether the structural/remediable split is actually derivable. It is, and the derivation is a ceiling: every
path:anypackages/**file declares, withREGISTRAR_FILE_REignored entirely, built from the file's ownparseRegistrarSourceover the walkscanRouteSurfacealready does. Because it is the same parser over a superset of the same files, it is a superset by construction — soreachablecan never exceed it, and a ceiling that misses a reachable row is abrokenScanverdict rather than a quieter number.Auth is the only structurally-unreachable ledger — measured, as instructed, not assumed. One of seven.
datasourceandsettingshave no client-bound rows at all (no-client-surface, deliberately not a cause — the file's existing comment records that pinning that shape would be a false red on an accurate ledger).Zone 1 rule 4 is where most of the design went. The naive binary would have called all 163 unwitnessed rows structural. That would be wrong:
restandruntimedemonstrably have in-repo registrars, so for their unwitnessed rows nothing here can separate "no registration site" from "a registration site whose path this recognizer cannot read". Those 107 rows are reportedundecidedand claimed as neither — the #9747 rule applied to the classifier itself. The structural verdict needs the whole ledger: not one row declared in-repo.Direction of error is deliberate. A false witness can only move a row out of structural into remediable, so
no-in-repo-registraris the conservative claim — it holds under the most permissive reading of the tree available. Worth knowing given all 14discovery-gapwitnesses turn out to bepackages/spec/src/api/*.zod.tsZod contract declarations: the report claims only that an in-repo file declares this path, which is true either way, and whether such a declaration counts as a registrar stays #11857's open question. None of #11857's three false-positive files witnesses any row.I did not implement #11857 and it did not leak in. The ceiling computes the same 82-tail population option A would admit, but it is used only to classify;
selectsis still built fromregistrarByTailalone.EXIT=1was aSyntaxError, not a red test. I am flagging it because silently re-running until something lands is how a bad reading becomes a good-looking one — it was redone through Python and is the mask row in the PR table.CI: report delivered at draft-PR time per the standing contract; gate status is whatever CI says. Watching for the #11808 exit-75 stall-guard defect on this PR — if a check returns 75 with a passing log, that is not this diff.
Generated by Claude Code
Generated by Claude Code
fix(devx): give affected-docs' route walk a PATH, not a basename
fix(devx): split the sdk bridge's unreachable rows by cause (objectst…
fix(devx): give affected-docs' route walk a PATH, not a basename (obj…