Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-auth): make the SCIM parity gate reach its model diff on stable @better-auth/scim - #11429
Merged
Merged
Conversation
… accepts The gate is the declared safety net for the @better-auth/scim migration (#3653): the day the pin moves off 1.7.0-rc.1 it is supposed to name every model whose platform object is missing. It did not open at that moment. `scim({})` is legal on the pinned rc.1 (options optional, nothing read at construction), but the stable factory's first statement is `validateConnections(options)`, which reaches `options.connections.length` before any existence check. The schema is read in the describe body and in a `for` header, so on stable the file dies during COLLECTION with TypeError: Cannot read properties of undefined (reading 'length') and prints that instead of the model diff it promises. Construct with `connections: []` plus a never-invoked bearer-token verifier -- the cheapest shape stable accepts (an empty connection list is only legal when a verifier or the managed catalog resolves connections), and inert on the pinned rc.1, whose factory spreads its options and reads only the keys it knows. `managedConnections` is deliberately not configured: the auth manager does not pass it, and it is what adds the conditional managed trio. A constructor mismatch can only ever be a collection failure here -- the `for` header has to read the schema -- but it does not have to be illegible, so the call is wrapped and re-thrown with the vendor error as `cause` and an instruction naming the constructor as the broken half. Gate-only change: no product code, no published surface. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
…im-parity-gate-ctor
…pares the real surface
The repair picked the cheapest options stable accepts. Cheapest is only safe if
it does not narrow WHAT the gate compares, so that half is now measured rather
than assumed: against the published 1.7.1, `connections: [] +
authentication.verifyBearerToken` and a realistic static connection
(`connections: [{ id, credentials: [{ type: 'bearer', id, token }] }]`) declare
the SAME seven models with the SAME column sets.
Also records why a connection entry is not the smaller input it looks like:
validateConnections goes on to read `connection.credentials.length`, so a
half-specified entry throws the same TypeError one line further down -- measured
while probing this.
Comment-only; no assertion or construction changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4Contributor
📓 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. |
os-sam
marked this pull request as ready for review
August 23, 2026 18:06
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 24, 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 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#11380
The guard that did not open at the promised moment
The SCIM parity gate (
better-auth-schema-parity.test.ts, the@better-auth/sso + @better-auth/scimblock) was written down on #3653 as the safety net for the@better-auth/scimmigration: "the gate will report all seven model differences the instant you upgrade." It does not. On the stable line it dies in collection with an uninformative vendorTypeErrorand prints no model list at all — a migrator gets a crash from the guard that exists to inform them. Declared, not enforced.Reproduced first, so the repair has a before. Pristine gate, published
@better-auth/scim@1.7.1:scim({})is legal on the pinned1.7.0-rc.1— options are optional there and nothing is read at construction. The stable factory's first statement isvalidateConnections(options), which reachesoptions.connections.lengthbefore any existence check. The schema is read in the describe body and in aforheader, so the failure lands at collection time and takes the whole file with it.What this changes
A single test file. Three
scim({} as never)call sites become oneSCIM_GATE_OPTIONSconstant —connections: []plus a never-invokedauthentication.verifyBearerToken— constructed through a smallconstructScim()wrapper that re-throws with the vendor error attached and says constructor mismatch, not parity failure, since a construction error here can only ever be a collection failure and should at least be legible.Cheapest options, but not a narrower comparison — that is the half that would have made the repair worthless, so it is measured rather than assumed. Against the published 1.7.1, the gate's shape and a realistic static connection (
connections: [{ id, credentials: [{ type: 'bearer', id, token }] }]) declare the same seven models with the same column sets.managedConnectionsis deliberately not configured: the auth manager does not pass it, and it is what adds the conditionalscimManagedConnection/scimManagedCredential/scimManagedConnectionEventtrio (measured: 7 models without it, 10 with).⛔ Out of scope by dispatch and untouched here: the
@better-auth/scimpin (plugin-auth/package.json:29, still1.7.0-rc.1),auth-manager.ts'sscim({...})call, both scaffoldpeerDependencyRules.allowedVersionssuppressions, andpackages/spec. This is gate-only: no product code, no accept/reject behaviour, no published surface — henceskip-changesetrather than a changeset (test files are excluded from every package'sfileswhitelist, whichcheck:published-filesre-verified on this tree).Answering the two questions the dispatch asked
node_modulessymlink pointed at it for the duration of each run, then restored by a trap. No tracked manifest was touched;git statuswas clean at every leg boundary, and the restore leg is printed in each harness log.The diff the repaired gate prints (published 1.7.1)
The full run is posted on #3653, where the migration ruling resumes — #3653 stays open, nothing here decides it. Summary: 7 failed | 21 passed (28), where the pristine gate produced
Tests no tests.plus
expected [ 'user', 'session', 'account', …(9) ] to include 'scimProvider'(the model the bridge maps is gone on stable) and one namedmust map to a platform object via AUTH_MODEL_TO_PROTOCOLfailure per new model.Verification — all at
7544c02b5bGate union derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, no hand-supplied paths (1 path vs merge base122ef3860). All 13 families green, each read from its own verdict line:check:published-files✓ … 69 publishable package(s) … admits no test, test-harness config or build scriptcheck:slot-lookup✓ slot-lookup ratchet holds … none newcheck:test-source-aliascheck-test-source-alias OK — 72 packages with tests scannedcheck:type-source-resolutioncheck-type-source-resolution OK — 77 packages … scannedcheck-ci-filter-parity.mjsOK: all 89 declared cross-package glob(s) … coveredcheck-plugin-teardown-shape.mjs✓ … 63 Plugin implementation(s) across 4547 source(s)docs-audit/check-affected-docs.mjscheck:query-options-erasure✓ query-options-erasure ratchet holds … none newcheck:type-check-coveragecheck:engine-double-contractOK — 388 pinned, 133 in the DEBT ledger, 2 exemptcheck:cross-package-test-inputsOK: 14 package(s) read outside themselves, all declaredcheck:where-matcher✓ where-matcher conformance holds … 0 silently-wrongcheck:nul-bytescheck-nul-bytes: OK (scanned 6411 text file(s) … no raw ASCII control bytes)pnpm --filter @objectstack/plugin-auth test— 70 files / 1455 tests passed,VERDICT command-exit 0.pnpm --filter @objectstack/plugin-auth typecheck— green (VERDICT command-exit 0) once the package's owndistis built; the first attempt'sexamples/basic-usage.ts … TS2307was an unbuilt-package artefact, not this change.pnpm lint— repo-wideeslint . --no-inline-config,VERDICT command-exit 0. No narrowing claimed.24 passed (24)both ways — the repair moves nothing on the version CI actually runs.Declared narrowing, one family:
check:type-check-debt(the--re-measureratchet) needs the whole ledgered dependency closure built and was not run locally; CI runs it. Substituted, since this change adds test code to aTEST_DEBTpackage: the same TEST_DEBT-shaped tsc project (package tsconfig + explicitfileslist putting the hidden*.test.tsback, 116 files) measured 97 errors before (origin/main) and 97 after, identical error sets. Reverse control: injecting one deliberate type error into the edited file moves it to 99 and names the file and line — the instrument can see this file, so the zero delta is a measurement rather than a blind spot.Generated by Claude Code