Uh oh!
There was an error while loading. Please reload this page.
test(client): resolve every AUTH_ROUTE_LEDGER client name against a real client - #11403
Merged
Merged
Conversation
…eal client Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
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. |
This was referenced Aug 23, 2026
Closed
os-sam
marked this pull request as ready for review
August 23, 2026 16:04
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 23, 2026
This was referenced Aug 23, 2026
Merged
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 freeto 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#11359
AUTH_ROUTE_LEDGERis the largest of the six route ledgers (56 rows, 54sdk, 55 carrying aclientname) and it was the only one whoseclientnames were never resolved against a real client. The server-side test that reads as if it did —auth-route-ledger.conformance.test.ts, "everysdkentry names its client method" — filters one.disposition === 'sdk' && !e.client, and!e.clientis falsy-on-absent, so it checks presence, not existence. A row spelledclient: 'auth.setInitialPasword'passed it unchanged.This adds the missing client half as
packages/client/src/auth-route-ledger-coverage.test.ts, alongside the three that were already there.Not a live wire defect. All 55 client-naming rows resolve today, so this file was green on its first run. That is the expected result and it is also the reason the ablation below is the substance of this PR rather than a formality: a guard that passes because the invariant holds and a guard that passes because it asserts nothing are indistinguishable from outside, and that indistinguishability is the defect being closed.
Where it lands, and why the card beat the triage note
The card said
packages/client; triage saidpackages/plugins/plugin-auth. Measured rather than picked:typeof resolve(...) === 'function'ledger guardroute-ledger-coverage.test.ts,rest-route-ledger-coverage.test.ts,service-route-ledger-coverage.test.ts; all inpackages/client/src, 0 elsewhere@objectstack/plugin-authdepend on@objectstack/client?dependencies,devDependenciesorpeerDependenciespackages/clientalready readauth-route-ledger.ts?client-url-conformance.test.ts:52androute-ledger-response-schema.test.ts:48, both by relative source specifierSo the split is: each ledger's server half lives next to the registrar that mounts the routes, and every client half lives in
packages/client, because resolving a name needs anObjectStackClient. Landing it inplugin-authwould have created aplugin-auth → clientpackage edge — the "backwards" direction all three sibling headers name, and unbuildable in CI's per-package closure. The auth ledger's own header already anticipates this file: "This module is package-internal … It must stay import-free — the client-side guard imports it as a relative SOURCE file."A sibling file rather than a fourth leg inside
service-route-ledger-coverage.test.ts(the card allowed either): that file's title and docblock are a statement about the #3636service tranche — "all THREE server surfaces" — and auth is a plugin, not a service. Filing auth under it would have made the file's own title false, which is the class of defect this card exists to remove.Zone-2 assumptions, as measured
packages/plugins/plugin-auth/src/auth-route-ledger.tswas already declared inCROSS_PACKAGE_TEST_INPUTS['@objectstack/client']and already listed inturbo.json's@objectstack/client#testinputs, because two existing tests read it the same way.clientname (54sdk+ onedisabledrow that deliberately keeps its name, auth-method-matrix:change-emailanddelete-userare booked as mounted inauth-route-ledger.tsbut plugin-auth never configures better-auth'suser.changeEmail/user.deleteUser— no switch to enable them #7735auth.deleteUser), and all 55 resolve to a function. No live defect found, so the clause-② grading ofnostill holds and needed no re-grade.The ablation
Predicted in writing before running: typo one row's
client(auth.changePassword→auth.changePasword); the new leg fails naming that row, while the existing server-side check stays green.Mutation subject is
packages/plugins/plugin-auth/src/auth-route-ledger.ts, which the suite imports as a relative source path — noexportsresolution, no vitest alias, sodistis not on the path for it and no rebuild is required for a mutation to reach the test. (check-test-source-alias's registry agrees:@objectstack/client's unaliased dist imports are six package specifiers, and@objectstack/plugin-authis not among them.) The dependency closure was built first regardless —pnpm --filter '@objectstack/client^...' build,BUILD_EXIT=0— so nothing measured here ran against a staledist. Mutation confirmed on disk by anchored grep in both directions plus an untouched-sibling control; restore verified byte-identical withgit hash-object; the whole script ran undertrap restore EXIT INT TERM.LEG 2 is the finding the card was filed on, measured rather than argued: the same typo that the new guard catches is waved through by the test whose name claims to check it.
The second leg in the new file is that ablation made permanent — a population floor plus a positive and a negative control derived from the ledger itself, so the guard cannot decay into one that passes while measuring nothing.
Serial, not folded — and a claim in #10974 that does not hold
#10974 is in flight on the same auth surface. Its PR #11360 changes exactly two files,
packages/client/src/index.tsand a changeset — disjoint from everything here, so this proceeded independently.Reported rather than fixed here, because it is another card's reasoning: #10974's ordering argument says a
sdkrow naming a nonexistent method is "the exact failurepackages/client/src/route-ledger-coverage.test.tswas written for". That test readsROUTE_LEDGER, the dispatcher's — notAUTH_ROUTE_LEDGER— so it would not have caught an auth row, and until this PR nothing would have. The conclusion of that argument survives and is in fact strengthened (with no guard at all, a premature auth row would have sat there silently), but the authority cited for it was wrong. Left for the PM to route.Deliberately not added: a reverse-direction leg ("every
client.auth.*method is backed by a ledger row"), which the service-ledger file carries forstorageandi18n. Adding it here would go red the moment #11360 merges and stay red until #10975 lands — inverting the maintainer-ruled ordering of option C. That is a separate decision, not a rider on a guard-hole card.Verification
Gate union derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, no hand-supplied paths, re-derived after the final commit. All at2de8c3ef71.Green, each read from the gate's own verdict line with the exit code captured before any pipe:
check:cross-package-test-inputs·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:published-files·check:slot-lookup·check:test-source-alias·check:type-source-resolution·check-ci-filter-parity·check-plugin-teardown-shape·docs-audit/check-affected-docs· and the convention-triggeredcheck:query-options-erasure·check:engine-double-contract·check:where-matcher·check:type-check-coverage.Suites:
pnpm --filter @objectstack/client test→ 24 files / 316 tests passed.pnpm --filter @objectstack/client typecheck→ green, andcheck:test-typecheckreports "@objectstack/client's test layer compiles under packages/client/tsconfig.test.json; 0 file(s) / 0 error(s) held in test-typecheck-debt.json", so the new test file adds no debt.pnpm --filter @objectstack/plugin-auth typecheck→ green.A second reverse control, on the one declaration this PR adds: removing the new glob from
CROSS_PACKAGE_TEST_INPUTSand its$TURBO_ROOT$twin fromturbo.jsonturnscheck:cross-package-test-inputsred with "@objectstack/client names path(s) no declared glob covers … packages/plugins/plugin-auth/src/auth-route-ledger.conformance.test.ts (named in packages/client/src/auth-route-ledger-coverage.test.ts)" — so the declaration is load-bearing, not decorative. Restored byte-identically.Narrowed, and declared as narrowed:
pnpm check:type-check-debt --re-measureneeds the whole workspace built and was not run locally. It cannot move here:plugin-auth'stsconfig.jsonexcludes**/*.test.ts, its ledger entry is the hidden test layer (109 errors), and this PR's only change to that package is comment lines inside an existing test body — no identifiers, no imports.pnpm lintwas likewise narrowed to the changed files: eslint reports 3 of the 4 linted (it ignoresturbo.json— "File ignored because no matching configuration was supplied", identical on base), 0 errors, 0 warnings, counts read from--format json; and no untouched file's verdict can move, becauseeslint.config.mjsstates in its own words that this repo "runs oneeslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not". CI runs both in full regardless.No changeset
skip-changeset, and the label is on this PR. The diff is one new test file, comment lines in another test file, one entry in a gate's declaration table and its matchingturbo.jsoninput — nothing published, no user-visible behaviour, no public surface. Nothing here belongs in release notes.Generated by Claude Code
Generated by Claude Code