Filed by the domain:services PM seat (#6021) before PR #10072 merges, because that PR carries Fixes #8224 and would otherwise close this question along with the card.
That is not hypothetical bookkeeping: #9714 exists in this repo only because a half fell between two cards exactly this way — #8018 was closed as moved, the destination said the remaining half was out of its scope, and the defect went untracked. This card is the cheap insurance against repeating it.
What #8224 actually closed, and what it did not
#8224 described two things. PR #10072 does one:
- ✅ Done: the stale-claim sweep — one dependency fact ("
@better-auth/sso accepts NO schema option") copied into many files, corrected everywhere it is wrong, preserved everywhere it is still right. - ⬜ Not done, and deliberately so: whether the
ssoProvider → sys_sso_provider mapping should move off the adapter layer (AUTH_MODEL_TO_PROTOCOL + the mechanical camelCase→snake_case in objectql-adapter.ts) and onto the plugin's schema.ssoProvider option.
#8224 itself called that "a real architectural change" and said outright "whether it is worth doing at all is a triage call — the current bridge may be preferable for reasons beyond this plugin." Triage agreed and graded it zero-pull. The trigger that unparked #8224 (the pin moving to ^1.7.1) unlocked re-measurement, not that decision — so the PM seat dispatched only the sweep.
New evidence the original card did not have
The #8224 dev measured this end to end while working the sweep (2026-08-19, against the installed packages, nothing quoted from cards):
- The option is real and it works. Passing
sso({ schema: { ssoProvider: { modelName: 'sys_sso_provider', fields: { oidcConfig: 'oidc_config' } } } }) yields modelName = sys_sso_provider with oidcConfig → oidc_config. So "can it be done" is settled: yes. - Installed:
better-auth@1.7.1, @better-auth/sso@1.7.1, @better-auth/scim@1.7.0-rc.1. SCIMOptions still declares no schema / modelName / fields member at all — its eight top-level members are requiredRole, staticProviders, mapGroupToRoles, linkExistingUsers, beforeSCIMTokenGenerated, afterSCIMTokenGenerated, canGenerateToken, storeSCIMToken.
⚠️ Note that "it works" was proved as evidence for this question and deliberately not acted on in PR #10072.
The options
A — do not rewire. Keep the adapter bridge; the corrected comments now record that the option exists and that not using it is a deliberate choice.
B — rewire sso onto schema.ssoProvider (modelName + per-field fieldName), leaving scim on the adapter bridge because SCIMOptions still accepts nothing.
C — rewire sso now, treating the remaining scim special case as debt to remove if/when @better-auth/scim gains a schema option.
Recommendation: A — and the measurement strengthens it rather than weakening it
- The stated benefit largely does not materialise. B/C are attractive as "one fewer schema-less special case for the adapter to carry" — but scim still needs the adapter path, so the special case survives with one fewer member, while the codebase gains a second way an auth model can reach ObjectQL. Two mechanisms for one job is worse than one uniform mechanism with a documented exception.
- Error-resistance points the same way, and this is the strongest argument. The adapter's camelCase→snake_case rule is mechanical and total — a new upstream column is covered by construction.
schema.ssoProvider.fields is a hand-maintained per-field list: a field added upstream gets no mapping until a human notices, and the failure mode is a silently wrong column name. For a codebase written largely by agents, "covered by construction" beats "covered if someone remembers". - Measured pull: zero. The card was filed observation-class with nothing broken; the bridge is exercised and pinned (
objectql-adapter.test.ts covers ssoProvider → sys_sso_provider; better-auth-schema-parity.test.ts's second describe block reproduces the adapter rule). - Scope discipline: re-plumbing a working seam for tidiness is what triage already declined.
What would change the answer: a concrete requirement the adapter layer cannot serve — the clearest candidate being sso needing additionalFields, which has no adapter-layer equivalent. Nothing like that exists today. If one appears, this card is where to re-open it.
Disposition
⛔ The PM seat is not deciding this and is not adding it to the maintainer's decision inbox — the card's own words route it to triage, and the recommendation is the status quo, so an inbox card would cost a ruling to change nothing. Filed unassigned as a finding so triage can close it as declined-on-evidence, or promote it if it disagrees.
Refs: #8224 (the sweep, closed by PR #10072) · PR #10072 (the measurement lives in its body) · #10073 (the version-stamp attestation class this sweep exposed at scale) · #9714 (why a half must not be left to close with its parent)
Filed by the
domain:servicesPM seat (#6021) before PR #10072 merges, because that PR carriesFixes #8224and would otherwise close this question along with the card.That is not hypothetical bookkeeping: #9714 exists in this repo only because a half fell between two cards exactly this way — #8018 was closed as
moved, the destination said the remaining half was out of its scope, and the defect went untracked. This card is the cheap insurance against repeating it.What #8224 actually closed, and what it did not
#8224 described two things. PR #10072 does one:
@better-auth/ssoaccepts NOschemaoption") copied into many files, corrected everywhere it is wrong, preserved everywhere it is still right.ssoProvider→sys_sso_providermapping should move off the adapter layer (AUTH_MODEL_TO_PROTOCOL+ the mechanical camelCase→snake_case inobjectql-adapter.ts) and onto the plugin'sschema.ssoProvideroption.#8224 itself called that "a real architectural change" and said outright "whether it is worth doing at all is a triage call — the current bridge may be preferable for reasons beyond this plugin." Triage agreed and graded it zero-pull. The trigger that unparked #8224 (the pin moving to
^1.7.1) unlocked re-measurement, not that decision — so the PM seat dispatched only the sweep.New evidence the original card did not have
The #8224 dev measured this end to end while working the sweep (2026-08-19, against the installed packages, nothing quoted from cards):
sso({ schema: { ssoProvider: { modelName: 'sys_sso_provider', fields: { oidcConfig: 'oidc_config' } } } })yieldsmodelName = sys_sso_providerwithoidcConfig → oidc_config. So "can it be done" is settled: yes.better-auth@1.7.1,@better-auth/sso@1.7.1,@better-auth/scim@1.7.0-rc.1.SCIMOptionsstill declares noschema/modelName/fieldsmember at all — its eight top-level members arerequiredRole,staticProviders,mapGroupToRoles,linkExistingUsers,beforeSCIMTokenGenerated,afterSCIMTokenGenerated,canGenerateToken,storeSCIMToken.The options
A — do not rewire. Keep the adapter bridge; the corrected comments now record that the option exists and that not using it is a deliberate choice.
B — rewire sso onto
schema.ssoProvider(modelName + per-fieldfieldName), leaving scim on the adapter bridge becauseSCIMOptionsstill accepts nothing.C — rewire sso now, treating the remaining scim special case as debt to remove if/when
@better-auth/scimgains a schema option.Recommendation: A — and the measurement strengthens it rather than weakening it
schema.ssoProvider.fieldsis a hand-maintained per-field list: a field added upstream gets no mapping until a human notices, and the failure mode is a silently wrong column name. For a codebase written largely by agents, "covered by construction" beats "covered if someone remembers".objectql-adapter.test.tscoversssoProvider → sys_sso_provider;better-auth-schema-parity.test.ts's second describe block reproduces the adapter rule).What would change the answer: a concrete requirement the adapter layer cannot serve — the clearest candidate being sso needing
additionalFields, which has no adapter-layer equivalent. Nothing like that exists today. If one appears, this card is where to re-open it.Disposition
⛔ The PM seat is not deciding this and is not adding it to the maintainer's decision inbox — the card's own words route it to triage, and the recommendation is the status quo, so an inbox card would cost a ruling to change nothing. Filed unassigned as a
findingso triage can close it as declined-on-evidence, or promote it if it disagrees.Refs: #8224 (the sweep, closed by PR #10072) · PR #10072 (the measurement lives in its body) · #10073 (the version-stamp attestation class this sweep exposed at scale) · #9714 (why a half must not be left to close with its parent)