Skip to content

fix(plugin-auth)!: lift the better-auth family from 1.7.3 to 1.7.12 - #25109

Draft
objectstack-fleet[bot] wants to merge 11 commits into
mainfrom
claude/issue-25096-better-auth-1-7-12
Draft

objectstack-fleet[bot] wants to merge 11 commits into
mainfrom
claude/issue-25096-better-auth-1-7-12

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #25096

Clause-②: no (narrowing)

What changed

  • The lift, one commit (75aaf74a1d). The 11 better-auth family overrides in pnpm-workspace.yaml and the 5 exact ranges in packages/plugins/plugin-auth/package.json move from 1.7.3 to 1.7.12, still exact. The selectors keep their bound at the 2.0.0 major boundary; the header's selector rule is untouched. The lockfile is regenerated by pnpm install (under the verify lock), never by hand.
  • pnpm-workspace.yaml family note states the new target, why, and what moved with it.
  • auth-route-ledger.ts: one new row, POST /api/v1/auth/oauth2/client/rotate-keys. @better-auth/oauth-provider 1.7.12 registers it, so the catch-all publishes it. It is refused 403 access_denied because auth-manager.ts does not set allowClientKeyRotation (measured below). This is the one test the lift turned red (auth-route-ledger.conformance.test.ts, the mounted-surface equality).
  • Two new pin files for vendor changes plugin-auth callers now depend on, each with one ablation leg (below):
    • session-read-failure-answer.test.ts: a session lookup the store cannot answer is a failure, never a signed-out caller.
    • verification-reservation-single-use.test.ts: the ObjectQL adapter keeps the primary key better-auth forces on a single-use reservation, so a replay is refused.
  • Version attestations: the 32 lines under packages/plugins/plugin-auth/src that named 1.7.3 were each re-measured on 1.7.12 (table below). 19 restamped with the date and the card, 12 left as written (they are facts about the 1.7.3 release and still hold), and 1 not restamped but flagged and reported (its claim is false).
  • .changeset/25096-better-auth-1-7-12.md: @objectstack/plugin-auth minor, BREAKING, with the Clause-② line, the ADR-0087 disposition not-required (no-migration-prescription), FROM / TO per measured change, Unchanged, and the operator notes.

No behaviour of plugin-auth changes beyond the vendor's. No other dependency was moved on purpose; packages/spec/** and packages/objectql/** are untouched.

Lockfile: what moved

Resolved package set, old vs new (packages: keys of pnpm-lock.yaml):

  • The family: all eleven 1.7.3 to 1.7.12.
  • Declared by 1.7.12 itself: better-call 1.4.0 to 1.4.4, @better-auth/utils 0.4.2 removed (0.5.0 was already resolved and is now the only copy; 1.7.12 peers ^0.4.2 || ^0.5.0), @better-fetch/fetch 1.3.1 to 1.3.2.
  • Gone because 1.7.12 no longer asks for them: the auto-installed better-sqlite3@12.11.1 copy (better-auth dropped its optional better-sqlite3 ^12 peer) and the 20 packages only its native build used (prebuild-install, node-abi, tar-fs, bindings and the rest); @cloudflare/workers-types (core dropped the peer); @opentelemetry/semantic-conventions (core dropped the dependency); @noble/ciphers@2.3.0 and tldts/tldts-core@7.4.9 (deduplicated onto the 2.4.0 and 7.4.16 copies already in the tree).
  • Nothing added outside the family. One cosmetic swap: the vitest snapshot lines of the packages/apps/account and packages/apps/setup importers exchanged which of the two existing vite@8.3.4 peer variants (esbuild@0.28.1 / 0.28.2) they name. No version entered or left.

Measured on 1.7.12, item by item (the card's list)

Real stack throughout: ObjectQL over @objectstack/driver-sql (better-sqlite3 :memory:), the objects from plugin-auth's manifest, a real AuthManager, requests through AuthManager.handleRequest. Where a reading comes from a one-shot probe it is marked probe: a throwaway vitest file in the package, run once at 347772618d, not committed.

Card item Reading on 1.7.12 How
Export surface check:vendor-export-contract PASS, 3 edges (@better-auth/core/context, @better-auth/core/utils/ip, better-auth/adapters); check:override-consistency PASS; plugin-auth build (DTS) and typecheck green gates
1.7.7 better-auth/better-auth#11494 identifier prefixes vs implicit-account-linking.ts A Magic Link row is stored as magic-link:TOKEN with value {"type":"magic-link",...}; an OAuth state row as auth-state:STATE. The unlink tombstone is account-unlinked:USER:PROVIDER; its exact read and its user-prefix read each return the tombstone alone, and the user sweep left both vendor rows in place. No collision, and plugin-auth reads neither prefix. implicit-account-linking.test.ts green probe + suite
1.7.8 better-auth/better-auth#11391 customSession now throws Over HTTP a store failure on GET /get-session answers 500 FAILED_TO_GET_SESSION and keeps the session cookie; in process api.getSession rejects. Before (vendor ablation restoring the 1.7.7 catch): 401 UNAUTHENTICATED "Sign in first" and null. Right answer: yes, see below new pin + ablation
better-auth/better-auth#11540 / better-auth/better-auth#10591 / better-auth/better-auth#11718 cookies and session storage, with secondaryStorage Fork, below. Cookies: HttpOnly; SameSite=Lax; Path=/, no Secure on plain HTTP, the same with or without a host secondaryStorage, so the RFC 6265bis forcing (SameSite=None and Partitioned imply Secure, in better-call 1.4.4) never applies to plugin-auth's cookies. better-auth/better-auth#10591: a stale session cookie on /get-session is now cleared (Max-Age=0 on session_token, session_data, dont_remember) alongside the same 401. better-auth/better-auth#11718: not armed here probe
1.7.7 better-auth/better-auth#11375 active organization refresh vs the session.create.before stamp Client-only: it widens the organization client's $activeOrgSignal matcher to the session signals. No server path changed. The stamp's suites (first-session-membership-ordering, session-positions-security-axis, session-grants-resolved-once, principal-session-mint) green. The client that would pick it up is objectui's @object-ui/auth, which declares better-auth ^1.7.2 on its own lockfile; nothing here moves it dist + suite
oauth-provider 1.7.9 better-auth/better-auth#10799 DPoP htu Authorization-code exchange with a DPoP proof: htu = the public base URL is accepted (token_type: DPoP) even when the request reaches the server under another host (127.0.0.1:3000); htu = that request URL is refused 400 invalid_dpop_proof "DPoP proof htu does not match the request URL". 1.7.3 compared against the request URL. Widening for host-rewriting proxies, in open questions probe
oauth-provider 1.7.10 better-auth/better-auth#11724 PKCE 401 to 400 Wrong code_verifier: 400 invalid_grant "code verification failed"; control with the right verifier: 200. No pin of the old 401 exists (repo-wide grep for PKCE / code_verifier in tests: the success-path uses in auth-manager.mcp-oauth-resource.test.ts and auth-manager.oauth-consent-organization.test.ts, no status pin on a mismatch), so none was updated probe + grep
1.7.8 better-auth/better-auth#11625 replay protection; which ids the adapter generates plugin-auth sets no generateId: user, session, account and verification rows carry better-auth's own 32-character base62 ids, kept as given. A reservation is stored under the forced base64url SHA-256 id, and its replay is refused. The uuid path the fix is about is not armed here new pin + ablation + probe
Version attestations 32 lines, table below dist re-reads, suites, probes
GHSA-965c-763c-88jm precondition set With Magic Link on (plain token storage, the default), a social provider configured and database-backed OAuth state, a value minted by the OAuth-state flow presented at /magic-link/verify answers 302 to ?error=INVALID_TOKEN, sets no session cookie, and leaves the state row intact. Control: the real link answers 302 to the callback with a session cookie. The precondition set no longer yields the exposure probe

Is the new customSession answer right? Yes. A store outage is not a signed-out caller. Under 1.7.3 the browser was told "Sign in first" while its session still existed; now it is told the read failed and keeps its cookie. That follows AGENTS.md "Absence must be loud" and the degradation rule (a failure handed to the caller is not a silent degradation). The in-process callers that want "anonymous" on a failed read keep it on purpose: resolveRequestExecutionContext (core) and resolveSessionData (runtime) catch the throw themselves. One in-process path gets stricter: core's auth-gate re-read (decideAuthGate) already maps a throw to AuthzStoreUnavailableError; under 1.7.3 the swallowed failure reached it as null and the gate read as not held. The raw /admin/* routes that call api.getSession answer through their own catch.

Also measured, not on the card's list (both in the changeset): an over-long password at /sign-in/email now answers 400 PASSWORD_TOO_LONG before any lookup, and still counts as a failed attempt for the lockout (failed_login_count 0 to 1, then 2 after a wrong short password); and the new rotate-keys route answers 403 access_denied "Client key rotation is disabled".

Forks from the card

  1. "plugin-auth sets session storeInDatabase whenever a host supplies secondaryStorage." Measured: it sets verification.storeInDatabase: true, not session.storeSessionInDatabase. With a host secondaryStorage the session of record lives in the cache only (0 sys_session rows; the cache holds the token and active-sessions-USER), and /list-sessions answers from that list. fix(session): fall back to database for bulk session lookups better-auth/better-auth#11718's database fallback (databaseSessionFallbackEnabled) requires storeSessionInDatabase === true, so it is never armed on this platform.
  2. "Any test that pins the old [PKCE] status needs updating." None exists; nothing to update.
  3. Changeset level. The card asks patch; this ships minor, BREAKING, per the claim's Clause-②: no (narrowing).

Attestations: the 32 lines

R = restamped to 1.7.12 with the date and #25096 after the re-measure; K = kept, a fact about the 1.7.3 release that still holds on 1.7.12; F = flagged, not restamped.

Line at base Claim 1.7.12
account-identity-preflight.ts:11 1.7.3 removed the issuer-scoped account identity account key still (providerId, accountId), no issuer in core's account table K
account-invitation-marker.ts:14 requestPasswordReset answers { status, message }; only sendResetPassword gets the link password.mjs request leg unchanged R
account-invitation-marker.ts:68 runInBackgroundOrAwait catches the send; answer stays { status: true } create-context.mjs unchanged there R
admin-import-users-invite-delivery-failure.test.ts:9 same same; suite green R
admin-user-endpoints.ts:69 providerId + accountId is the whole identity since 1.7.3 holds K
auth-email-locale.test.ts:871 /sign-in/magic-link does no user lookup; /magic-link/verify creates a user unless disableSignUp holds (1.7.12 adds a form-CSRF middleware and a record-type check, no lookup) R
auth-email-locale.test.ts:1006 body schema has no case transform; findUserByEmail lowercases holds R
auth-manager.test.ts:318 1.7.3 rolled the issuer key back holds K
auth-manager.ts:1802 appName read once; two otpauth readers; no cookie name derives from it same three sites R
auth-manager.ts:2062 the reset token lives resetPasswordTokenExpiresIn seconds, or 3600 when that is falsy password.mjs:73 R
auth-manager.ts:2084 a server-side reset reaches the callback with no request to-auth-endpoints.mjs byte-identical; sendResetPassword(..., ctx.request) unchanged R (extended)
auth-manager.ts:4727 no database builds a store keyed by schema KEY, read by modelName probe: Model sys_user not found R
auth-manager.ts:9701 /sign-in/email asks findUserByEmail(email.toLowerCase()) holds, now after the new length check R
auth-schema-config.ts:104 1.7.3 removed the issuer model holds K
auth-schema-config.ts:956 scim exposes no schema option; six SCIMOptions members holds R
auth-schema-config.ts:961 the re-read history 1.7.12 appended R (extended)
implicit-account-linking.ts:45 the handleOAuthUserInfo gate, then assertValidUserInfo with link-account holds; the explicit link now runs through linkOAuthAccount, same check, noted R
in-process-redispatch.ts:30 disableRefresh is read from the query on both re-dispatches in-process-session-renewal.pin.test.ts green R
internal-field-readback.ts:197 the read-back census, including "identifier: no flow reads it back off a row" false, on 1.7.3 and 1.7.12 alike: @better-auth/sso's /sso/verify-domain reads it back (reported below) F
invite-role-not-found-code.ts:16 invite throws an uncoded ROLE_NOT_FOUND; update-member-role a coded one crud-invites.mjs byte-identical; crud-members.mjs still coded R
list-user-invitations-verification.ts:11 listUserInvitations never asks the verification rule crud-invites.mjs byte-identical R
objectql-adapter.test.ts:95 1.7.3 removed the issuer column holds K
objectql-adapter.ts:1051 identity is (provider_id, account_id) since 1.7.3 holds K
organization-delete.ts:10 what POST /organization/delete does and leaves crud-org.mjs and the organization adapter byte-identical R
session-envelope-completion.ts:21 createSession awaits the write; line references holds; lines now 259-332 and 333-370 R
two-factor-reenrollment-verified-reset.test.ts:27, 29, 340, 345, 354, 461 (6 lines) 1.7.3 added the TOTP_ALREADY_ENABLED gate plugins/two-factor/index.mjs byte-identical 1.7.3 to 1.7.12 K
two-factor-totp-issuer.test.ts:11 what better-auth does with appName same three sites R

node scripts/check-vendor-version-stamps.mjs is green on every plugin-auth line. It stays red on two sites outside this card's file surface, which the lift makes false and which I did not touch (claim: stop on breach):

  • packages/spec/src/identity/organization.zod.ts:95: "Both halves measured against the installed better-auth 1.7.3, so fallback A applies". packages/spec/** is a hard stop, and the line is TSDoc on a schema property. Re-measured on 1.7.12: the organization plugin's schema.mjs is byte-identical and the adapter factory's transformOutput still iterates the declared fields only, so the claim holds and a restamp is honest.
  • packages/cli/src/commands/migrate/account-issuer.ts:71: the command description "...is ONE account under the key better-auth 1.7.3 restored". It is operator-facing text. The claim holds on 1.7.12.

Both need the seat's word on the file surface before this lands; the report proposes the text.

Ablations

Both on committed code, each leg proven on disk, each restore proven by hash.

  1. Reservation pin (aa3112430a). scripts/ablation-replace.mjs (WRAP) replaced the adapter's dataEngine.insert(objectName, row) with an insert that drops id (an adapter that mints its own key): anchor 1 to 0, replacement 0 to 1, blob 5ed0db896088 to cbd1309c37a7. The test reaches the adapter through relative src imports, so no build sits between. Result: Tests 2 failed | 1 passed (3): "the engine kept the forced primary key: expected '3hj6lgd1y2K1GdHC' to be 'RhMSgVct...'" and "expected true to be false" (the replay won); the control stayed green. Restore: blob 5ed0db896088 equals HEAD, git diff HEAD empty, git status --porcelain empty.
  2. Session-read-failure pin (aa3112430a). The behaviour is the vendor's, so the mutation is in the installed better-auth 1.7.12 dist/plugins/custom-session/index.mjs: the 1.7.7 .catch(...) put back around the inner session read. That file is a hard link into the shared pnpm store, so the link was broken first (inode 4096254 to 1565215, 1 link) and the store copy was never written. On disk: anchor 1 to 0, injected 0 to 1, sha256 b2e60e53... to 25e2b179.... Result: Tests 2 failed | 2 passed (4): over HTTP "expected 401 to be 500" with body UNAUTHENTICATED "Sign in first"; in process "promise resolved null instead of rejecting"; both controls green. Restore under a trap: sha256 back to b2e60e53..., and the store inode still hashes b2e60e53....

Tests

  • plugin-auth, full suite on the lift alone (75aaf74a1d): Test Files 1 failed | 176 passed (177), Tests 1 failed | 3375 passed | 10 skipped. The one red was the route ledger (rotate-keys), fixed by 9ffd7eb619.
  • New pins: session-read-failure-answer.test.ts Tests 4 passed (4); verification-reservation-single-use.test.ts Tests 3 passed (3).
  • Final head 89b9affe00 (this branch with origin/main 2e242bb0a9 merged in; every reading below was taken after that merge):
    • plugin-auth vitest run: Test Files 179 passed (179), Tests 3383 passed | 10 skipped (3393).
    • plugin-auth typecheck: exit 0, with check:test-typecheck: OK and the debt ledger unchanged (88 errors, 23 pinned signatures).
    • pnpm check:vendor-export-contract: VERDICT: PASS, 3 edges. pnpm check:override-consistency: all 9 published-manifest declarations resolve to their override targets.
    • node scripts/check-vendor-version-stamps.mjs: exit 1, red only on the two out-of-surface sites above.
    • node scripts/pm/dispatch-gates.mjs --commands, every family run and reconciled with --ran: 86 derived, 86 run, 0 NOT-MEASURED, every exit 0. On the first run, at becfb99a89, check:dual-build-cjs-loads exited 3 (no full build yet); after the dogfood closure build it was measured green.
    • Full dogfood battery (pnpm --filter @objectstack/dogfood test, no shard, no path, under the lock, after pnpm --filter '@objectstack/dogfood^...' build): Test Files 286 passed | 3 skipped (289), Tests 2387 passed | 18 skipped (2405), lock verdict command-exit 0. The auth suites run in it.
  • CI check-runs: CI owns them; they had not finished when this PR was opened.

Acceptance notes

  • Reported, out of scope: SSO domain verification cannot succeed with the record plugin-auth tells the operator to publish. POST /api/v1/auth/admin/sso/request-domain-verification answers dnsRecordValue as _better-auth-token-PROVIDER=TOKEN. @better-auth/sso's /sso/verify-domain accepts a TXT record equal to IDENTIFIER=VALUE, building IDENTIFIER from the verification row it read, or equal to the bare VALUE. On this store sys_verification.identifier is internal and the read hands better-auth undefined there, so it expects undefined=TOKEN. Probe, vendor door, DNS answered by a double: the record as instructed answers 502 DOMAIN_VERIFICATION_FAILED; the bare token answers 204. Same vendor code on 1.7.3. Not changed here. The report asks the seat to file it.
  • Reported, out of scope: the Magic Link mail states a lifetime the link does not have. sendMagicLink passes expiresInMinutes: 10 to auth.magic_link, whose copy says "This link expires in 10 minutes"; magicLink() is mounted without expiresIn, so better-auth stores the row with a 300-second lifetime (probe: lifetimeSec: 300). Unchanged by the lift.
  • Pre-existing, loud, unchanged: a host secondaryStorage together with the embedded OAuth provider (on by default with the MCP surface) makes the auth instance throw at build ("OAuth Provider requires session.storeSessionInDatabase: true when using secondaryStorage"), on 1.7.3 and 1.7.12 alike. The existing secondaryStorage suites set oidcProvider: false.
  • Not measured: fix(password): revoke sessions and write the new password atomically better-auth/better-auth#11626 / fix(password): consume the reset token inside the reset transaction better-auth/better-auth#11646 say a password reset consumes its token, revokes sessions and writes the password in one transaction. plugin-auth's adapter opens a real transaction only inside a SCIM request and runs other flows sequentially (objectql-adapter.ts, the SCIM: 停在 @better-auth/scim rc.1,等正式版再整体迁移 —— rc.2 换掉了整套模型 #3653 scoping note), so on this platform those three writes are sequential, in the vendor's order: consume, revoke, write. A refusal before the write (bad token, password length, unknown user) no longer spends the token.
  • Accidental push, repaired: the throwaway probe file rode one commit for about a minute. I removed it by amending and a --force-with-lease push pinned to that commit, with all five AGENTS.md §3 conditions holding (a claude/issue-* branch this worktree created, one author, no PR yet, the lease on the sha I last pushed). It holds no secret.

Patch round 1 (seat rulings 6315255713, claim amendment 6315268175)

  • packages/spec/src/identity/organization.zod.ts: the updatedAt TSDoc keeps its 1.7.3 reading and appends the re-measurement: "(re-measured on 1.7.12 for deps(plugin-auth): lift the better-auth family from 1.7.3 to 1.7.12 — security fixes published after the 2026-10-01 deferral #25096: the organization plugin schema is byte-identical and transformOutput still projects declared fields only)". Nothing else in packages/spec moves. pnpm --filter @objectstack/spec check:generated after a fresh spec build: all 14 generated artifacts up to date.
  • packages/cli/src/commands/migrate/account-issuer.ts: the description now reads "...is ONE account under the (provider_id, account_id) key better-auth restored...", with no version, so a later lift cannot falsify it. No test pins the sentence. cli unit layer: Test Files 321 passed (321), Tests 4746 passed (4746); cli typecheck exit 0.
  • Changeset: @objectstack/spec and @objectstack/cli ride as patch, text only and not breaking. plugin-auth stays the BREAKING minor, and the DPoP htu FROM / TO still names both halves (ruling Q2).
  • With this round, the two out-of-surface stamp reds named under Attestations are resolved: node scripts/check-vendor-version-stamps.mjs exits 0 (check:vendor-version-stamps: OK — 10187 file(s) scanned).
  • origin/main was merged at ba244f1b48 before the edits (merge 28476848b5). Final head: 8c4bf6489b.
  • Final head readings: pnpm check:vendor-export-contract PASS (3 edges); pnpm check:override-consistency (9 declarations resolve to their targets); plugin-auth Test Files 180 passed (180), Tests 3422 passed | 10 skipped (3432), typecheck exit 0; dispatch-gates.mjs --commands 108 derived, 108 run, 0 NOT-MEASURED, every exit 0 (--ran); full dogfood battery (no shard, no path) Test Files 286 passed | 3 skipped (289), Tests 2387 passed | 18 skipped (2405), lock verdict command-exit 0.

Generated by Claude Code

All sixteen pins move together, exact: the eleven family overrides in
pnpm-workspace.yaml and the five exact ranges plugin-auth declares.
The lockfile is regenerated by pnpm install.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
… publishes

@better-auth/oauth-provider 1.7.12 registers POST /oauth2/client/rotate-keys,
so the catch-all now publishes it. The handler refuses unless the plugin
is given allowClientKeyRotation, which auth-manager does not set.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
… reservations on 1.7.12

Two vendor changes the lift brings, measured on a real ObjectQL + SQL stack:
customSession lets a failed session lookup through as 500
FAILED_TO_GET_SESSION instead of a signed-out null, and the adapter keeps
the forced reservation id so a replayed reservation is refused.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
… 1.7.12

Every live reading that named 1.7.3 was re-measured against the installed
1.7.12 dist or by the test that pins it, and restamped with the date and
the card. Facts about the 1.7.3 release itself stay as written. The
internal-field-readback census is scoped to its 1.7.3 trace and flagged
instead of restamped: sso's verify-domain reads the verification
identifier back off the row, which the census says no flow does.

The pnpm-workspace.yaml family note states the new target and what the
lift moved.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
Breaking narrowing: Clause-② no (narrowing), ADR-0087 disposition
not-required (no-migration-prescription), with FROM / TO for each
measured vendor change and the operator notes.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling labels Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/plugin-auth, @objectstack/service-datasource, @objectstack/spec, touching 9 documentable anchor(s). ⚠️ 10 changed file(s) yielded no anchor (packages/plugins/plugin-auth/package.json, packages/plugins/plugin-auth/src/account-invitation-marker.ts, packages/plugins/plugin-auth/src/auth-schema-config.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/cli.mdx (via os migrate account-issuer (command, read off packages/cli/src/commands/migrate/account-issuer.ts))
  • content/docs/kernel/contracts/auth-service.mdx (via AuthManager (symbol, a top-level class))
  • content/docs/kernel/services-checklist.mdx (via AuthManager (symbol, a top-level class))
  • content/docs/permissions/authentication.mdx (via AuthManager (symbol, a top-level class))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-5.mdx (via os migrate account-issuer (command, read off packages/cli/src/commands/migrate/account-issuer.ts))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 10 changed file(s) yielded no anchor (packages/plugins/plugin-auth/package.json, packages/plugins/plugin-auth/src/account-invitation-marker.ts, packages/plugins/plugin-auth/src/auth-schema-config.ts, …) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 153 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 ae58b26f9c099ca44ac479d61df2727dea711d04 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 69b68c80e27f02f8944643d502810fb218cc7f7a — the merge of head 1a28ccb4384cb103a9dbe0b585a241e1e2f6929e into base ae58b26f9c099ca44ac479d61df2727dea711d04, 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 69b68c80e27f02f8944643d502810fb218cc7f7a && git checkout 69b68c80e27f02f8944643d502810fb218cc7f7a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ae58b26f9c099ca44ac479d61df2727dea711d04 1a28ccb4384cb103a9dbe0b585a241e1e2f6929e && git checkout -B drift-repro ae58b26f9c099ca44ac479d61df2727dea711d04 && git merge --no-ff 1a28ccb4384cb103a9dbe0b585a241e1e2f6929e

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

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

… falsified

The organization schema's updatedAt TSDoc keeps its 1.7.3 reading and
appends the 1.7.12 re-measurement (the organization plugin schema is
byte-identical; transformOutput still projects declared fields only).
The os migrate account-issuer description names the restored key and
no version, so no later lift can falsify it. spec and cli ride the
existing changeset as patch, text only.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
…rror, not a phantom bindings

The never-built fixture required the bindings package relative to
better-sqlite3 13, which does not depend on it; it resolved only through
pnpm's hidden hoist of the copy better-sqlite3 12 brought in as
better-auth 1.7.3's optional peer, and the 1.7.12 lift removed that copy.
better-sqlite3 13 loads its addon with a plain require of
build/Release/better_sqlite3.node, so the fixture now makes that require
from the installed package against a module root that was never built,
and the case asserts MODULE_NOT_FOUND. The two docblock sentences that
named bindings' message now describe the 13.x error. No code moves.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>
Its resolveSqliteDriver docblock ships in dist/index.d.ts, so the
corrected never-built sentence publishes.

Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK
Co-authored-by: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
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 size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

deps(plugin-auth): lift the better-auth family from 1.7.3 to 1.7.12 — security fixes published after the 2026-10-01 deferral

2 participants