Skip to content

feat(auth): migrate @better-auth/scim to stable 1.7.1 — seven-model provision, app-owned bearer verification (#3653) - #12726

Merged
os-sales merged 6 commits into
mainfrom
claude/issue-3653-scim-stable-pin-move
Aug 27, 2026
Merged

feat(auth): migrate @better-auth/scim to stable 1.7.1 — seven-model provision, app-owned bearer verification (#3653)#12726
os-sales merged 6 commits into
mainfrom
claude/issue-3653-scim-stable-pin-move

Conversation

@os-sales

@os-sales os-sales commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Fixes #3653

Leg 2′ of epic #11632 — the @better-auth/scim stable-1.7.x migration, landed atomically as the parity gate's exact-set assertion and the registry's bidirectional conformance test force (neither half can land alone; maintainer rulings 2026-08-24 「os#3653 立项迁移」 and 2026-08-25 「#11693 + #11757 SCIM 目前没有真实客户在用,不需要考虑旧数据,只要现有系统能平滑升级到新版本就可以。」 govern; quoted verbatim, untranslated, per repo rule).

Branch note (forked window): this branch continues the measurement base c87d731f6 (pin move alone, deliberately not a landing) and merges origin/main (15bf9e8) in — merge commit, no rebase of the shared base.

What lands

  • Pin: @better-auth/scim resolves 1.7.1 exactly (workspace override target + plugin-auth declaration both exact). ^1.7.1 resolves to 1.7.2, whose better-auth/@better-auth/core peers at ^1.7.2 would be silenced (not satisfied) by the family overrides — epic-owner pin ruling, adopted at the ACCEPT (comment 5437430514). Floating to 1.7.2 is its own follow-up card. Stale rc-hold comment blocks in pnpm-workspace.yaml rewritten to the landed state.
  • Seven platform objects for the stable model set under packages/platform-objects/src/identity/: sys_scim_connection_binding, sys_scim_group, sys_scim_group_member, sys_scim_identity_tombstone, sys_scim_projection_grant, sys_scim_subject, sys_scim_user — columns mirror the installed 1.7.1 plugin.schema (dumped, not assumed), uniques mirrored as indexes, managedBy: 'better-auth', generic API read-only.
  • sys_scim_connection_credential — the ObjectStack-owned credential store the verifyBearerToken route requires (stable upstream stores NO credential: storeSCIMToken/generate-token/scimProvider are 0 occurrences; no stable model declares a token/secret column). Deliberately not a sys_scim_provider resurrection: one row per bearer credential (staged rotation), managedBy: 'engine-owned', capability-gated, digest-only at rest.
  • Registry (packages/spec/src/system/constants/platform-object-names.ts): eight additive names in the platform-objects group — the measured-unsplittable spec edit. sys_scim_provider stays (retirement is Retire sys_scim_provider — consumer sweep first, no data migration (leg 1a of #11632, ruled on #11693) #11757's, Blocked-by: this card). system-names.ts untouched, as measured.
  • Bridge: seven AUTH_MODEL_TO_PROTOCOL entries; parity gate green with KNOWN_UNMAPPED_MODELS shrunk to the empty set (exact-set assertion kept, both directions — the ruled direction; nothing widened).
  • auth-manager.ts: the stale scim({ storeSCIMToken: 'hashed' }) (a TS2353 compile error on stable, measured) replaced with the stable shape — connections: [] + application-owned authentication.verifyBearerToken resolving the connection from a credential row at request time. Connections stay runtime data; managedConnections NOT adopted (ruling). Auth secret now resolved once (memoized) so better-auth and the credential digests share key material — generateSecret()'s dev fallback is per-call.
  • scim-connection-service.ts (new): mint + digest + verify. At rest: HMAC-SHA-256 keyed by the deployment auth secret over a domain-separated bearer, base64url — parity or better vs rc.1's unsalted SHA-256 (keyed ⇒ offline-table-resistant), deterministic on purpose (the digest is the unique lookup key). Keying consequence stated in the file header: secret rotation invalidates stored SCIM credentials ⇒ IdP token reissue, which is already the epic's migration-day posture on every path.
  • Native adapter transactions, scoped to SCIM protocol requests (objectql-adapter.ts + scimRequestScope in scim-connection-service.ts): stable scim refuses to mount on the factory's sequential-fallback transaction (assertNativeSCIMTransactions demands a function — found by running the real manager, not by reading release notes). Implemented over engine.transaction() (ambient handle, ADR-0034), opened ONLY inside an authenticated SCIM request — the verifier marks its request's async chain via AsyncLocalStorage.enterWith. The scoping is load-bearing and was measured twice, not hedged: better-auth wraps its OWN whole request flows in adapter.transaction (runWithTransaction), so (a) a fail-closed require: true variant 500'd every sign-up on the memory engine (275 plugin-auth tests red), and (b) an unconditional real-transaction variant starved the single-connection sqlite pools — the dogfood showcase boot deadlocked on Acquire connection error to the 180s hook timeout, in CI (Dogfood Regression Gate 3/3 on da00d48) and reproduced locally; with the scoping the same dogfood file passes in 19s (13/13). Core flows keep the sequential behaviour they have always had under the factory's as-is default; SCIM provisioning gets the genuine atomicity upstream demands (the credential-at-rest suite drives a real SCIM 2.0 request through the scoped transaction over better-sqlite3).
  • credential-at-rest-posture.test.ts restructured honestly, never relaxed: the upstream-cleartext control arm is no longer measurable (upstream stores nothing), so its successor pins that vacating fact from the installed artifact (no credential-shaped column on any stable model + generate-token 404 through the real manager) plus the keyed digest relationship recomputed independently with node:crypto, three wrong-shape negatives (unkeyed SHA-256 = the rc.1 shape, wrong key, missing domain separation), and live rejection paths: forged bearer → 401 with the SCIM error envelope (urn:ietf:params:scim:api:messages:2.0:Error, status: '401'), revoked → 401, expired → 401.
  • D7 gate (managed-extension-fields.test.ts): scim constructed with stable-legal options; seven UNMAPPED_MANAGED_OBJECTS entries (same bridge reason as sys_sso_provider); sys_scim_provider entry re-grounded (no model derives on stable).
  • Epic item 7, same stroke: the one retiring allowedVersions key @better-auth/scim>better-call removed in its two declared places (packages/cli/src/commands/init.ts SCAFFOLD_ALLOWED_PEER_VERSIONS, packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml) — stable 1.7.1 peers better-call@1.4.0 exactly. Both presence ratchets flipped to absence pins (packages/cli/test/init.test.ts, template-consistency.test.ts). Re-derived from the tree: better-auth>better-sqlite3 and the four @better-auth/utils entries STAY (separate, unmet retirement conditions), and the separation pin now guards that they survived the better-call retirement.
  • Translations: regenerated with pnpm i18n:extract only (extract config + bundle-ownership list extended — the two lists that config file instructs be kept in sync).
  • Changeset: minor across the six touched published packages, with the semver level argued in the changeset body (default-off feature, zero real customers by ruling, smooth-upgrade constraint satisfied: every table the installed library can write exists at this commit).

Declared deviation from the claim surface (falsifies one PM mechanism assumption)

packages/plugins/plugin-security/src/objects/default-permission-sets.ts — 7 additive names in BETTER_AUTH_MANAGED_OBJECTS. Its drift pin (default-permission-sets.test.ts #3325) asserts bidirectionally that every platform-objects export with managedBy: 'better-auth' is in that list, so declaring the objects honestly forces this edit exactly the way the registry's conformance test forces the spec edit. The alternative — omitting managedBy to stay inside the administrative boundary — would ship dishonest metadata to dodge a gate (wrong on the AI-error-proofing axis) and would drop the write-deny posture on tables the library owns. The edit is mechanical (content dictated by the pin test), additive, and verified by that package's own suite below. Also outside the letter of the surface: packages/platform-objects/scripts/i18n-extract.config.ts and src/apps/translations/bundle-ownership.test.ts (the enumerated-input and in-sync-list halves of the "generated translation bundles" surface item), two stale-prose touch-ups where files named the retired storeSCIMToken wiring as current (sys-scim-provider.object.ts header, sso-client-secret.ts header), and scripts/check-prerelease-pin-watch.mjs — the self-retirement leg its own header promised, forced red on this PR's CI by the pin move itself (see Verification record).

Not in this PR (deliberately)

  • sys_scim_provider removal — Retire sys_scim_provider — consumer sweep first, no data migration (leg 1a of #11632, ruled on #11693) #11757 carries it, Blocked-by: this card. The object, its registry entry, its AUTH_MODEL_TO_PROTOCOL entry and its D7/permission-set entries stay.
  • Floating scim to 1.7.2 — its own follow-up with the family moved in step.
  • The Setup admin surface over sys_scim_connection_credential (mint/revoke UI) — a separate epic leg; mint/verify are service-level here and fully test-driven.
  • Scheduling the migration-day IdP token reissue — human-owned epic item; nothing in this PR implies it has been scheduled. SCIM stays default-off (OS_SCIM_ENABLED).

Verification record

All heavy runs via scripts/pm/os-verify-lock.sh; exit codes captured before any pipe; verdicts below quote each gate's own line. Full suites on the changed packages:

  • @objectstack/spec platform-object-names.test.ts: Test Files 1 passed (1) / Tests 7 passed (7) (bidirectional registry conformance with the 8 new objects).
  • @objectstack/platform-objects build + full suite: Test Files 33 passed (33) / Tests 523 passed (523).
  • @objectstack/plugin-auth build green (check-dts-emitted 2/2); parity + D7 gates: Test Files 2 passed (2) / Tests 55 passed (55); credential-at-rest-posture.test.ts: Tests 5 passed (5); full suite: Test Files 81 passed (81) / Tests 1660 passed (1660) (a first run failed 275 tests under the fail-closed transaction variant — one of the two measurements that produced the scoped design above).
  • @objectstack/cli full suite: Test Files 197 passed (197) / Tests 2237 passed (2237).
  • create-objectstack full suite: Test Files 14 passed (14) / Tests 177 passed (177) (after one honest fix: the first template-comment draft cited an issue number, which starter-comments-self-contained.test.ts correctly refused).
  • pnpm check:i18n green after CLI build (first run was PREREQUISITE NOT MET — not measured — rerun after turbo run build --filter=@objectstack/cli).
  • Ablation on the credential-at-rest pin (mutation proven on disk BEFORE any verdict; restore proven by blob equality, not by trap): mint mutated to store the cleartext bearer (token_digest: token); proof injected-marker=1, original-line=0, blob 8bca1cf3 → 601d0c56; predicted direction 2 failed | 3 passed (at-rest + authenticate red, rejections/oauth/upstream-arm green); measured Tests 2 failed | 3 passed (5) — predicted and measured agree; restore blob 8bca1cf3 = HEAD blob, marker absent. No dist leg needed: the mutation target resolves via relative src imports under vitest (not through package exports).
  • Full plugin-auth suite re-run on the FINAL shape (after the transaction scoping): Test Files 81 passed (81) / Tests 1660 passed (1660); plugin-security full suite 86 passed / 1561 passed; spec full suite 435 passed / 11541 passed; typechecks green for all six changed packages (their tsconfigs exclude **/*.test.ts — pre-existing posture; the edited test files are exercised by the vitest runs above).
  • Gate union from node scripts/pm/dispatch-gates.mjs (no paths passed, changed set read from the merge base) at head dba7c1d: 44/44 families green — including check:override-consistency, check:nul-bytes, check:i18n, check:i18n-coverage, check:type-check-debt (--re-measure OK, none above its recorded number), check-prerelease-pin-watch (retired-state self-test), the changeset trio, and check-osv-exemptions. Three of the first-pass reds were unbuilt-workspace PREREQUISITE NOT MET-class readings, re-run green after turbo run build --filter='./packages/*' --filter='./packages/*/*'. Deliberately excluded as CI-context runs: the docs-audit pair (its bot comment on this PR is the evidence it ran) and release-rehearsal-clone (cut-rc/release workflow, not PR CI).
  • CI failure Add metamodel interfaces for ObjectQL/ObjectUI contract #1 (Stable release watch for prerelease pins): the watcher's own --self-test asserted the repo still HAS a prerelease pin — the retirement leg its header promised was missing. Reproduced locally (exit 1, same line), fixed in da00d48 by flipping the repo-anchored pair to the retired state (overrides must still parse non-empty so a parser break cannot masquerade as retirement), re-run green.
  • CI failure ✨ Set up Copilot instructions #2 (Dogfood Regression Gate 3/3): the transaction-scoping fix above (dba7c1d), root-caused by local reproduction, not waved off as a flake.

Operational notes

  • SCIM remains default-off. Deployments that enable it mint connection credentials via mintScimConnectionCredential (plaintext shown once); rc.1 tokens are not portable on any path.
  • The standing "do not let the IdP push groups" boundary is CLOSED by this PR — all seven stable models, groups included, are provisioned and column-checked.

Generated by Claude Code

os-litant and others added 4 commits August 27, 2026 09:53
…e line (measurement base, #3653)

Part of #3653. NOT a proposed landing: leg 2' stopped on a scope fork before
the provisioning half could be written, so this branch carries the pin move
alone as the reproducible base every measurement in the report was taken on.
A pin-move-only tree is red by construction (7 failed | 21 passed on the
parity gate) and must never be landed on its own.

  - packages/plugins/plugin-auth/package.json  1.7.0-rc.1 -> ^1.7.1
  - pnpm-workspace.yaml  '@better-auth/scim@<1.7.0-rc.1': '1.7.0-rc.1'
                      -> '@better-auth/scim@<2.0.0': '^1.7.1'
    (major-boundary bound, matching the rest of the family per this file's
    own doctrine; the old bound sat AT the rc precisely so a <2.0.0 bound
    could not rewrite the stable release down onto it.)

Measured consequences, all reproducible from this commit:

  - '^1.7.1' resolves to 1.7.2, not 1.7.1. Every prior measurement on this
    epic was taken against 1.7.1. Re-measured on 1.7.2: same seven models,
    same three-way constructor predicate, same absent scimProvider.
  - scim lands on 1.7.2 while the rest of the family stays 1.7.1. 1.7.2 peers
    '@better-auth/core': '^1.7.2' and 'better-auth': '^1.7.2', which 1.7.1
    does not satisfy; this repo's existing overrides rewrite those peer ranges
    down to '^1.7.1' in the lockfile, so nothing reports the mismatch. One
    copy each of better-auth and @better-auth/core remain in the store.
  - check:prerelease-pins retires itself: "No prerelease pin left in
    pnpm-workspace.yaml overrides - nothing to watch", exit 0.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0194kbQJxUvv2yvsGRtuXpP5
… 1.7.1 — provision the seven models, app-owned bearer verification (#3653)

Part of #3653 (leg 2' of epic #11632). The pin resolves to 1.7.1 EXACTLY
(not ^1.7.1, which resolves to 1.7.2 whose better-auth/core ^1.7.2 peers
only the workspace overrides' silencing would 'satisfy'; floating is its
own follow-up card).

- pnpm-workspace.yaml + plugin-auth: exact 1.7.1 pin, stale rc-hold
  comment blocks rewritten to the landed state.
- packages/platform-objects/src/identity/: the seven stable models
  provisioned as platform objects (scimConnectionBinding, scimGroup,
  scimGroupMember, scimIdentityTombstone, scimProjectionGrant,
  scimSubject, scimUser) plus sys_scim_connection_credential, the
  ObjectStack-owned credential store for the app-owned verifyBearerToken
  route (stable upstream stores no credential at all). Translation
  bundles regenerated with pnpm i18n:extract only.
- packages/spec platform-object-names.ts: eight additive registry names
  (the measured-unsplittable spec edit; sys_scim_provider stays — its
  retirement is #11757's).
- plugin-security BETTER_AUTH_MANAGED_OBJECTS: the seven library-managed
  names, forced additively by the bidirectional drift pin
  (default-permission-sets.test.ts) the moment the objects declare
  managedBy: 'better-auth'.
- plugin-auth scim-connection-service.ts: mint + digest + verify owned
  outright; at rest only HMAC-SHA-256(secret, domain-separated bearer),
  base64url — at parity or better than the rc.1 unsalted SHA-256.
- auth-manager.ts: stable constructor shape — connections: [] plus
  authentication.verifyBearerToken resolving the connection from a
  sys_scim_connection_credential row at request time; auth secret
  resolved once (memoized) so digests and better-auth share key material.
- objectql-adapter.ts: seven AUTH_MODEL_TO_PROTOCOL bridge entries, and
  NATIVE adapter transactions over engine.transaction({ require: true })
  — stable scim refuses the sequential fallback by assertion.
- better-auth-schema-parity.test.ts: KNOWN_UNMAPPED_MODELS shrinks to
  the empty set (exact-set assertion kept, both directions).
- credential-at-rest-posture.test.ts: restructured honestly — the
  upstream-cleartext control arm is no longer measurable (upstream
  stores nothing); its successor pins that vacating fact plus the keyed
  digest relationship, negatives for the unkeyed/wrong-key/undomained
  shapes, and live 200/401 rejection paths incl. revocation and expiry.

Co-authored-by: Claude <noreply@anthropic.com>
…l suppression with the pin; adapter transaction degrades declared (#3653)

Epic item 7, same stroke as the pin move: stable 1.7.1 peers
better-call@1.4.0 exactly, so the rc-era allowedVersions key retires in
its two declared places (SCAFFOLD_ALLOWED_PEER_VERSIONS and the blank
template) and both presence ratchets flip to absence pins. The
better-auth>better-sqlite3 and four @better-auth/utils entries stay —
re-derived from the tree, their conditions are separate and unmet; the
separation pin now guards that they survived the retirement.

Adapter transactions: better-auth routes its own multi-writes (sign-up
included) through adapter.transaction — measured: a fail-closed
require:true 500'd every sign-up on the memory engine — so the
implementation keeps the two declared degrades those flows always had
(no-transaction-API engines run the callback directly; drivers without
beginTransaction follow the engine's ADR-0119 D1 warn-once degrade).
SQL drivers get real atomic transactions.

Also: changeset (minor across the six touched published packages, level
argued in the body), and two stale-prose corrections where files
described the retired rc.1 storeSCIMToken wiring as current.

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:system tests tooling labels Aug 27, 2026
…test with the pin (#3653)

The watch script promised to retire itself when the last prerelease pin
went stable, and its CLI does (empty watch list is the success state) —
but its --self-test kept an anti-vacuity assertion that the repo's own
pnpm-workspace.yaml parses to a NON-empty watch list, so the 'Stable
release watch for prerelease pins' job went red on the very commit that
redeemed the pin. Flip the pair to the retired state without opening the
hole the old check guarded: the overrides block must still parse
non-empty (a parser break cannot masquerade as retirement), and the
derived watch list must be empty, with the failure text instructing how
to flip back if a prerelease pin ever reappears.

Reproduced the CI failure locally before the fix (self-test exit 1, same
failing line) and measured exit 0 after; check-self-test-workflow-commands
green (140 scripts scanned).

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/cli, objectstack-blank, @objectstack/platform-objects, @objectstack/plugin-auth, @objectstack/plugin-security, @objectstack/spec, touching 113 documentable anchor(s). ⚠️ 6 changed file(s) yielded no anchor (packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml, packages/platform-objects/src/identity/index.ts, packages/platform-objects/src/identity/sys-scim-provider.object.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

76 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json d29e42f8b1b44b92ea58c1d05e619f27c027c3a9.

7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 6 changed file(s) yielded no anchor (packages/create-objectstack/src/templates/blank/pnpm-workspace.yaml, packages/platform-objects/src/identity/index.ts, packages/platform-objects/src/identity/sys-scim-provider.object.ts, …) — pages documenting those are invisible to this run
  • 2 cross-cutting symbol(s) contributed no route anchor: organizationId (4 routes), isSystem (5 routes)
  • 2 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 34 pages), sys_user (literal, 29 pages)
  • 10 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d29e42f8b1b44b92ea58c1d05e619f27c027c3a9packageMentionDocs.

Which tree this was computed on

This run read content/docs from c8829449e4302636ac0f7ab81cfd20bc0dcaae27 — the merge of head dba7c1d21892ef3d87f9fe25ccbec125804d32c0 into base d29e42f8b1b44b92ea58c1d05e619f27c027c3a9, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c8829449e4302636ac0f7ab81cfd20bc0dcaae27 && git checkout c8829449e4302636ac0f7ab81cfd20bc0dcaae27
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d29e42f8b1b44b92ea58c1d05e619f27c027c3a9 dba7c1d21892ef3d87f9fe25ccbec125804d32c0 && git checkout -B drift-repro d29e42f8b1b44b92ea58c1d05e619f27c027c3a9 && git merge --no-ff dba7c1d21892ef3d87f9fe25ccbec125804d32c0

node scripts/docs-audit/affected-docs.mjs --json d29e42f8b1b44b92ea58c1d05e619f27c027c3a9

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs d29e42f8b1b44b92ea58c1d05e619f27c027c3a9 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…equests (#3653)

Measured twice on the unscoped variant: better-auth wraps whole request
flows in adapter.transaction (runWithTransaction), so opening a real
driver transaction around every sign-in/sign-up starved the
single-connection sqlite pools — the dogfood showcase boot deadlocked on
'Acquire connection error' until the 180s hook timeout, in CI and
reproduced locally on this branch, with 337 sibling dogfood tests green.

The scim verifier now marks its request's async chain (AsyncLocalStorage
enterWith), and config.transaction opens a real engine.transaction only
inside that scope — exactly where assertNativeSCIMTransactions demands
atomicity. Every other better-auth flow keeps the sequential behaviour
it has always had under the factory's as-is fallback, so nothing existing
weakens. Re-measured after the fix: the deadlocked dogfood file passes in
19s (13/13), the credential-at-rest suite (which drives a real SCIM 2.0
request through the scoped transaction over better-sqlite3) stays green,
and the full plugin-auth suite is 81 files / 1660 tests green.

Co-authored-by: Claude <noreply@anthropic.com>
@os-sales
os-sales marked this pull request as ready for review August 27, 2026 15:14
@os-sales
os-sales enabled auto-merge August 27, 2026 15:14
@os-sales
os-sales added this pull request to the merge queue Aug 27, 2026
Merged via the queue into main with commit 366f895 Aug 27, 2026
42 checks passed
@os-sales
os-sales deleted the claude/issue-3653-scim-stable-pin-move branch August 27, 2026 15:37
os-trump pushed a commit that referenced this pull request Aug 28, 2026
Remove the rc.1-era @better-auth/scim connection-row object and every
in-repo reader, plus its registry line in
packages/spec/src/system/constants/platform-object-names.ts (one PR, both
halves — the registry conformance test asserts the set bidirectionally).
Stable @better-auth/scim 1.7.x derives no scimProvider model (#3653 landed
in PR #12726), so the object backed nothing. No data migration by ruling.

The ADR-0066 D3 capability-gate pin moves from the retired object to the
surviving sibling SysSsoProvider so the gate cannot be dropped silently.
Translation bundles regenerated (pnpm i18n:extract).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation protocol:system size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SCIM: 停在 @better-auth/scim rc.1,等正式版再整体迁移 —— rc.2 换掉了整套模型

3 participants