Skip to content

refactor(plugin-auth): remove the inert AUTH_SCIM_PROVIDER_SCHEMA export (enforce-or-remove) - #12008

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-11777-retire-auth-scim-provider-schema
Aug 25, 2026
Merged

refactor(plugin-auth): remove the inert AUTH_SCIM_PROVIDER_SCHEMA export (enforce-or-remove)#12008
os-trump merged 1 commit into
mainfrom
claude/issue-11777-retire-auth-scim-provider-schema

Conversation

@claude

@claudeclaudeBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Fixes#11777

Deletes AUTH_SCIM_PROVIDER_SCHEMA from packages/plugins/plugin-auth/src/auth-schema-config.ts under ADR-0049 enforce-or-remove. It was a publicly exported scimProvider column mapping that nothing read and nothing could ever hand to a plugin — a dead second source of truth for four column names whose live owner is the adapter layer.

⭐ Clause-② — declared, and this PR carries the gate

Clause-② applies: YES.needs:contract-review is on the card and is set on this PR from the moment it exists (dual carrier). The reasoning, since triage did not assert it: the gate is judged from content, and a tightening counts as much as a widening. Deleting a publicly exported symbol NARROWS this published package's public surface — a public-surface change in the direction people forget to count.

⛔ Not cleared here, not flipped ready, no auto-merge armed: the contract-review tier gates enqueueing and the floor only goes one way.

The zero, re-measured on this branch — with a positive control

A search tool's silence is an instrument reading, not evidence, so the count was taken against a ref (never the shared checkout, which is hundreds of commits behind) and controlled against a symbol known live in the same file.

measuredresult
target, pinned to a refgit grep -n AUTH_SCIM_PROVIDER_SCHEMA origin/main -- .1 hit — its own declaration (auth-schema-config.ts:1005), exit 0
positive control, same filegit grep -n buildOrganizationPluginSchema origin/main -- .15 hits across README, auth-manager.ts, and 4 test files, exit 0
in-file uses of the targetgrep -n in auth-schema-config.ts0 — declared, never referenced, not even locally
org-wideGitHub code search, AUTH_SCIM_PROVIDER_SCHEMA org:objectstack-ai1 result, the same declaration
sibling app checkoutgrep -rn over objectstack-ai/hotcrm @ 5eee1bd361exit 1 (no match), 0 lines

origin/main was at 497ded7801 for every reading. The control returning 15 hits is what makes the target's 1 a measurement rather than a broken channel.

Why it was inert by construction, not merely unused yet

Its own doc comment stated the reason it could never be applied: @better-auth/scim hardcodes its model and exposes no schema option — still true of the installed @better-auth/scim@1.7.0-rc.1, whose SCIMOptions declares no schema, modelName or fields member at all. There was nowhere to pass it. Its siblings in the same file (buildOrganizationPluginSchema, buildDeviceAuthorizationPluginSchema, …) are all genuinely handed to their plugins as schema options; this one was the odd member.

The mapping that actually governs those writes is the adapter layer — AUTH_MODEL_TO_PROTOCOL (objectql-adapter.ts:57, scimProvider: 'sys_scim_provider') plus the mechanical camelCase-to-snake_case field resolution — over the sys_scim_provider platform object, which is where the four column names are really declared (packages/platform-objects/src/identity/sys-scim-provider.object.ts). That route is pinned by the dedicated sso/scim block in better-auth-schema-parity.test.ts. The deleted copy could drift from it with every gate green, and the next reader could not tell which of the two was authoritative.

What replaces it

A NOTE, following the sibling sso NOTE already in this file, which records the same class of fact for buildSsoPluginSchema(). It states why no scim mapping exists, what owns the column names instead, what pins that owner, and the condition under which one could legitimately return (the plugin grows a schema option AND it is passed). It deliberately does not restate the four column names — a note that repeated them would re-create the second source of truth this PR removes.

Net: 31 lines deleted, 17 added, one file.

Changeset tier

minor, not major. Removing a public export is breaking in the semver sense, and the launch-window convention ships breaking as minor (scripts/check-changeset-no-major.mjs: every publishable package is in the Changesets fixed group, so one major promotes ~70 packages). The changeset declares **BREAKING (public export removed)** in the body so the declaration is not lost by the tier, which is exactly the spelling check-adr-0087-registration reads. No tier was forced and nothing needed to stop.

ADR-0087 disposition: not-required (no-migration-prescription) — no consumer exists to rewrite, and the constant is not a metadata surface (no Zod schema, no packages/spec declaration, no authorable key, no stored representation), so objectstack migrate meta has nothing to visit. runtime-interface-only was considered and does not apply: it requires the named symbol to resolve at HEAD, and this one is deleted.

Serial note — discharged, not deferred

Triage asked to check the SCIM stable migration's in-flight legs for collision. #11632 is pm:blocked with nothing cuttable (leg 1b cannot precede the pin move; the pin move is #3653, also blocked), so no in-flight branch exists in that family to collide with. If that migration's future design resurrects a mapping seam, that is its card's business — not a reason to keep this dead one alive now.

Verification

All readings below were taken on the final commit, b537844aa4 (git rev-parse --short HEAD from the same runs). Every exit code was captured before any pipe, and each line quotes the gate's OWN verdict rather than a bare $?.

Consumer-direction reverse verification — the control that actually proves the export is gone from what a consumer reads. A probe file was added to a REAL direct consumer (@objectstack/service-sms) importing the deleted symbol and a live sibling in one statement, then removed by the script's trap … EXIT INT TERM. The probe was confirmed on disk before tsc ran (control_mentions=2 target_mentions=2), and git status was clean afterwards.

src/__probe-11777.ts(2,36): error TS2724: '"@objectstack/plugin-auth"' has no exported
member named 'AUTH_SCIM_PROVIDER_SCHEMA'. Did you mean 'AUTH_SSO_PROVIDER_SCHEMA'?
target refused : 1
control refused : 0
total tsc errors: 1

The differential is what makes it a measurement: the target is refused, the sibling imported in the same statement is not, and the compiler proposes the sibling by name — so it read the REBUILT surface, not a cached one. total tsc errors: 1 also says the consumer package is otherwise clean: the only error in it is the one deliberately introduced.

The built type surface, read after the rebuild (dist/index.d.ts, mtime 04:39, after the edit): AUTH_SCIM_PROVIDER_SCHEMA 0 occurrences, AUTH_SSO_PROVIDER_SCHEMA 2 — same positive-control shape.

whatcommandverdict line
build (deps + package)os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-auth...' build"VERDICT command-exit 0 · held the lock 202s
typecheckpnpm --filter @objectstack/plugin-auth typecheck (tsc --noEmit + tsconfig.examples.json)VERDICT command-exit 0 · held the lock 10s — no diagnostics
suitepnpm --filter @objectstack/plugin-auth test -- --maxWorkers=2Test Files 74 passed (74) · Tests 1519 passed (1519)
repo-wide lintpnpm lint (= eslint . --no-inline-config)VERDICT command-exit 0 · held the lock 80s — the whole farm, not a narrowed subset
check:changeset-gate-self-tests ×3 self-tests (118 + 212 + 116 assertions)
check-adr-0087-registration --base origin/main✓ … 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition
check-changeset-no-major --base origin/main✓ This diff introduces no 'major' bump.
check-empty-changeset✓ No empty-frontmatter changeset introduced by this diff
check:objectui-changeset✓ objectui-range --self-test: all checks passed
check:published-files✓ … 69 publishable package(s) … declare a 'files' whitelist
check:slot-lookup✓ slot-lookup ratchet holds … none new · baseline key set verified against 497ded7: no files added
check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scanned
check:type-source-resolutioncheck-type-source-resolution OK — 93 tsc program(s) across 77 packages
check-plugin-teardown-shape✓ … 63 Plugin implementation(s) across 4647 source(s)
check:type-check-coveragecheck-type-check-coverage: OK — 65/78 workspace packages type-checked
docs-audit/check-affected-docs✓ affected-docs self-test: 437 cases pass.
release-rehearsal-clone --self-test✓ self-test passed
check-nul-bytescheck-nul-bytes: OK (scanned 6663 text file(s) … no raw ASCII control bytes)

Both changed files were also swept directly for control bytes (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', exit 1 — clean).

NOT MEASURED locally, and not reported as passes. Two gates refused to run for want of a full-workspace build, and each says so in its own words. CI builds the closure before these steps, so both are left to CI:

  • check:type-check-debt"--re-measure cannot run: 30 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk … Build the closure first, exactly as lint.yml does before this step." A declared refusal, not a verdict.
  • check:published-readme-exports"✗ check:published-readme-exports — 64 package(s) are not built, so this …". Every one of its 64 lines is does not exist. Build first; the count of findings that are NOT that is 0, and the count naming plugin-auth — the package this PR touches, which IS built here — is also 0.

The gate list was derived, not recalled: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths — the script takes the change set from the merge base itself), re-run against the final commit, and it names 13 families for this card's 2 paths. Twelve are above; the thirteenth, docs-audit/check-drift-comment.mjs, reads a comment on the PR and cannot run before the PR exists.

Out of scope

Filed as #12009 (unassigned, finding, no pm:queue): AUTH_SSO_PROVIDER_SCHEMA, in the same file, is referenced only from one comment and two frozen CHANGELOG lines — the same shape, measured with the same positive control. The file's own NOTE routes it to #8224, but that card is closed, and the follow-up that owned the remaining half, #10074, is closed not_planned on ruling A — do not rewire the seam. Ruling A settles where the mapping lives; it never ruled on the exported constant that copies the names alongside it, so the question is genuinely untracked rather than answered. Not touched here: it differs from the scim case in ways that could change its disposition (the sso schema option is real on @better-auth/sso@1.7.1, so it is unused by choice rather than inert by construction), and #11777's scope is one symbol.

AUTH_JWKS_SCHEMA looked similar on a per-file hit count and is not dead — it is consumed in-file at auth-schema-config.ts:868. Recorded here so the next reader does not re-derive it.


Generated by Claude Code


Generated by Claude Code

`AUTH_SCIM_PROVIDER_SCHEMA` was a publicly exported `scimProvider` column
mapping with zero consumers -- exactly one repo-wide hit, its own declaration
-- and inert by construction: `@better-auth/scim` hardcodes its model and
exposes no `schema` option (still true of the installed
`@better-auth/scim@1.7.0-rc.1`, whose `SCIMOptions` declares no `schema` /
`modelName` / `fields` member), so there was nowhere to hand it.
Removed under ADR-0049 enforce-or-remove: it was a second source of truth for
the four SCIM column names, where the load-bearing one is the adapter layer
(`AUTH_MODEL_TO_PROTOCOL` + camelCase-to-snake_case field resolution in
`objectql-adapter.ts`) over the `sys_scim_provider` platform object. A dead
copy cannot fail when it drifts, and the next reader cannot tell which of the
two is authoritative. A NOTE in its place records why no mapping exists and
what owns the names instead, following the sibling sso NOTE in the same file.
Fixes#11777
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-auth, touching 7 documentable anchor(s).

29 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 227c1788d55da8f94127137bcf75ba2bf08b15d8.

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

What this run could not see
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: 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 — 11 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 227c1788d55da8f94127137bcf75ba2bf08b15d8packageMentionDocs.

Which tree this was computed on

This run read content/docs from 3567bde3f55cc97b5346bc5c51ce559686854450 — the merge of head b537844aa40467248154b5a0982d40853ea3cc7b into base 227c1788d55da8f94127137bcf75ba2bf08b15d8, 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 3567bde3f55cc97b5346bc5c51ce559686854450 && git checkout 3567bde3f55cc97b5346bc5c51ce559686854450
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 227c1788d55da8f94127137bcf75ba2bf08b15d8 b537844aa40467248154b5a0982d40853ea3cc7b && git checkout -B drift-repro 227c1788d55da8f94127137bcf75ba2bf08b15d8 && git merge --no-ff b537844aa40467248154b5a0982d40853ea3cc7b
node scripts/docs-audit/affected-docs.mjs --json 227c1788d55da8f94127137bcf75ba2bf08b15d8

⚠️ 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 227c1788d55da8f94127137bcf75ba2bf08b15d8 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-trumpClaude

Copy link
Copy Markdown
Collaborator

Contract review PASS at CONTRACT_REVIEW_TIER (verdict + readings on the card: #11777). Label cleared on both carriers; flipping ready and entering the merge queue.


Generated by Claude Code

Merged via the queue into main with commit 911da5fAug 25, 2026
41 checks passed
@os-trump
os-trump deleted the claude/issue-11777-retire-auth-scim-provider-schema branch August 25, 2026 09:45
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AUTH_SCIM_PROVIDER_SCHEMA is a publicly exported mapping with zero consumers — a second, inert source of truth for the SCIM column names

2 participants

@os-trump@claude