Repository navigation
Commit 794b769
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuMjbPDrsigsyqoKsV4WZQMz)_
Part of #25224
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160, 6364006498
The basis for the `Clause-②: no` line: this is a move plus a
comment-and-anchor reader fix. 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 after each commit (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, after
commit 1, and at head.
The `.d.ts` bar is ruling `6348891160` item 1 (on #25222, covering this
card). Ruling `6364006498` is Sales's ruling A on this card, for bodies
that read shared unexported helpers. This is the third cut it applies
to.
Cut 11 of the `auth-manager.ts` split: **the WebContainer password
hasher**, after **a declared reader fix for cut 8's anchor miss**.
#25224 stays open for the later cuts.
This follows the amended claim `6372234293`, which amends `6369962162`
on the file cut 10 left (#25508, landed note `6371356601`). The shape is
cuts 1 to 10's, which is #23050's.
Branch: `claude/project-thread-lg3wrj`, the harness-pinned branch for
this session, restarted from `origin/main` `7d3c0139fb` after #25508
merged. It names no issue, so the card relation is this body's first
line.
`auth-manager.ts` goes from 8,521 to 8,496 lines. The cut is +96 / −33
over 4 files: 129 changed lines, under the 3,000-line line. It is not a
governed surface (`check-governed-merges.mjs --branch HEAD`: NOT
governed). Two commits, in the order the seat set:
1. `e2b7669134` `chore(plugin-auth): anchor
auth-manager-membership-admission.ts to ADR-0093`
2. `08e7407930` `refactor(plugin-auth): move AuthManager's WebContainer
password hasher into auth-manager-password-hasher.ts`
## Commit 1: the reader fix (comment and anchor entry only)
Cut 8 (#25423) moved `settleMembership`'s body into
`auth-manager-membership-admission.ts`. The ADR-0093 D2 invariant that
governs it stayed anchored only on `auth-manager.ts` and
`auth-manager-database-hooks.ts`. Cut 10's report named this as an
out-of-scope finding, and the seat put the fix in this PR, as its own
commit.
- **A new anchor entry**,
`scripts/adr-anchors/packages__plugins__plugin-auth__src__auth-manager-membership-admission.ts.json`.
`adrs` is `["ADR-0093"]`, and `invariant` is the D2 paragraph verbatim.
That is the same text as the database-hooks entry's invariant and the
second paragraph of the `auth-manager.ts` entry, in the database-hooks
entry's JSON layout.
- **One header paragraph** in the module, just above "Nothing re-exports
this module", worded as the seat approved. It says `settleMembership` is
governed by ADR-0093 D2, and that `scripts/adr-anchors/` carries the
invariant for this file as it does for `auth-manager.ts`. The presence
check needs the citation: an entry whose file never names its ADR fails
`check-adr-anchors`.
- **No code changes.** The module's tokens are 1,055 before and after,
and its comment-free print is identical.
## Commit 2: the move
| moved | base line | proof lines | members it reads |
|---|---:|---:|---|
| `resolvePasswordHasher` (private) | `:3160` | 33 | none |
The line is base `7d3c0139fb`. The 33 proof lines are the two
return-type continuation lines and the 31 body lines (`:3161`–`:3193`).
The body has no `this` at all.
- **What it is:** the pure-JS scrypt hasher that `createAuthInstance`
(`:1696`, call at `:1700`) hands better-auth inside StackBlitz's
WebContainer, whose `node:crypto.scrypt` polyfill fails sign-up. Outside
WebContainer it returns `undefined`, so the native hasher stays.
- **What moves with it, text unchanged:** its lazy `await
import('@noble/hashes/scrypt.js')` (still loaded only inside
WebContainer), the scrypt parameters, the `{saltHex}:{keyHex}` storage
format, and its `console.warn` (`[AuthManager] WebContainer detected but
pure-JS scrypt unavailable: …`).
- **The shape:** the class keeps the method at its place, with its name,
signature and docblock, because the published `.d.ts` lists it. The body
reads no member of the class, so it becomes a plain call, `return
resolvePasswordHasher();`, with the comment "it reads no member of this
class". There is no host type.
- **The stub is no longer `async`.** It returns the moved async
function's own promise, and the one caller awaits it, so the caller
settles on the same promise. Calling an async function cannot throw
synchronously, so nothing now throws where it used to reject.
**`isWebContainerRuntime` is exported in place** (ruling `6364006498`,
parameter 2):
- `:262` becomes `export function isWebContainerRuntime()`. That is the
only edit to code that stays.
- It does not move, because `installWebContainerRequestStatePolyfill`
(`:342`) reads it too. The constructor installs that polyfill (`:1641`).
- The new module imports it by value from `./auth-manager.js`. The one
read is inside the moved body, when the better-auth instance is built.
The two modules import each other. That is safe because the new module's
top level is one import and one function declaration, so nothing reads
the binding while the two modules are first evaluated.
- `index.ts` lists `auth-manager.ts`'s published names one by one (cut
9), and it lists neither this function nor the new module. The head's
ESM and CJS entries do not export either name, and `dist/index.d.ts`
names neither.
**Imports.** No import leaves `auth-manager.ts`: the body's only reads
are its own dynamic import and `isWebContainerRuntime`. One import line
is added after cut 10's block, with a comment in the same form.
## The decision-letter layer: +2, as agreed with the seat
`check-adr-anchors`, measured at each point. The gate exits 0 at all
three.
| point | anchored files | citations | files | decision letters |
|---|---:|---:|---:|---:|
| base `7d3c0139fb` | 82 | 52,244 | 6,661 | 1,978 |
| after commit 1 `e2b7669134` | 83 | 52,247 | 6,662 | 1,980 |
| head `08e7407930` | 83 | 52,247 | 6,662 | 1,980 |
- The three new citations are the header's `ADR-0093 D2`, the entry's
`adrs` id, and its invariant's `ADR-0093 D2`. The new file is the entry
JSON.
- The two new letters are the header's D2 and the invariant's D2, both
now on the anchored surface.
- Commit 2 moves nothing in this layer. The moved body cites no ADR, and
the new module's header cites no `ADR-NNNN`.
- The anchored-file count was 81 at cut 10's base `05cc3ee953` and is 82
at this base. This entry makes it 83.
## Path readers
No path reader needs migrating: none reads a moved line.
- **The tenant-audit census.** No census site moves: the moved text has
no engine write, no `this.X.delete()` and no elevation read.
`check-tenant-audit-census` exits 0 at head with no `--write`. There are
238 sites at base and at head, compared per site with line numbers
ignored: 0 differences, 2 in `auth-manager.ts` at both, and 0 in the new
module. So neither census document is edited.
- **`check-dispatcher-error-vocabulary.mjs`** and
**`api-error-code.pin.test.ts`'s `authManagerFiles` floor**: no `new
APIError(` sits in the moved text. The gate reads 56 sites found and 56
classified at base and at head.
- **`check-auth-mount-ledger.mjs`**: no base-path literal or declaration
sits in the moved text.
- **The plugin-factory scans** in `better-auth-schema-parity.test.ts`
and `managed-extension-fields.test.ts` match `better-auth/plugins…` and
`@better-auth/…` dynamic imports. The moved `@noble/hashes/scrypt.js`
import is neither, and 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.
- **The system-context census** reads 149 elevation read sites at base
and at head.
- **Prose that names `isWebContainerRuntime()` in `auth-manager.ts`**
(`jwt-key-algorithm.ts:24`) still holds: the function stays there.
- **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, with the moved body blanked, and at head.
None names `resolvePasswordHasher` or `isWebContainerRuntime`. One names
a token the moved text carries, `#AuthManager` (ADR-0135 `:104`; the
moved warning spells `[AuthManager]`), and it resolves on the class
declaration, which stays.
## Proof
Builds and suites ran through `os-verify-lock`, and every exit code was
captured before any pipe. Base `7d3c0139fb`. A full `turbo run build`
ran first. Then `plugin-auth` was built and its suite run at base, built
after commit 1, and built and run again at head, on the same dependency
dists.
**1. The moved text.**
- 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): 33 lines and 301 tokens.
- Every line is the base line minus exactly two leading spaces, and the
return type is the same text.
- The class method keeps its docblock and signature, with `async`
dropped.
- **Negative control:** one token changed in the module (`p: 1` → `p: 2`
in the scrypt parameters) makes the method fail (tokens and lines
DIFFER), and the run exits 1. The module was restored afterwards, and
`cmp` against the saved copy was clean.
- **An independent reading:** `git diff --color-moved=plain
--color-moved-ws=allow-indentation-change e2b7669 08e7407` over
the source files marks 31 of the 33 deleted lines and 32 of the 83 added
lines as moved.
- The other 2 deleted lines are `function isWebContainerRuntime()` (now
`export function`) and the `private async resolvePasswordHasher():
Promise<` signature line (now without `async`).
- The other added lines are the module header, its import, the pointer
docblock, the function signature, the stub's two lines and the new
import block.
- A one-word comment correction in the new header was amended into
commit 2 after the full run ("nine" → "ten", the count of earlier
`auth-manager-*.ts` modules). The rest of this section, the suites, the
typechecks and the gates were measured at the pre-amend head
`e66a6f4a8d`. Measured again after the amend: the move proof is still 1
of 1, the module's comment-free token print is identical to the
pre-amend one (355 tokens), a rebuilt `dist/` matches the head readings
byte for byte (only the two `.build-input-hash` files differ), `eslint`
exits 0, and `check-adr-anchors` reads as in the table.
**2. `.d.ts` and exports (ruling `6348891160` items 1 and 2).**
| file | base, after commit 1, head | raw diff lines |
|---|---|---:|
| `dist/index.d.ts` raw |
`b85cb81c24e981304d02184a9ef1294be702aa7aa9df1d327f51e66be9dcfd33` | 0 |
| `dist/index.d.ts` normalised |
`c86ed39db60c3929cf287b59d6cc9aa1a2505ccbfbaa23a624af35a3fcd11abb` | |
| `dist/index.d.mts` raw |
`3561220e444c82fbf102db534ec2a274041db507ed9e38785c0993ca92b84f70` | 0 |
| `dist/index.d.mts` normalised |
`6165644e3dfd1d779991e9bd63ceac3b9ab3dc47d910776ac5a88b4293ef6de5` | |
- These are the same readings as cut 10's head, because neither cut
changes the published declarations.
- 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 resolvePasswordHasher;` →
`resolvePasswordHasher;`, line 1732, a one-line change) hashes raw
`aadc3e91ed586bd9ff14509f08529a4b9df5b0d5ee3ee9c3e9d914e939b6a3bd` and
normalised
`a31ac6fb32392d426d5480769cd97133ed0895014104c731f80316d2b7e0b3c9`. Both
differ from the readings above.
- **Export lists:** ESM `dist/index.mjs` and CJS `dist/index.js` each
have 159 names at all three points, the same list (the sha256 of the
sorted names is `3abb126f5babb701…` on all six). `isWebContainerRuntime`
and `resolvePasswordHasher` are not among them.
- **The bundle:** 18 files in `dist/` at all three points, the same
list. After commit 1 only the two `.map` files differ, because a comment
line moved code positions in the source. At head `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 189 passed (189)`,
`Tests 3580 passed | 10 skipped (3590)`, 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 --no-inline-config` (the root `lint` script's binary and
flags) over the 3 source files: exit 0.
**4. The censuses, the measurement and the anchors, at base and at
head.**
- The tenant-audit census, as above: 238 sites at both, 0 per-site
differences, 0 unresolved and 0 unledgered at both, and every total the
same. Only `scannedSources` (734 → 735, the new module) and `measuredAt`
differ.
- `scripts/measure-durability-swallow-family.mjs` (a measurement, not a
gate): 77 members and 84 quiet rows at both, the same with line numbers
ignored. Only the scanned file count moves (3,459 → 3,460, the new
module). `tryStatements` (3,633), `droppedNotAwaited` (242) and every
other statistic are the same: the moved `try` calls no same-file helper,
so its walk does not change.
- 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
(and the two ADR ones after commit 1 too):
- `check-adr-symbol-anchors`: 2,384 anchors across 142 records, 466
symbol (440 declaration, 26 literal), at both;
- `check-adr-anchors`: as in the table above;
- `check-scripts-symbol-anchors`: 4,070 anchors at both;
- `check-spec-docblock-symbol-anchors`: 5,075 anchors at both;
- `check-system-context-census`: 149 elevation read sites at both;
- `check-platform-checklist`: 16 areas, 329 items at both.
**5. Gates.**
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derives 63 commands for the 4 paths. All 63
ran at head after the full build, and all 63 exited 0.
- `--ran` over the record with each exit code: 63 derived, 63 run, 0
NOT-MEASURED, a derived zero.
- `check-governed-merges.mjs --branch HEAD`: NOT governed, 129 changed
lines.
- **Docs drift** (`node scripts/docs-audit/affected-docs.mjs --json
7d3c013`, exit 0): 3 pages,
`content/docs/kernel/contracts/auth-service.mdx`,
`content/docs/kernel/services-checklist.mdx` and
`content/docs/permissions/authentication.mdx`. All three are reached
only through the `AuthManager` class name, which stays. So no doc edit
is owed. 0 release-owned pages.
- **Serial constraints** (in the claim): 8 PRs are open. One touches
`auth-manager.ts`: #25515, in four hunks (one name added to an import
and two new import lines at the top, a hook arm of `createAuthInstance`,
the custom organization roles in `buildPluginList`, and a new method
above `resolveActor`). None reaches this cut's regions. A scratch `git
merge-tree` of `origin/main` `7a70be4365` with #25515, then this branch,
has no conflict at either step. `origin/main` has moved 7 commits past
this base. None of them touches `plugin-auth` or `scripts/adr-anchors/`.
Re-read just before this PR was opened: #25519 has merged since
(`aa2dbab7b6`, `plugin-email` only), #25523 has opened
(`.github/workflows/ci.yml` and one `scripts/ci/` file only), and
#25515's head is unchanged.
## 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](https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01LSXBc2GCBwiAL7vV4kWkyr
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent f38832b commit 794b769
4 files changed
Lines changed: 96 additions & 33 deletions
File tree
- packages/plugins/plugin-auth/src
- scripts/adr-anchors
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
48 | 54 | | |
49 | 55 | | |
50 | 56 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
255 | 259 | | |
256 | 260 | | |
257 | 261 | | |
| |||
262 | 266 | | |
263 | 267 | | |
264 | 268 | | |
265 | | - | |
| 269 | + | |
266 | 270 | | |
267 | 271 | | |
268 | 272 | | |
| |||
3166 | 3170 | | |
3167 | 3171 | | |
3168 | 3172 | | |
3169 | | - | |
| 3173 | + | |
3170 | 3174 | | |
3171 | 3175 | | |
3172 | | - | |
3173 | | - | |
3174 | | - | |
3175 | | - | |
3176 | | - | |
3177 | | - | |
3178 | | - | |
3179 | | - | |
3180 | | - | |
3181 | | - | |
3182 | | - | |
3183 | | - | |
3184 | | - | |
3185 | | - | |
3186 | | - | |
3187 | | - | |
3188 | | - | |
3189 | | - | |
3190 | | - | |
3191 | | - | |
3192 | | - | |
3193 | | - | |
3194 | | - | |
3195 | | - | |
3196 | | - | |
3197 | | - | |
3198 | | - | |
3199 | | - | |
3200 | | - | |
3201 | | - | |
3202 | | - | |
| 3176 | + | |
| 3177 | + | |
3203 | 3178 | | |
3204 | 3179 | | |
3205 | 3180 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
0 commit comments