Skip to content

refactor(plugin-auth): move the createAuthInstance body into auth-manager-auth-instance.ts (#25224 cut 14) - #25566

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

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

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

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

The basis for the Clause-②: no line: this is a move plus one comment migration. No accept set, route, refusal, code or answer changes, and the published surface is measured unchanged:

  • @objectstack/plugin-auth's 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). The positive control is the one ruling 6348891160 asks for.
  • Its ESM and CJS entries export the same 159 names at base and at head. readDisableSignUpEnv, which this cut exports in place inside the package, is not among them.

The .d.ts bar is ruling 6348891160 item 1 (on #25222, covering this card). Ruling 6364006498 is Sales's ruling A on this card. This is the sixth cut it applies to.

Cut 14 of the auth-manager.ts split: the rest of createAuthInstance, which is its whole body. It is the last of the three createAuthInstance slices the seat set (hooks.after, then hooks.before, then the rest of the method). #25224 stays open for the later cuts.

This follows the amended claim 6377759458, which amends 6376168919 on the file cut 13 left (#25548, landed note 6377004954). The shape is #23050's method move, the one cuts 1 to 11 used; cuts 12 and 13 moved closures inside this method.

Branch: claude/project-thread-lg3wrj, the harness-pinned branch for this session, restarted from origin/main after #25548 merged and rebased onto 45f996231e when #25541 merged. It names no issue, so the card relation is this body's first line.

auth-manager.ts goes from 7,807 to 7,122 lines. The cut is +926 / −749 over 3 files: 1,675 changed lines, under the 3,000-line line. One of the three files is a path reader outside the move, so check-governed-merges.mjs ran on the trial file list before the cut: NOT governed (0 of 3 paths hit the register), and --branch HEAD agrees. One commit: b2bbcd6cac refactor(plugin-auth): move the createAuthInstance body into auth-manager-auth-instance.ts.

The move

moved base lines proof lines tokens members it reads
the body of createAuthInstance :1690–:2415 728 2,616 45 methods, 2 fields

The lines are base 45f996231e. The 728 proof lines are the body with its braces (:1689's { to :2416's }).

  • What it is: createAuthInstance (:1689, private async, annotated Promise<Auth<any>>) builds the better-auth instance. getOrCreateAuth() calls it once per build (:1669, this.createAuthInstance().then(…)).

  • What the body does, in order (all of it moves, with its comments): the deferred better-auth and better-auth/api imports, the plugin list and the password hasher, then one config literal (secret, origin and base path, app name, database, the user, account and verification model configs, the social providers, emailAndPassword with its reset-password and sign-up callbacks, the e-mail verification block, session, rate limiting and secondary storage, the plugins, the composed database hooks, the global hooks.before and hooks.after arrows that cuts 12 and 13 left in place, onAPIError, the trusted origins and advanced), the betterAuth() call, and the wait on auth.$context before the instance is returned.

  • Where it goes: a new module, packages/plugins/plugin-auth/src/auth-manager-auth-instance.ts (861 lines). The body becomes the body of export async function createAuthInstance(this: AuthInstanceHost): Promise<Auth<any>>, re-indented two spaces (one level) and otherwise unedited. The module header gives the reasoning, in the form the thirteen earlier auth-manager-*.ts modules use.

  • What stays: createAuthInstance keeps its place, its docblock and its private modifier. Its body is now:

    private createAuthInstance(): Promise<Auth<any>> {
      // [#25224 cut 14] The body moved to `auth-manager-auth-instance.ts` (see `AuthInstanceHost`).
      const readConfig = () => this.config;
      const readIdpLocale = () => this.idpLocale;
      const host: AuthInstanceHost = {
        assertAccountNotLocked: (...a) => this.assertAccountNotLocked(...a),
        // … one closure per method, alphabetical, 45 in all, and:
        get config() { return readConfig(); },
        get idpLocale() { return readIdpLocale(); },
      };
      return createAuthInstance.call(host);
    }

The seat's bars

Body.

  • The body is the same TypeScript token sequence at base and in the module function: 728 lines, 2,616 tokens. Every line is the base line minus exactly two spaces. The method takes no parameters, and the base method, the stub and the function carry the same return annotation, Promise<Auth<any>>.
  • async moves from the method to the function. The stub is not async, and it returns createAuthInstance.call(host), the function's own promise, unchanged. getOrCreateAuth() chains .then(…) on that promise exactly as before. The two reader closures, the host literal and the .call cannot throw before the promise exists, and a throw inside the body is still a rejection of it.
  • No arguments, new.target or super (0 of each), and no bare this: the body's only this uses are member reads this.X. Every method read is a direct call, and the body writes no member. No this sits inside a non-arrow function, so every callback the body hands better-auth reads the same receiver the body does.

Receiver.

  • The body's this was the AuthManager instance. It is now the host.
  • AuthInstanceHost names exactly the 47 members the body reads, no more, each typed as the class member (AuthManager['x']).
  • 45 are methods, so each goes as a late-bound closure, (...a) => this.x(...a). The method runs on the manager and is looked up when the body calls it, so a spy or double set on the manager is still the one the body reaches, including from the callbacks better-auth runs long after the function returns.
  • Two are fields, and both go as getters over a reader closure, readonly on the interface; the body writes neither:
    • config, which the class reassigns after construction (setRuntimeBaseUrl, :3975, writes it at :3987; applyConfigPatch, :3998, at :4068);
    • idpLocale (:4167, private readonly, an IdpLocaleSync), which is not reassigned, as every field this series carries goes (the same pair composeDatabaseHooks' stub hands its body as getters, :7030).
  • Function-valued reads, disclosed (the way refactor(plugin-security): move the everyone anchor, the explanation, the sharing verdicts and the authored-row write verdict out of security-plugin.ts (#25223 cut 10) #25516 disclosed resolveKernelService): the body calls this.idpLocale.stage(data, ctx) once (:1839), and this.config.logger?.info?.(m, meta) once (:1843, inside the logInfo arrow it hands on). Each receiver is the object the getter returns, the manager's current one, so each is looked up and called on the same object, at the same moment, with the same this, as before.
  • hasExistingUserFor and isBootstrapCreation read private statics. They stay in the class, per ruling A, and the body reaches them as members. The body reads no AuthManager.X, so the module imports the class as a type only.

The two hook arrows. hooks.before and hooks.after move with the literal, unedited. Each builds its own host from this, which is now this host: each of their closures calls this host's closure, which calls the manager's method, and their config getters read this host's getter, which reads the manager's field. So they reach the same member, at the same moment, as before. Their imports (globalBeforeHook, globalAfterHook and the two host types) move to the new module with them.

One top-level helper, exported in place. The body reads one top-level declaration of auth-manager.ts: readDisableSignUpEnv (:384, once). Per ruling 6364006498 (A), it gets export in place, in this cut, the first whose body reads it, and the module imports it by value from ./auth-manager.js. The binding proof checks that the head declaration is the base one with export in front and nothing else changed, and that the base and head reads resolve to it. src/index.ts names its auth-manager.ts exports one by one and does not name it, so nothing is published.

Host lifetime. The host is built once per call of createAuthInstance, which getOrCreateAuth() makes once per build. This is what every method stub in this series does. Nothing the body reads is captured early: the closures look each method up on the manager at call time, and the getters read the fields when the body, or a callback it handed better-auth, reads them.

Readers. The decision letters are read on the merge base and on the head below: 8 drop, as expected, and one entry judgment is disclosed. Tenant census and symbol-anchor output are byte-identical at both ends, and the full plugin-auth suite passes at both ends. The one path reader whose text names where the moved lines live is migrated in this commit.

Imports and evaluation order

Ten imported values leave auth-manager.ts, because the body was their last reader there:

  • PLATFORM_IDP_PROVIDER_ID and refuseImplicitAccountLink (the implicit-account-linking.js block keeps type LinkingAuthContext);
  • readEnvWithDeprecation (the @objectstack/types block keeps its other names);
  • the whole account-invitation-marker.js line (readAccountInvitation, settleImportResetDelivery) and the whole client-address-vendor.js line (vendorClientAddressOptions);
  • AUTH_USER_CONFIG, AUTH_SESSION_CONFIG, AUTH_ACCOUNT_CONFIG and AUTH_VERIFICATION_CONFIG (the auth-schema-config.js block keeps its other names).

The cut 12 and cut 13 import lines (globalAfterHook, globalBeforeHook and the two host types) and their comments are replaced by one comment and one line: import { createAuthInstance, type AuthInstanceHost } from './auth-manager-auth-instance.js'. audiencePermitsSelfRegistration, resolveEmailVerificationRequirement, isPlaceholderEmail and the better-auth types have other readers, so both files import them.

esbuild drops a type-only import, so a module whose last value import leaves auth-manager.ts can now be evaluated later. The order was measured rather than argued:

  • The ES modules, at run time. A loader hook logged each ES module as its body ran while dist/index.mjs loaded, at base and at head. 171 modules evaluate at both, and the first 117 are the same set at both. One block moves: the 17 modules @better-auth/core/context loads (16 files of @better-auth/core's context, async_hooks, error, db, env and utils directories, and better-call's error) ran first at base and now run after the 100 others in that set (zod's v4 modules, @objectstack/spec's security, api and data entries, pg-connection-string and @objectstack/types). They follow the bundle's first @better-auth/core/context import, which is account-invitation-marker.ts's at both, and that module's segment now sits later in the bundle (line 196 at base, 477 at head). From position 118 on, the order is identical. Their top levels are constants, classes and function declarations, plus defineErrorCodes, createLogger() and the env proxy; the only load-time reads are of process.env (NODE_ENV, TEST, the default logger's colour depth), and the better-auth:global slot is touched only when a function is called. None of the 100 writes process.env or that slot. The bundle's own body runs last, after all 170, at both.
  • The CommonJS entry. A Module._load log for dist/index.js gives 127 first loads at both, the same apart from @better-auth/core/dist/context/index.mjs, which now loads last instead of eighth from last.
  • plugin-auth's own modules. A walk of the bundle's top level and its eager init_* calls finds three that run later: implicit-account-linking.ts (one place), client-address-vendor.ts and account-invitation-marker.ts, whose value imports left auth-manager.ts with the body. Their top levels are string constants, one arrow constant (authContextOf), and two defineRequestState(() => null) calls, each of which makes its own frozen key object and touches no shared state until it is read inside a request. No importer reads any of their bindings at load time: all 14 references in the 6 importing files are inside functions.
  • The new module's own top level is imports, one interface and one function declaration. Its value import of auth-manager.ts, for readDisableSignUpEnv, and auth-manager.ts's import of it form a cycle with nothing read at load time.

The decision-letter layer: −8, as expected

check-adr-anchors, measured on the merge base and on the head. The gate exits 0 at both.

point anchored files citations files decision letters
merge base 45f996231e 84 52,280 6,688 1,946
head b2bbcd6cac 84 52,280 6,689 1,938
  • The eight letters are in the body's comments: ADR-0131 D7 (:2000, :2114), ADR-0069 D4 (:2142), ADR-0069 D2 (:2190, :2214), ADR-0093 D2 (:2222) and ADR-0135 D6 (:2347, :2351). They leave auth-manager.ts, which is anchored, for a module that is not. Their citations still resolve, so the citation count does not move.
  • One judgment, disclosed: no entry is owed for ADR-0093 D2. The letter at :2222 is in the comment on the databaseHooks: this.composeDatabaseHooks(this.config.databaseHooks) wiring, which says D2 makes the database-hook seam the single owner of the user-lifecycle invariants. The auth-manager.ts entry's D2 paragraph describes settleMembership, which stays, and its two seams (user.create.after and session.create.before), which are database hooks that cut 4 moved to auth-manager-database-hooks.ts with their own entry. It does not describe the wiring line, and no other entry names it. The ADR-0108 paragraph (the organization roles map) is not in the body. So, per the seat's bar, no entry is added for the new module. If the seat reads the wiring as part of the D2 mechanism, the remedy is an entry for the new module carrying the D2 paragraph verbatim, cut 4's precedent, in this PR.

Path readers

One reader outside the move names where the moved lines live, and it follows the body in this commit:

  • auth-email-locale.test.ts: the two population comments (the docblock at :861 and the in-test note at :1126, head numbering) that name the files the five sendTemplate sites live in now name auth-manager-auth-instance.ts (the body of createAuthInstance) among them. No assertion, count or pattern changes.

Read, and not edited:

  • api-error-code.pin.test.ts: the body carries no new APIError( site, so authManagerFiles is not owed the new module. Its sum stays 30, as at base.
  • scripts/check-dispatcher-error-vocabulary.mjs: the body has no new APIError( or APIError.from( site and no live member, so nothing in the gate moves. It exits 0 at both, 56 sites found and 56 classified at both, and only its scanned-file count moves (3,430 → 3,431). --self-test is among the derived gates below.
  • The tenant-audit census. No census site moves: the body has no engine write. check-tenant-audit-census exits 0 at both with no --write, with byte-identical output. Its --json document has 238 sites at both and 0 per-site differences with line numbers ignored. 2 sites are in auth-manager.ts at both (each 685 lines earlier at head, the cut's net), 0 are in the new module, and 0 are unresolved or unledgered. Only scannedSources (751 → 752) and measuredAt move, so neither census document is edited.
  • scripts/measure-durability-swallow-family.mjs (a measurement, not a gate): 77 members and 84 quiet rows at both, identical with line numbers ignored (one member's write line, in observeFirstMemberships, which stays, moves 5,177 → 4,492). files moves 3,480 → 3,481, and tryStatements (3,645) is the same. One statistic moves, and I traced it: droppedNotAwaited goes 233 → 234 (delete 149 → 150). It counts write-named calls that are not awaited inside a try block and the same-file helpers that block calls, walked by name, each name once, at most four levels deep. The try in linkingContext (base :4355) walks getOrCreateAuth, then createAuthInstance. At base the walk entered the body and reached isOperatorProvisioning at depth 4, so prunePendingOperatorProvisioning sat at depth 5, past the limit. At head the walk enters the stub, whose host literal lists the members alphabetically, and reaches isOperatorProvisioning at depth 3. So prunePendingOperatorProvisioning (:4923, which stays) is now walked, and its this.pendingOperatorProvisioning.delete(key) is counted. That call is a synchronous Map delete, not a write, and the code is unchanged. A logged copy of the instrument, run on the base and head trees and then removed, shows this is the only dropped call that differs (233 at base, 234 at head). Membership and the quiet rows, which are what the measurement reports, do not move.
  • check-auth-mount-ledger.mjs reads only the basePath declaration in auth-manager.ts, which stays. auth-manager-base-path.test.ts reads auth-manager.ts for code lines carrying '/api/v1/auth'; the body has one only in a comment (:1814).
  • The plugin-factory scans in better-auth-schema-parity.test.ts and managed-extension-fields.test.ts match const { X } = await import('better-auth/plugins…' | '@better-auth/…'). The body's two dynamic imports are better-auth and better-auth/api, neither a factory. At head the scanned shape matches 16 times in auth-manager.ts, as at base, and 0 times in the module. Both suites pass at head.
  • rate-limit-storage-isolation.test.ts walks the import graph from rate-limit-storage.ts, which reaches neither file. It passes at head.
  • Prose elsewhere that names auth-manager.ts for code now in the module (for example the NODE_ENV-gated trusted-origins substitution named in packages/cli/test/serve-node-env-production-default.e2e.test.ts) reads no file and is not edited, as cuts 12 and 13 left the prose that names the before-hook.
  • 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, and all 29 are declaration-class at base, at base with the body blanked, and at head. Five of their symbols occur as a token on a line of the body: secondaryStorage (ADR-0127 :114), composeDatabaseHooks (the access-security checklist area :2419), isBootstrapCreation (identity-auth :1810), readDisableSignUpEnv (identity-auth :1901) and requireEmailVerification (identity-auth :2004). Each resolves to its declaration, which stays, at all three readings; readDisableSignUpEnv's declaration (:384) gains only export.

Proof

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

1. The moved text.

  • Token identity: the base method body and the module 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): 728 lines and 2,616 tokens, each line the base line minus two spaces.

  • The stub's shape, checked by the same run: not async, the same docblock, modifiers, parameters and return annotation as the base method, the two reader closures, one host literal whose keys are exactly AuthInstanceHost's, alphabetical, whose 45 method values are each (...a) => this.<key>(...a) and whose config and idpLocale are getters over the readers, then return createAuthInstance.call(host), and nothing else. The interface types every member AuthManager['x'], and exactly the two getter fields are readonly.

  • Binding identity: a second proof walks all 672 identifiers of the body at base and in the module, in order, and resolves each with the TypeScript checker (the module's ./auth-manager.js resolved to the head file, where the in-place export lives). All 672 resolve the same way at both ends:

    • 402 to a declaration inside the body, the same declaring identifier by position;
    • 108 to receiver members (AuthManager at base, AuthInstanceHost at head);
    • 19 to imports with the same specifier and imported name, readDisableSignUpEnv among them;
    • 14 to the same declaration in auth-manager.ts: seven AuthManagerOptions members read through this.config (databaseHooks, emailService, logger and its info, rateLimit, rateLimitStorage and secondaryStorage);
    • 129 to no declaration: globals and property names on untyped values.

    None resolves to an unexported top-level declaration of auth-manager.ts or to a top-level declaration of the module.

  • Negative controls: each proof reads the module text, and each control changes it in memory only; the file is untouched.

    • Token proof: betterAuth( changed to betterAuth?.( fails the token and line checks, and the run exits 1.
    • Binding proof: the module's placeholder-email import pointed at another specifier gives 1 difference, and the run exits 1. The same import shadowed by a module-level const gives 1 difference and reports the module top-level read, and exits 1. Run without being told about the in-place export, it reports the base read of an unexported helper, and exits 1.
  • An independent reading: git diff --color-moved=plain --color-moved-ws=allow-indentation-change 45f996231e b2bbcd6cac over the two source files marks 734 of the 744 deleted lines and 768 of the 920 added lines as moved.

    • The 10 unmoved deletions are two import-name lines (refuseImplicitAccountLink,, readEnvWithDeprecation,), the six comment lines above the cut 12 and cut 13 import lines, function readDisableSignUpEnv(): boolean | undefined { (now with export) and private async createAuthInstance(): Promise<Auth<any>> { (now without async).
    • The other added lines are the module header, its import lines, the host interface and its docblock, the function signature and docblock, the stub's comment, readers, host literal and return, and the new import comment and line.

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

file base and head raw diff lines
dist/index.d.ts raw 1b3a48357d137155de33104277667a922aab62df73b418b03894c56faaacd0ba 0
dist/index.d.ts normalised cee7303ccdfe72794efd588c85b628419d4253d730b5a0c5a3f1cdb8dddb4443
dist/index.d.mts raw b5e8dc982b634f0a5108b1b533626a68767f2da5a56f72dba801059b149e2872 0
dist/index.d.mts normalised 562d0a4ce656f97aeb937541482cdf531b61d7c75a005918784bf0a52d032dfc
  • 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 byte-identical, so no union-order swap occurs: 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 createAuthInstance; → createAuthInstance;, line 1712, a one-line change) hashes raw 35b18682171cd394898c9e56bc8d5272ccf20aeff0d1966fc568cb5e566a08df and normalised e69c1b0157b45e48bbc16bd64eb939abf9be33ccb31553485df93ef05aac6fc3. Both differ from the readings above.
  • Export lists: ESM dist/index.mjs and CJS dist/index.js each have 159 names at base and at head, the same list (the sha256 of the sorted names is 3abb126f5babb701… on all four). readDisableSignUpEnv, createAuthInstance and AuthInstanceHost are not among them, and dist/index.d.ts names neither readDisableSignUpEnv nor AuthInstanceHost.
  • The bundle: the same 18 files in dist/ at both, plus the two .build-input-hash stamps the build writes from its inputs. Only index.js, index.mjs and their two .map files differ, as a move of code inside the bundle must. tsup bundles the new module into the root entry (splitting: false).

3. Tests and lint.

  • pnpm exec vitest run --maxWorkers=3 in packages/plugins/plugin-auth: base Test Files 191 passed (191), Tests 3620 passed | 10 skipped (3630), exit 0. Head: the same counts, exit 0.
  • pnpm --filter @objectstack/plugin-auth typecheck (both tsc projects and check:test-typecheck): exit 0. pnpm --filter @objectstack/runtime typecheck: exit 0.
  • eslint over the 3 files: exit 0.

4. The censuses, the measurement and the anchors, at base and at head.

  • The tenant-audit census, as above: byte-identical gate output, 238 sites at both, 0 per-site differences line-blind, 0 unresolved and 0 unledgered at both.

  • The durability measurement, as above, with its one moved statistic traced.

  • These checkers were run by hand at base and at head, because dispatch-gates does not derive the ADR one. All six exit 0 at both. Five print byte-identical output at both (cmp clean):

    • check-adr-symbol-anchors: 2,384 anchors across 142 records, 466 symbol (440 declaration, 26 literal);
    • check-scripts-symbol-anchors: 4,070 anchors;
    • check-spec-docblock-symbol-anchors: 5,076 anchors;
    • check-system-context-census: 149 elevation read sites;
    • check-platform-checklist: 16 areas, 329 items.

    The sixth, check-adr-anchors, differs only in the file count and the decision letters, as in the table above.

5. Gates.

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 60 commands for the 3 paths. All 60 ran at head after the full build, and all 60 exited 0.
  • --ran over the record with each exit code: 60 derived, 60 run, 0 NOT-MEASURED, a derived zero. --tier: no path-derived mandate.
  • check-governed-merges.mjs: on the trial file list before the cut and --branch HEAD after the commit, both read NOT governed (0 of 3 paths hit the register), 1,675 changed lines.
  • Docs drift (node scripts/docs-audit/affected-docs.mjs --json 45f996231e, exit 0): 8 pages, all name matches of names the moved code carries (the OS_CORS_ORIGIN and CORS_ORIGIN literals, isAuthGateActive and resolveActor as host members, and sys_oauth_resource in a comment). None names auth-manager.ts, and the code that reads those names is unchanged, so no doc edit is owed. 3 of them are release notes (17.3, 18.0, 18.1), owned by the release, and not edited.
  • Serial constraints (in the claim, and re-read at 00:52Z before this PR): 11 PRs are open, and none touches auth-manager.ts, the new module's path, auth-email-locale.test.ts or a reader named above. One opened after the claim touches plugin-auth: fix(plugin-auth, driver-sql)!: fail closed on a missing auth secret outside development; compile window functions from closed tables #25563 edits auth-manager-database-secret.ts and auth-manager.test.ts, neither a file of this cut, and reads no source by path; resolveAuthSecret, which the body calls, is reached through a late-bound closure, so a change to it lands the same way at both. The Version Packages PR chore: version packages #24876 touches plugin-auth's package.json, its CHANGELOG.md and changesets only. feat(security): the external organization role is the EXTERNAL principal — owner-only org-wide default, explicit grants only #25541, which the cut 13 claim named, merged as this base; its hunks in the file are outside the body. origin/main has moved 6 merges past this base, to 041a120b61; none touches plugin-auth, scripts/adr-anchors/ or a reader named above.

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

…ager-auth-instance.ts

The body of AuthManager.createAuthInstance() moves unedited into the
module function createAuthInstance, re-indented one level: the deferred
better-auth imports, the plugin list and password hasher, the whole
config literal (including the hooks.before and hooks.after arrows that
cuts 12 and 13 left in place), the betterAuth() call and the wait on
auth.$context. The method keeps its place, docblock, modifiers and
return type; it now builds one host of the forty-seven AuthManager
members the body reads (forty-five methods as late-bound closures,
config and idpLocale as getters) and returns
createAuthInstance.call(host). The async moved from the method to the
function.

readDisableSignUpEnv, the one top-level helper the body reads, is
exported in place and imported by value; src/index.ts names its
auth-manager.ts exports one by one, so nothing new is published. Ten
imported values leave auth-manager.ts with the body, and the cut 12 and
cut 13 hook imports are replaced by the new module's.

One reader outside the move follows it: the population comments in
auth-email-locale.test.ts name the new module among the files the five
sendTemplate sites live in.

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 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/deployment/environment-variables.mdx (via CORS_ORIGIN (literal, a string literal in createAuthInstance), OS_CORS_ORIGIN (literal, a string literal in createAuthInstance))
  • content/docs/deployment/production-readiness.mdx (via OS_CORS_ORIGIN (literal, a string literal in createAuthInstance))
  • content/docs/deployment/self-hosting.mdx (via OS_CORS_ORIGIN (literal, a string literal in createAuthInstance))
  • content/docs/kernel/contracts/auth-service.mdx (via isAuthGateActive (symbol, a field of interface AuthInstanceHost), isAuthGateActive (literal, a string literal in isAuthGateActive))
  • content/docs/permissions/system-context.mdx (via resolveActor (symbol, a field of interface AuthInstanceHost), resolveActor (literal, a string literal in resolveActor))

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

  • content/docs/releases/v17/17-3.mdx (via sys_oauth_resource (literal, a string literal in a comment in createAuthInstance))
  • content/docs/releases/v18/18-0.mdx (via OS_CORS_ORIGIN (literal, a string literal in createAuthInstance))
  • content/docs/releases/v18/18-1.mdx (via OS_CORS_ORIGIN (literal, a string literal in createAuthInstance))

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, 43 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 — 21 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 041a120b6189e9b174a3f4b8cf6202f22ace2bbd → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 041a120b6189e9b174a3f4b8cf6202f22ace2bbd

⚠️ 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 041a120b6189e9b174a3f4b8cf6202f22ace2bbd → 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-24T01:34Z

domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · card #25224 cut 14 (claim 6377759458, amending 6376168919; os-dev-report 6378140559) · reviewed head b2bbcd6cac · merge base 45f996231e

  • Shape: the remaining createAuthInstance body moves unedited into auth-manager-auth-instance.ts in [maintainer] refactor(rest): split packages/rest/src/rest-server.ts (13,701 lines, 70 commits in 30 days) along its register*Endpoints seams, one move-only slice per PR, /meta after #22877 stage 2 #23050's method shape: 728 lines and 2,616 tokens, with 672/672 identifier bindings. The stub builds a host of 45 late-bound closures plus getters for config and idpLocale. readDisableSignUpEnv is exported in place (ruling A) and is not among the package's exports. 3 files, +926 / −749, 1,675 changed lines.
  • Path reader: the two POPULATION comments in auth-email-locale.test.ts now name the new file among the five sendTemplate sites. The edit is to prose only.
  • The ADR-0093 D2 letter at :2222 (the card thread's question): no entry owed, and the seat agrees.
    • The D2 paragraph ("plugin-auth composes a user.create.after step into composeDatabaseHooks…") describes composeDatabaseHooks and the reconciler it composes.
    • composeDatabaseHooks is still declared in auth-manager.ts at head, with 0 declarations in the new module.
    • What moved is the databaseHooks: this.composeDatabaseHooks(…) wiring line and its pointer comment.
  • Durability, accepted as disclosed: droppedNotAwaited goes 233 → 234. This is the instrument, not the code. The name-deduped, depth-4 helper walk now visits the host in alphabetical order and reaches prunePendingOperatorProvisioning's synchronous Map.delete. Members and quiet rows are identical.
  • Load order, accepted as disclosed: the 17 modules @better-auth/core/context loads now evaluate after zod, three spec entries and types. Their top levels are inert, and nothing before them writes process.env.
  • .d.ts, ruling 6348891160 item 1, re-run by the seat in its own worktree:
    • turbo run build at 45f996231e and at this head.
    • index.d.ts raw 1b3a48357d137155… / normalised cee7303ccdfe7279…, and index.d.mts raw b5e8dc982b634f0a… / normalised 562d0a4ce656f97a…, at both ends. The raw diff is 0 lines.
    • The seat's positive control makes head line 347, private options;, public. That moves both hashes, to raw 51a55969ad8afac6… and normalised 66bcd34e7ea6963c….
  • Gates re-run by the seat, all exit 0 at base and at head:
    • check-adr-symbol-anchors: byte-identical output.
    • check-tenant-audit-census: byte-identical output, 238 sites.
    • check-adr-anchors
    • check-dispatcher-error-vocabulary: 56/56 classified. The scan grows by the one new file.
  • Decision-letter count, read by the seat: 1946 at the merge base and 1938 at head, with 84 anchored files at both ends. The −8 are itemised in the report: ADR-0131 D7 ×2, ADR-0069 D4, ADR-0069 D2 ×2, ADR-0093 D2 and ADR-0135 D6 ×2.
  • Report: the suite gives 191 files and 3,620 at both ends. The merge check on 9c43e19f65 is clean.
  • CI: 34 success and 12 expected skips, none failed or pending (46 runs).
  • Mergeable: git merge-tree onto today's main (ce18e73483) is clean. No other open PR touches auth-manager.ts.
  • Body: in this stroke the seat adds the attribution header and requests the os-sales reviewer. The footer is present.
  • Landing: not governed (check-governed-merges --pr 25566: 1,675 lines). Ready + auto-merge through the relay. The card stays open and dispatched.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot requested a review from os-sales October 24, 2026 01:35
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 24, 2026 01:35
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 24, 2026 01:35
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 24, 2026
Merged via the queue into main with commit 35d5060 Oct 24, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-lg3wrj branch October 24, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants