Skip to content

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

Merged
objectstack-fleet[bot] merged 12 commits into
mainfrom
claude/issue-25096-better-auth-1-7-12
Oct 21, 2026
Merged

objectstack-fleet[bot] merged 12 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)
Metadata-protocol: none

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.

Patch rounds 2 and 3: the Test Core 4/6 red (job 119207996576) and its fix

  • The failing test: packages/services/service-datasource/src/__tests__/sqlite-driver-fallback-file-open-failure.test.ts, "an addon that was never built (no code) steps down to wasm SQLite", Error: Cannot find module 'bindings'. It reproduced at 8c4bf6489b (Tests 1 failed | 929 passed (930)) and was green on origin/main ae58b26f9c (Tests 930 passed (930)).
  • Root cause: the fixture required bindings relative to better-sqlite3 13.0.3, which does not depend on it: its only dependency is node-addon-api, and it loads its addon with a plain require. On main, bindings@1.5.0 existed only under better-sqlite3@12.11.1, the optional peer copy auto-installed for better-auth 1.7.3, and the test reached it through pnpm's hidden hoist. 1.7.12 dropped that peer (see Lockfile: what moved), so the phantom left. The producer was never affected: resolveSqliteDriver classifies by error code, not message.
  • Fix (round 3, seat ruling A1, claim amendment 6316873193): realNotBuiltFailure() now produces better-sqlite3 13's own never-built error: a require() of build/Release/better_sqlite3.node, made from the installed package, against a module root that was never built, which Node answers MODULE_NOT_FOUND. No phantom dependency. The case is renamed "an addon that was never built (MODULE_NOT_FOUND) steps down to wasm SQLite" and asserts that code, still driving the producer with a real load error. The two sqlite-driver-fallback.ts docblock sentences that named bindings' "Could not locate the bindings file" now describe the 13.x error; no code moved. @objectstack/service-datasource rides the changeset as patch, text only (the docblock ships in dist/index.d.ts).
  • Ablation (on c428137808, scripts/ablation-replace.mjs WRAP, relative src import, no build between). A first leg at isSqliteFileOpenFailure was a no-op for this case: the classifier sees the driver's DATABASE_ERROR wrapper, not the load error's code. Re-anchored at the step-down decision (if (isSqliteFileOpenFailure(err)) throw err; also re-throwing on a MODULE_NOT_FOUND code or cause): Tests 1 failed | 8 passed (9), the renamed case red and the ERR_DLOPEN_FAILED case green. Restore: blob cd3fd64524ff equals HEAD and git diff HEAD is empty.
  • Final head 1a28ccb438 (origin/main ae58b26f9c merged, merge 9dcf97cae7): service-datasource Test Files 53 passed (53), Tests 930 passed (930); plugin-auth Test Files 180 passed (180), Tests 3422 passed | 10 skipped (3432), typecheck exit 0; check-vendor-version-stamps OK; check:vendor-export-contract PASS; check:override-consistency green; dispatch-gates.mjs --commands 108 derived, 108 run, 0 NOT-MEASURED, every exit 0; full dogfood battery Test Files 286 passed | 3 skipped (289), Tests 2387 passed | 18 skipped (2405).
  • Level (seat ruling A, 6317990662, under the maintainer's ruling A on changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105): this lift changes nothing a metadata author can write, so it declares Metadata-protocol: none and ships @objectstack/plugin-auth as a BREAKING-bannered patch, with its ADR-0087 disposition kept.
  • Patch round 4 (ruling A, 6317990662): ea86e48563 regrades @objectstack/plugin-auth from minor to patch under the maintainer's ruling A (a non-protocol breaking change ships patch and keeps its BREAKING banner and ADR-0087 disposition) and adds Metadata-protocol: none under the changeset's Clause-② line; spec, cli and service-datasource stay patch, and nothing outside .changeset/ moved. With origin/main's scripts against this body: check-changeset-no-major.mjs exit 0 (level axis: none at patch) and check-adr-0087-registration.mjs exit 0; check-vendor-version-stamps.mjs exit 0 on the head.

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 f2b6cf230b4cddd795b854b044bbcd95e37b6f55 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 f2b6cf230b4cddd795b854b044bbcd95e37b6f55 → 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>
…otocol none

The lift changes nothing a metadata author can write. Under the
maintainer's ruling A a non-protocol breaking change ships patch and
keeps its BREAKING banner and ADR-0087 disposition, so plugin-auth moves
from minor to patch and the changeset declares Metadata-protocol: none.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ea86e485634d247bbd29f39c0984ba11a0abfa66
Local-runs: none
Scope: packages/spec/src/identity/organization.zod.ts + pnpm-workspace.yaml, packages/plugins/plugin-auth/package.json, pnpm-lock.yaml, .changeset/25096-better-auth-1-7-12.md, packages/plugins/plugin-auth/src/auth-route-ledger.ts, packages/plugins/plugin-auth/src/session-read-failure-answer.test.ts, packages/plugins/plugin-auth/src/verification-reservation-single-use.test.ts, packages/services/service-datasource/src/tests/sqlite-driver-fallback-file-open-failure.test.ts, packages/services/service-datasource/src/sqlite-driver-fallback.ts, packages/cli/src/commands/migrate/account-issuer.ts

Remainder: the other 13 packages/plugins/plugin-auth/src files are comment-only attestation restamps, the lane seat's ACCEPT at TIER_DEFAULT. Inputs: card #25096 (body and all 13 comments), PR #25109 (body, 24-file list, net diff against the merge base ae58b26f9c), and the check-runs on this head. Read-only throughout: git show / git diff on the fetched branch, the REST API, and a line-based read of the two pnpm-lock.yaml blobs; nothing was built, tested or re-run.

① Derived judgments

  1. The 16 pins — right. pnpm-workspace.yaml: all 11 family overrides (better-auth, @better-auth/core, scim, oauth-provider, sso, drizzle-adapter, kysely-adapter, memory-adapter, mongo-adapter, prisma-adapter, telemetry) move 1.7.3 to 1.7.12, exact; every selector keeps its below-2.0.0 major bound and the header's selector rule is untouched (one header sentence loses a literal 1.7.2, prose only). packages/plugins/plugin-auth/package.json: the five declared ranges move to exact 1.7.12, no caret. No other manifest in the workspace names a better-auth package. The family's comment note states the delta measured in item 2 and matches it.
  2. Lockfile delta — right, and confined as the PR claims. Measured on the packages: keys of the two blobs: 40 removed, 13 added. Added = the 11 family packages at 1.7.12, better-call@1.4.4, @better-fetch/fetch@1.3.2, nothing else. Removed = the 11 at 1.7.3, better-call@1.4.0, @better-fetch/fetch@1.3.1, @better-auth/utils@0.4.2 (0.5.0 stays as the only copy; every 1.7.12 family package peers ^0.4.2 || ^0.5.0), better-sqlite3@12.11.1 and the 20 packages only its native build pulled (bindings, prebuild-install, node-abi, tar-fs, rc, ini, pump, simple-get and the rest), @cloudflare/workers-types and @opentelemetry/semantic-conventions (their only dependent on main was @better-auth/core@1.7.3), and two dedups, @noble/ciphers@2.3.0 (2.4.0 stays, the range service-settings declares) and tldts / tldts-core@7.4.9 (7.4.16 stays). Importers: plugin-auth's five specifiers, plus the apps/account / apps/setup vitest rows exchanging the two existing vite@8.3.4 peer variants (esbuild 0.28.1 / 0.28.2) — no version enters or leaves. Zero dangling edges at head: every snapshot and importer dependency resolves to a snapshot key. Nothing removed is still needed: no manifest, tsconfig or source file at head names any of the 22 removed package names (import, require and types forms grepped); bindings was the one hidden-hoist consumer and is the round-3 fix. Validate Package Dependencies, Build Core and every Lint and Type Check lane installed this lockfile frozen and concluded success.
  3. Route ledger — right. One row, POST /api/v1/auth/oauth2/client/rotate-keys, with its measured answer (403 access_denied; allowClientKeyRotation unset) in the comment. The vendor publishes the route through plugin-auth's catch-all, the ledger's mounted-surface equality is exactly the pin a lift should redden, and the ledger stays truthful (rule 4, Route & surface ownership).
  4. Two new pins — right. Both boot the real stack (ObjectQL over driver-sql, the manifest's objects, a real AuthManager), import workspace packages only (no escaping path reads), keep controls beside the failure legs, and pin the vendor property plugin-auth now depends on: a store failure answers 500 FAILED_TO_GET_SESSION and keeps the cookie (not the 1.7.3 401), and the ObjectQL adapter keeps the forced reservation id so a replay is refused. The PR body records one ablation leg per pin, each going red on the committed code.
  5. packages/spec hunk — prose only. Three lines inside the updatedAt property's TSDoc, appending the 1.7.12 re-measurement after the 1.7.3 reading, as ruled. The .describe() string, the Zod shape and every key are unchanged; the TSDoc sentence appears in no generated artifact on main (grepped), so no check:docs or authorable-surface artifact moves and none is in the diff. Metadata-protocol: none is true for spec.
  6. packages/cli description — right. The operator-facing sentence loses the version and carries no tracker number; no test pins it.
  7. service-datasource — right. realNotBuiltFailure() now requires the absent build/Release/better_sqlite3.node under the test's scratch directory, through a require seeded at the installed better-sqlite3 resolved from @objectstack/driver-sql (a declared dependency): Node answers MODULE_NOT_FOUND, the code the 13.x loader meets with no prebuild and no build, and the case asserts that code instead of toBeUndefined(). No phantom dependency remains; the unused dirname import is dropped. The sqlite-driver-fallback.ts change is comment-only — every changed line is a docblock line and no code moves — and that docblock ships in dist/index.d.ts, which is why the package rides the changeset.
  8. Attestations — right. The 13 remaining plugin-auth files change comments only: 19 lines restamped with the re-measurement, 12 kept as facts about the 1.7.3 release, and internal-field-readback.ts flags its identifier census as wrong in place instead of restamping it (the READBACK_FIELDS table is unchanged; the finding is reported for filing). The lint lane carrying check-vendor-version-stamps concluded success.

② Semver level

  • Changeset .changeset/25096-better-auth-1-7-12.md: four entries, all patch (plugin-auth, spec, cli, service-datasource; none is private). Line-initial Clause-②: no (narrowing) and Metadata-protocol: none sit in the changeset body and in the PR body. Under AGENTS.md item 3 as it reads on main (ruling A on changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105, landed by PR feat(gates): the changeset level follows the metadata protocol — Metadata-protocol: line, level axis, console refresh at patch, release notes by reader action (ruling A) #25114), none takes patch on every entry and a non-protocol breaking change keeps its BREAKING banner and ADR-0087 disposition at patch — which is what the file does: the banner is kept and its sentence states patch under ruling A; exactly one disposition marker, not-required (no-migration-prescription), a category in the gate's CATEGORIES; the head's Check Changeset run passed its ADR-0087 step and annotated that exemption. The level is right, and ruling 6317990662 is carried out to the letter: the round-4 commit touches three lines of the changeset and nothing else.
  • Clause-②: no (narrowing) — the right arm. The lift narrows: new refusals (PASSWORD_TOO_LONG, OIDC issuer_mismatch, an ID token without exp / iat), a status move (PKCE 401 to 400), in-flight flows invalidated. The adversarial reading, yes (narrowing) ("widens one surface and narrows another"), was taken on the two candidates. The DPoP htu re-binding swaps one accept set for another (the request URL for the configured public URL plus path); the two coincide wherever the request host equals the base URL and are equal-sized where they differ — a replacement, not a superset — so ruling 6315255713 Q2 (A) stands. The newly mounted rotate-keys path grows the public face by one route that refuses every call unconditionally (the option that would open it is unset), so it admits nothing and no on 「放宽接受集」 holds; the ledger row is the declaration of the face change. Under ruling A the arm moves the level neither way.
  • FROM / TO and operator notes against the measurements. Each FROM / TO maps to a measured reading in the PR body: identifier prefixes (probe), get-session 500 (pin and ablation), PKCE 400 (probe), PASSWORD_TOO_LONG with the lockout count (probe), both DPoP halves (probe), stale-cookie clearing (probe), rotate-keys 403 (suite), the Unchanged list (probes and suites). One FROM / TO rests on the vendor rather than on this stack: the OIDC issuer_mismatch refusal and the exp / iat requirement are not in the measurement table. both are confirmed in the published 1.7.12 dists read for this review: issuer_mismatch in @better-auth/sso (a 400 naming the discovered and configured issuers), and the exp / iat requirement in @better-auth/core's oauth2/verify-id-token.mjs (ID_TOKEN_REQUIRED_CLAIMS is iss, sub, aud, exp, iat, passed to jose as requiredClaims), so the sentence is true of 1.7.12 even though neither leg was driven on this stack. The operator notes carry both items the card's acceptance 5 prescribes (restart in-flight Magic Link / OAuth / SAML flows; set an OIDC issuer to the exact discovery value), plus client notes for PKCE and get-session. The dependency sentence (better-call 1.4.4, @better-auth/utils 0.5.0, @better-fetch/fetch 1.3.2) matches the lockfile. Nothing claims more than is true, and the card's own patch ask is met after the ruling-A regrade.
  • The one shared body means the spec, cli and service-datasource CHANGELOGs will carry the fix(plugin-auth)!: entry with its BREAKING banner; the body says in its own words that those three are text-only and not breaking. Acceptable under the seat's "ride as patch" ruling; a separate non-breaking changeset would have been cleaner, not required.

③ Boundary flags

  • Seat rulings carried out. 6315255713: Q1 A — the spec clause and the cli wording land exactly as proposed, both packages ride as patch; Q2 A — the arm stands and the changeset names both DPoP halves; Q3 A — no token-storage option is added (the auth-manager.ts diff is comment-only). 6316865808: A1 — fixture and docblock as ruled, the case renamed, MODULE_NOT_FOUND asserted, no code in sqlite-driver-fallback.ts moves. 6317990662: A — carried out as read in ②.
  • Dev open_questions: round 1's three, round 2's one and round 3's one are each answered by a seat ruling on the card; the patch-round-1 and round-4 reports carry none. Nothing is open.
  • Out-of-scope findings the seat took on (6315255713): the SSO domain-verification TXT-record defect and the Magic Link lifetime text are to be filed as cards; both are pre-existing, neither is changed here, and the filing is the seat's act outside this record — owed, not blocking.
  • Escalated to the cli lane, not blocking. packages/cli/src/commands/init.ts SCAFFOLD_ALLOWED_PEER_VERSIONS carries five entries whose rationale the lift falsifies: the four @better-auth/utils entries (for core, oauth-provider, scim and sso, each mapped to 0.5.0) say the four packages peer an EXACT 0.4.2 and retire "when the four packages accept 0.5.0 upstream" — at 1.7.12 all four peer ^0.4.2 || ^0.5.0 (lockfile), so the retirement condition is met; the better-auth to better-sqlite3 entry ('13') suppresses a peer better-auth@1.7.12 no longer declares; and the docblock reads better-call as "the exact string 1.4.0" at "the pin today" (now 1.4.4). The stamp gate does not read these shapes (it is green), init.test.ts pins the entries' presence, and allowedVersions only suppresses a report, so the scaffold resolves identically — a stale ledger, not a defect in this PR. A card for the cli lane, with the test pins to flip alongside.
  • PR body: the "What changed" changeset bullet, fork 3 and the first Tests section still read minor and a stamp-gate exit 1; the appended patch rounds supersede them. A tidy is the seat's option.
  • Check-runs on this head: every check-run concluded success or a skip on the check-expected-skips.mjs roster (six rostered skips: the opt-in harness and tarball smoke, Console Pin Gate, Temporal Conformance, Dogfood Verify CLI and the matrix-templated Dogfood shard); the required contexts Lint & Repo Gates, TypeScript Type Check, Build Core, Test Core and Governed Surface Queue Guard are among them. Check Changeset's first attempt on this head failed in its Install dependencies step before any gate step ran — node-gyp's fetch of the nodejs.org checksum file reset during the better-sqlite3 install — while its level-axis step (no (narrowing) with Metadata-protocol: none at patch) and its ADR-0087 step both passed in that same attempt; the same frozen install succeeded in every sibling job, and main moved no manifest or lockfile since the merge base. The seat's one re-run of that job (attempt 2, check-run 119281479661) concluded success with every judging step run — install, changeset count, the changeset requirement, the empty-frontmatter refusal, the ADR-0087 disposition and the no-major / level-axis guard — and that latest attempt is the gate verdict read here.
  • Disclosure: nothing in the diff or the body exposes a deployment beyond what the card already states; nothing is held out.

Implemented-by: claude/issue-25096-better-auth-1-7-12
Reviewed-by: session_011TdZ8GjixNZFLz1xJD9zzK

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:services seat 1 · 2026-10-21T14:44Z

os-justin · session_011TdZ8GjixNZFLz1xJD9zzK (seat post #6021). Reviewed against GitHub and origin/main edcc8ceae6, not against the reports (os-dev reports 6315144841, 6316496809, the round-2 and round-3 reports, and 6317940845's successor on #25096).

Disclosure read first (#9387, 5323310017).

  • The advisories and upstream PRs named are the card's own, and they are public.
  • The seat removed, from the round-1 report and from this PR's body, a pointer to a commit no longer on the branch.
  • No reading on this PR exposes a deployment beyond what the card states (the review's ③).

Head under review:

ea86e485634d247bbd29f39c0984ba11a0abfa66
  1. Shape.
    • Draft, base main, first line Fixes #25096. Title fix(plugin-auth)!:.
    • Line-start Clause-②: no (narrowing) and Metadata-protocol: none sit in the body and in the changeset.
    • 24 files, none on a governed surface (governedPathsIn answers []). All of them sit within the claim and its two amendments (6315268175, 6316873193), and the two out-of-lane lines are declared to their lanes (6315228904, 6315240630), with no objection.
    • The trial merge onto origin/main is clean, and main has not touched this PR's files since the merge base.
  2. Review of record. The face is schema (the packages/spec TSDoc hunk) at CONTRACT_REVIEW_TIER. The isolated contract review 6318796768 reads PASS on this same head (Served-tier: CONTRACT_REVIEW_TIER, Local-runs: none). Since it, the seat changed nothing.
  3. The lift, read in the diff.
    • 16 exact 1.7.12 pins, and the selector rule is unchanged.
    • The lockfile delta is confined to the family and what 1.7.12 declares; the review measured it independently, and nothing removed is still consumed.
    • One truthful route-ledger row (rotate-keys, refused).
    • Two real-stack pins with ablation.
    • Comment-only attestation restamps, plus one flagged in place.
    • A TSDoc-only spec hunk and a version-free cli sentence.
    • The service-datasource fixture now drives the producer with better-sqlite3 13's real never-built error, with no phantom dependency.
  4. Gates.
    • At ACCEPT, every check-run on this head is success or an expected skip (38 and 6).
    • Check Changeset's first attempt died at "Install dependencies" on a network reset, before any gate step. The seat's one re-run concluded success with every judging step run.
    • Locally, on the final code head 1a28ccb438: 108 of 108 derived families exited 0, and check-vendor-version-stamps, check:vendor-export-contract and check:override-consistency all exited 0. The changeset gates were re-read on this head with main's scripts.
    • dogfood_battery (docs(os-dev, pm-dispatch): a breaking PR runs the full dogfood battery on its final head; the review checklist reads the field before arming #24917): 1a28ccb438, VERDICT command-exit 0, Test Files 286 passed | 3 skipped (289), Tests 2387 passed | 18 skipped (2405). This head differs from 1a28ccb438 only in .changeset/, so the battery stands.
  5. Classification. A security dependency lift at the maintainer's word on the card. It ships as a BREAKING-bannered patch with ADR-0087 not-required (no-migration-prescription), under the maintainer's ruling A on changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105, carried out by the seat's ruling 6317990662.
  6. Seat rulings carried out: 6315255713 (Q1–Q3 A), 6316865808 (A1) and 6317990662 (A). No dev question is open.
  7. Accepted deviations.
    • A probe file reached the branch for about a minute before the PR opened, and was removed by a lease-guarded force push on the dev's own branch (AGENTS.md §3).
    • One vendor-dist ablation broke the store hard link first and restored the file under a trap.
    • The trailers are model-free.
  8. Out-of-scope disposition.
  9. Landing clause. Not governed, under the human-merge threshold. The next step is ready + auto-merge through the relay. A push resets this reading.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 21, 2026 14:46
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