Skip to content

refactor(plugin-auth): move AuthManager's database hooks and identity-source stamp into auth-manager-database-hooks.ts (#25224 cut 4) - #25383

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-lg3wrj
Oct 23, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-lg3wrj

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Part of #25224
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160, 6353073966

The basis for the Clause-②: no line: this is a move. No accept set, export, route, refusal or answer changes. The .d.ts bar is ruling 6348891160 item 1 (on #25222, covering this card). @objectstack/plugin-auth's published dist/index.d.ts and dist/index.d.mts are raw byte-identical to base (0 diff lines, so the union-order normalisation has nothing to do), with the ruling's positive control. Its entry exports the same 155 names under ESM and CJS (measured below).

Cut 4 of the auth-manager.ts split: the better-auth database hooks, and the identity-source stamp they run. Two AuthManager method bodies, 406 lines, move unedited (re-indented one level) into one new module, packages/plugins/plugin-auth/src/auth-manager-database-hooks.ts. #25224 stays open for the later cuts.

This follows the amended claim 6357765071, which amends 6356007354 on the file cut 3 left (#25362, landed note 6357231692). The shape is cuts 1 to 3's, which is #23050's.

Branch: claude/project-thread-lg3wrj, the harness-pinned branch for this session, restarted from origin/main fb63d396b1 after #25362 merged. It names no issue, so the card relation is this body's first line.

auth-manager.ts goes from 9,592 to 9,212 lines. The cut is +538 / −418 over 5 files: 956 changed lines, under the 3,000-line line. Two of the files are the regenerated tenant-audit census, and one is the new module's ADR anchor entry. It is not a governed surface (check-governed-merges.mjs --branch HEAD: 0 of the 5 paths hit the register).

Why this seam

  • stampIdentitySource (cloud ADR-0024 D4, the sys_user.source provenance stamp) is the ninth method of ruling 6351993113. Cut 3 left it for its own area. Its only caller is account.create.after, inside composeDatabaseHooks.
  • composeDatabaseHooks is where better-auth's database hooks are assembled around a host's own databaseHooks. Each hook calls into another section of the class (membership settlement, self-registration grants, owner-verified staging, the IdP locale), but the composition itself is one body with one job.
  • So the two move together, and the class's own sections keep their bodies. The membership section's methods that the hooks call (settleMembership, settleSelfRegistrationGrant, takeOwnerVerifiedStamp, isCreatedInThisRequest) stay for that area's cut.

What moved

method base body (signature to brace) lines members it reads
composeDatabaseHooks :8431–:8792 362 audienceLogError, config, idpLocale, isCreatedInThisRequest, linkingContext, settleMembership, settleSelfRegistrationGrant, stampIdentitySource, takeOwnerVerifiedStamp
stampIdentitySource :8865–:8910 46 getDataEngine, logDurabilityDegradation

Lines are base fb63d396b1. The members come from a walk of every this.X in each body with the TypeScript compiler API. Neither body has a bare this, this[...], arguments or super, a this inside a function or a nested class, or an assignment to a this member. Neither reads a static (AuthManager.X).

composeDatabaseHooks returns an object of arrow functions. Their this is the function's own this, which is now the host.

Imports. No module-level declaration moves. Seven imported names are read only by the moved bodies, so they leave auth-manager.ts's import lists and the new module imports each from the same place:

  • clearUnlinkTombstone, clearUserUnlinkTombstones and recordUnlinkTombstone (./implicit-account-linking.js);
  • emitAuthSessionAuditEvent, loginEventFor and logoutEventFor (./auth-session-audit.js);
  • holdPrincipalSessionExpiry (./principal-session-mint.js).

Each of the three modules stays imported by auth-manager.ts for other names, so the order in which modules are evaluated does not change. withSystemReadContext (./objectql-adapter.js) and the BetterAuthOptions type are also read by code that stays, so both files import them.

The shape

  • The class keeps both methods at their places, with their names, signatures and docblocks: the published .d.ts lists each one. Each body is now a host literal of exactly the members that body reads, typed as a Pick of DatabaseHooksHost, plus .call.
  • DatabaseHooksHost lives in the new module. It names the 11 members the bodies read, each typed as the class member (AuthManager['settleMembership'], …). The module imports AuthManager as a type only, so there is no runtime cycle.
  • In composeDatabaseHooks the literal is named hookHost. The method's own parameter is already named host (the host stack's databaseHooks), and the stub passes it on: composeDatabaseHooks.call(hookHost, host).
  • Every member is read when a hook runs. better-auth calls the composed hooks on each account, user and session write, long after composeDatabaseHooks returned. So nothing is copied at composition:
    • Methods go as late-bound closures ((...a) => this.settleMembership(...a)). A test double or spy set on the manager is still the one a hook reaches. That includes stampIdentitySource, which account.create.after asks through the manager.
    • config goes as a getter. The class reassigns it after construction (setRuntimeBaseUrl, applyConfigPatch), and the hooks read its dataEngine, logger and getAuditSink when they run. Its autoActiveOrganization is still read once, at composition, as before.
    • idpLocale goes as a getter too, so user.delete.after and session.create.after reach the field the manager holds when they run.
  • stampIdentitySource is no longer async. It returns the moved async function's own promise, so a caller awaits the same promise the body settles. Building the host cannot throw, so the method does not now throw synchronously where it used to reject. composeDatabaseHooks was synchronous and stays so.
  • The cost: one small host object per composeDatabaseHooks call, which runs once per better-auth instance, and one per stampIdentitySource call, which runs on each account link.
  • Nothing re-exports the new module, so the published surface does not move.

Readers re-pointed in the same PR

reader what it reads base head
check:adr-anchors (scripts/adr-anchors/) each anchored file must still cite the ADRs listed for it 78 anchored files; the auth-manager.ts entry lists ADR-0093 and ADR-0108 79 anchored files. The new entry packages__plugins__plugin-auth__src__auth-manager-database-hooks.ts.json lists ADR-0093 and carries the ADR-0093 D2 invariant paragraph of the auth-manager.ts entry, verbatim.
the tenant-audit census (check-tenant-audit-census.mjs, its committed page and counts file) every engine write call site, keyed by file auth-manager.ts update sys_user: 2 auth-manager-database-hooks.ts update sys_user: 2. Regenerated with node scripts/tenant-audit-census.mjs --write, and the page's hand-written erased-receiver figures follow (see below).

Why the ADR anchor entry. The D2 paragraph names the two seams that must never disagree, user.create.after and session.create.before, and both are composed in the body that moved. The new entry follows what #25223's cuts 2 to 4 did for their new modules (#25360 the latest). Per ruling 6353073966 item 1, scripts/adr-anchors/ is not a row of the governed register.

The auth-manager.ts entry is unchanged and still holds:

  • settleMembership, the "single assembly" its D2 paragraph names, stays in the class, as does ADR-0108's role map.
  • Of the file's 26 lines citing ADR-0093 or ADR-0108, 22 stay.
  • The moved body keeps its own ADR-0093 D2, D7 and D9 comments, so the new module cites the ADR it now carries.
  • docs/adr/** is not touched.

Measured as reading no moved line:

  • api-error-code.pin.test.ts: neither body throws an APIError. The authManagerFiles floor reads the same 27 sites, and the per-file textual control finds 0 in the new module.
  • Plugin-factory scans in better-auth-schema-parity.test.ts and managed-extension-fields.test.ts: neither body has an await import( of a better-auth plugin.
  • pnpm check:dispatcher-error-vocabulary: exit 0. Its auth-manager.ts row names deliverPhoneOtp, which did not move.
  • check-auth-mount-ledger.mjs and auth-manager-base-path.test.ts: they read the base-path default, which neither body spells.
  • The census's UNTYPED_RECEIVERS row for auth-manager.ts: it names the SCIM transaction's db, which did not move.
  • The system-context census: neither body holds an isSystem read. stampIdentitySource's { isSystem: true, … } is a context it builds, not a read.
  • Callers through the manager: five suites call composeDatabaseHooks and one calls stampIdentitySource, each through the class method. All pass at head (below).
  • Every symbol anchor into the file. Before the cut, every auth-manager.ts#symbol anchor in the tracked tree outside changelogs was mapped with symbolSegmentResolution from scripts/symbol-anchors.mjs. There are 29, in ADR-0127, ADR-0134, ADR-0135 and four platform-checklist areas, and all 29 are declaration-class at base, with the two bodies blanked, and at head. The one that names a moved method, access-security.json:2419 #composeDatabaseHooks, resolves on the method the class keeps. These checkers were run by hand at base and at head, because dispatch-gates does not derive the ADR one:
    • check-adr-symbol-anchors: 2,383 anchors across 142 records, 466 symbol (440 declaration, 26 literal), the same at both.
    • check-adr-anchors: 78 anchored files at base, 79 at head (the new entry).
    • check-scripts-symbol-anchors (4,058 anchors), check-spec-docblock-symbol-anchors (5,071 anchors), check-system-context-census (148 elevation read sites) and check-platform-checklist (16 areas, 329 items): the same reading at both.
    • All six exit 0 at base and at head.

The census reading

The census reads 237 engine write sites at base and at head. Compared per site, with the new module's file name mapped back to auth-manager.ts, every site keeps its object, verb, tenancy, context and elevation reading: 0 differences, and 0 unresolved and 0 unledgered receivers on both sides.

What changes is how the census reaches the 2 engine.update('sys_user', …) sites of stampIdentitySource, the env_native and the idp_provisioned write:

  • In the class, their receiver is const engine = this.getDataEngine(). The census reads it through the method's declared return type in the same file, so the 2 counted as "receiver carried a readable engine type".
  • In the module, the same text runs on a this typed as Pick<DatabaseHooksHost, ...>. The census does not follow a function's this parameter, so it places them by their literal object name, sys_user, a declared object.
  • The generated table moves readable engine type 185 → 183 and placed by the object NAME 42 → 44, the only total that moves. The page's two hand-written erased-receiver counts (PROSE_COUNTS) are that sum plus the parameter and ledger rows, so they move 52 → 54. 54 of 237 is 22.8%, so "just over a fifth" stays true.
  • composeDatabaseHooks holds no engine write. Its sys_member lookups are reads.
  • The corpus-scale block (not enforced) is re-measured too: 714 sources at head, 713 at base, the difference being the new module, and 172 non-engine subtractions at both. Its Measured on line names the base, fb63d396b.

The effect is the same as cuts 2 and 3 (3 and 8 sites there). Keeping these 2 typed would mean annotating engine in the moved body, which is not a move, or teaching the census to read this parameters, which is a census logic change. So neither belongs in this PR.

Proof

Builds and suites ran through os-verify-lock, and every exit code was captured before any pipe. Base fb63d396b1, head 8f41da7fd7. A full turbo run build ran first. Then plugin-auth was built and its suite run at base, then built and run at head, on the same dependency dists.

1. The moved text.

  • For each of the 2 methods, the base class body and the new module's function body are the same TypeScript token sequence (scanner tokens, kind and text, trivia skipped, each template literal re-scanned at its substitutions as the parser does).
  • Every line is the base line minus exactly two leading spaces, and the parameters and return type are the same text.
  • The class method keeps its docblock and signature, with async dropped from stampIdentitySource.
  • Result: 2 of 2 pass, 406 body lines (composeDatabaseHooks 360 lines, 1,613 tokens; stampIdentitySource 46 lines, 263 tokens).
  • Negative control: one token changed in the module (role: 'owner' → role: 'admin' in composeDatabaseHooks' selectActiveOrg) makes that method fail (tokens and lines DIFFER), and the run exits 1. Restored afterwards; cmp against the saved copy was clean.
  • An independent reading: git diff --color-moved=plain --color-moved-ws=allow-indentation-change fb63d396b1 8f41da7fd7 over the two source files marks 404 of the 409 deleted lines and 418 of the 522 added lines as moved. The other added lines are the stubs' host literals, DatabaseHooksHost, the module header, the per-function pointer docblocks and the import blocks; the other 5 deleted lines are four of the import names that left (emitAuthSessionAuditEvent, loginEventFor, logoutEventFor, holdPrincipalSessionExpiry; the module imports them on one line or under another module's block) and stampIdentitySource's async signature line.

2. .d.ts and exports (ruling 6348891160 items 1 and 2).

file raw base raw head raw diff lines normalised base normalised head
dist/index.d.ts 8a39f1035b6a64888117bb152de21865289e67bc8111d5a14ea457e0fb084d51 8a39f1035b6a64888117bb152de21865289e67bc8111d5a14ea457e0fb084d51 0 486138d3045a7ae05205c64d640eb98c77a85e3ac797a0be0e9ee351e8230bca 486138d3045a7ae05205c64d640eb98c77a85e3ac797a0be0e9ee351e8230bca
dist/index.d.mts 074a15f3d7e075752bfb5aa299151d77d013fedfae947a8bcc8d28d8446b15df 074a15f3d7e075752bfb5aa299151d77d013fedfae947a8bcc8d28d8446b15df 0 8cb244809d4e1cee802060cfe8c37ff784d6f16d341672b7e2d74700ba2e1229 8cb244809d4e1cee802060cfe8c37ff784d6f16d341672b7e2d74700ba2e1229
  • The normaliser, verbatim (the ruling's one-liner): node -e 'const s=require("fs").readFileSync(process.argv[1],"utf8");process.stdout.write(s.replace(/"[^"\n]*"(?:\s*\|\s*"[^"\n]*")+/g,r=>r.split(/\s*\|\s*/).sort().join(" | ")))' dist/index.d.ts | sha256sum.
  • The raw files are already byte-identical, so no union-order swap occurs in this cut: items 1(b) and 1(d) hold with nothing to account for.
  • Positive control: the head dist/index.d.ts with one private member made public (private stampIdentitySource; → stampIdentitySource;, line 3821, a one-line change) hashes raw ae7b5fe254d6dfde9189a1ec5f556eebda8fc8c803859713d430e24f0d9c6503 and normalised 5546200f387e32564d475768d41a8ed51fbb2a6cd2450f8d71babceea0219912. Both differ from the readings above.
  • Export lists: ESM dist/index.mjs and CJS dist/index.js each have 155 names at base and at head, the same list (the sha256 of the sorted names is ed3ac8b902031d30… on all four).
  • The bundle: 18 files in dist/ on both sides, the same list. Only index.js, index.mjs and their two .map files differ, as a move of code inside the bundle must.

3. Tests and lint.

  • pnpm exec vitest run --maxWorkers=3 in packages/plugins/plugin-auth: base Test Files 187 passed (187), Tests 3526 passed | 10 skipped (3536), VERDICT command-exit 0. Head: the same counts, VERDICT command-exit 0. The six suites that call the two methods through the manager are among them: auth-manager.test.ts, auth-session-audit.test.ts, first-session-membership-ordering.test.ts, membership-policy-setting.test.ts, user-created-in-request.test.ts and durability-swallow-repair.test.ts.
  • pnpm --filter @objectstack/plugin-auth typecheck (both tsc projects and check:test-typecheck): exit 0.
  • eslint --no-inline-config (the root lint script's binary and flags) over the 2 TypeScript files: exit 0.

4. Gates.

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 93 commands for the 5 paths. All 93 ran at 8f41da7fd7 after the full build, and all 93 exited 0.
  • --ran: 93 derived, 93 run, 0 NOT-MEASURED.
  • check-governed-merges.mjs --branch HEAD: NOT governed, 956 changed lines.
  • Docs drift (node scripts/docs-audit/affected-docs.mjs --json fb63d396b1, exit 0): 20 hand-written and 9 release-owned pages, each reached through a literal the moved bodies carry unchanged (sys_member, sys_account, idp_provisioned). No page restates a rule this move changes, so no page edit is owed beyond the census's own two documents.
  • Serial constraints (in the claim): no open PR touches auth-manager.ts, the new module, the anchor entry, the census documents or the six suites above. origin/main has since moved to d12e650ded (feat(spec): ExecutionStatus gains missed, a scheduled due time that passed while no kernel was ticking #25363, packages/spec only), and git merge-tree of it with this head is clean.

Changeset

None: skip-changeset, per ruling 6348891160 item 4. The published declarations and export lists are unchanged, and the bundle's bytes move only because code moved inside it.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr

…-source stamp bodies into auth-manager-database-hooks.ts

Two method bodies move unedited: composeDatabaseHooks (the account,
user and session hooks composed around a host's databaseHooks) and
stampIdentitySource (the sys_user.source provenance stamp its
account.create.after runs). The class keeps both methods, their
docblocks and signatures, and hands each body the members it reads.
The new module gets its own ADR-0093 anchor entry, and the tenant-audit
census documents are regenerated.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 23, 2026
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation labels Oct 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/automation/approvals.mdx (via idp_provisioned (literal, a string literal in stampIdentitySource))
  • content/docs/data-modeling/validation-rules.mdx (via sys_account (literal, a string literal in stampIdentitySource))
  • content/docs/deployment/cli.mdx (via sys_account (literal, a string literal in stampIdentitySource))
  • content/docs/deployment/environment-variables.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/deployment/self-hosting.mdx (via sys_account (literal, a string literal in stampIdentitySource))
  • content/docs/deployment/tenancy-modes.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/permissions/authentication.mdx (via sys_account (literal, a string literal in stampIdentitySource), sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/permissions/delegated-administration.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/permissions/permission-sets.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/permissions/positions.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/permissions/system-context.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))

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

  • content/docs/releases/implementation-status.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v16.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v17/17-0.mdx (via sys_account (literal, a string literal in stampIdentitySource), sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v17/17-1.mdx (via sys_account (literal, a string literal in stampIdentitySource), sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v17/17-3.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v17/17-5.mdx (via sys_account (literal, a string literal in stampIdentitySource))
  • content/docs/releases/v17/17-6.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))
  • content/docs/releases/v17/17-7.mdx (via sys_account (literal, a string literal in stampIdentitySource))
  • content/docs/releases/v18/18-0.mdx (via sys_member (literal, a string literal in composeDatabaseHooks))

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
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 42 pages)
  • 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 — 19 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 448374d3f88f052faaf1fde377a54c8c2ce0eb47 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 448374d3f88f052faaf1fde377a54c8c2ce0eb47

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · 2026-10-23T05:39Z

domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · card #25224 cut 4 (amended claim 6357765071, Ruling-ref: 6348891160, 6351993113; os-dev-report 6358185682; card session branch claude/project-thread-lg3wrj) · reviewed head 8f41da7fd7edb0cc5fc7e992fdebe5a1b85c3efb · merge base fb63d396b1

  • Shape: two AuthManager method bodies (406 lines) move unedited into auth-manager-database-hooks.ts, in cut 1's shape.
    • composeDatabaseHooks: the better-auth account, user and session hooks, including the ADR-0093 D2 membership reconciler on user.create.after and the D9 default active organization on session.create.before.
    • stampIdentitySource: the cloud ADR-0024 D4 provenance stamp, whose only caller is account.create.after in the body above. This is the ninth method of ruling 6351993113, the one cut 3 left behind, so all nine have now moved.
    • Seven import names that only the moved bodies read leave auth-manager.ts. Their three modules stay imported, so module evaluation order is unchanged. 5 files, +538 / −418.
  • Move proof (report): 2 of 2 bodies are token-identical, with a failing negative control.
  • .d.ts, ruling 6348891160 item 1, re-run by the seat in its own worktree. turbo run build --filter=@objectstack/plugin-auth ran at fb63d396b1 and at this head, and the hashes are the same at base and head:
    • dist/index.d.ts: raw 8a39f1035b6a64888117bb152de21865289e67bc8111d5a14ea457e0fb084d51, normalised 486138d3045a7ae05205c64d640eb98c77a85e3ac797a0be0e9ee351e8230bca.
    • dist/index.d.mts: raw 074a15f3d7e075752bfb5aa299151d77d013fedfae947a8bcc8d28d8446b15df, normalised 8cb244809d4e1cee802060cfe8c37ff784d6f16d341672b7e2d74700ba2e1229.
    • The raw diff is 0 lines.
    • The seat's own positive control uses a different member from the report's: head line 3759, private composeDatabaseHooks, made public. Raw 2f1a408219560033… and normalised 1c65d5b9d99f12a4… both moved. The report's control (stampIdentitySource, line 3821) also moved both.
    • Export lists: 155 names, ed3ac8b902031d30 on all four (report).
  • The judgement the card thread flagged, the new scripts/adr-anchors entry: the seat agrees with it. The ADR-0093 D2 paragraph names user.create.after and session.create.before, and both seams' bodies now live in the new module. The precedent is [maintainer] refactor(plugin-security): split src/security-plugin.ts (10,746 lines, +2,803 in 30 days) along its SecurityPlugin seams, one move-only slice per PR #25223 cuts 2 to 4 (cut 4, refactor(plugin-security): move SecurityPlugin's controlled-by-parent read leaf into security-plugin-cbp-read-filter.ts (#25223 cut 4) #25360, copied the ADR-0055 §1 invariant). The seat checked the copy: the new entry's invariant is an exact substring of the auth-manager.ts entry's invariant, and that entry is unchanged. Seat re-run, both exit 0 at base and at head:
    • check-adr-anchors
    • check-adr-symbol-anchors: 2383 … 466 symbol.
  • Census:
    • Per site, 237 = 237 with 0 differences.
    • The only total that moves is placed-by-object-name 42 → 44, from the two sys_user updates in stampIdentitySource. The two hand-written figures go 52 → 54, regenerated with --write.
    • Seat re-run: check-tenant-audit-census exits 0 at base and head.
  • Tests and gates (report): plugin-auth 187 files, 3526 pass / 10 skipped, the same as base. typecheck and eslint exit 0. dispatch-gates --ran 93 / 93. CI on this head: 36 success and 11 expected skips, none failed or pending. Mergeable clean, and git merge-tree onto today's main is clean.
  • Body: the seat added the attribution header and requested os-sales through the relay (run 39906857920, read back identical). The report records the body's "20 hand-written" Docs Drift figure as 11 hand-written + 9 release-owned. It is advisory, so the body is left as written.
  • Declarations: refactor(plugin-auth), Clause-②: no, Metadata-protocol: none, skip-changeset. No contract-review record or dogfood battery is owed. Out-of-scope findings: none.
  • Landing: not governed (check-governed-merges --pr 25383: 956 lines). Ready + auto-merge through the relay. The card stays open and dispatched until the card thread reports the series finished.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 23, 2026 05:40
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 23, 2026 05:40
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 23, 2026
Merged via the queue into main with commit ca381a8 Oct 23, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-lg3wrj branch October 23, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants