Repository navigation
Commit f1e21c4
Refs #23184 (plugin-auth rows: scim-deactivation-reconcile-user,
last-admin-guard)
Clause-②: no
## Rows answered
Two rows of the card's `@objectstack/plugin-auth` list:
- `src/scim-deactivation-reconcile-user.test.ts`, listed at 35.3 s (run
`38267594064`, 14 of 14 window runs over budget);
- `src/last-admin-guard.test.ts`, listed at 10.9 s (run `38222448090`).
The pin is the next refresh PR's list: it should name neither file. The
card stays open for its other rows: the other three `plugin-auth` rows
(org-create-posture-gate, implicit-account-linking,
dev-admin-seed-credential-gate) and the `plugin-security`,
`service-datasource` and `organizations` rows.
The diff is tests only: two test files and one new test-only helper. No
production file, option, export or published byte changes. The helper's
symbol has 0 hits in `packages/plugins/plugin-auth/dist` (the positive
control `registerLastAdminGuard` has 4), and the package ships only
`dist`, `README.md` and `CHANGELOG.md`. Every `it(`, `describe(` and
`expect(` line in both files is byte-identical to the base and in the
same order.
## Where the time went, and what changed
### `scim-deactivation-reconcile-user.test.ts`: 18.7 s to 2.2 s local
A probe at the base (temporary, deleted) broke the file's wall down. Two
real-time 5.5 s ban-expiry waits took 11.0 s. Production-parameter
scrypt took 3.3 s over 29 derivations (10 hashes and 19 sign-in
verifies). The schema sync took about 0.9 s (12 boots of 32 objects,
about 80 ms each). Vendor module evaluation inside the first case took
about 0.8 s. Requests took the rest.
1. **The two timed-ban faces move the clock instead of sleeping.** The
expiry is judged by better-auth's admin plugin in its `session.create`
hook as `banExpires` before `Date.now()`, which is the global clock. So
`passTime(ms)` fakes `Date` alone (`vi.useFakeTimers({ toFake: ['Date'],
shouldAdvanceTime: true })`) and sets it `TIMED_BAN_WAIT_MS` ahead.
Timers stay real, the clock keeps running from there, and an `afterEach`
restores the real clock. `TIMED_BAN_MS` and `TIMED_BAN_WAIT_MS` are
unchanged. The pre-PATCH sign-in still runs on the real clock, so its
race margin is the same as before. No production constant changes.
2. **The scrypt memo from the previous slice**
(`src/__tests__/scrypt-memo.testkit.ts`) answers each sign-in verify:
same password, same salt, same parameters.
3. **One password hash per file.** The same stored value is written into
each case's own database. Every manager in the file is built with the
same options and the vendor's default hasher, so only the random salt is
shared, and no case reads it.
4. **Vendor modules load at module top** (the list the SCIM,
organization and admin managers boot through), following the AGENTS.md
rule that clocked windows measure behaviour, never loading.
5. **The engine comes from a per-file pool** (below). The `AuthManager`
and the SCIM credential are still built fresh in every case.
### `last-admin-guard.test.ts`: 2.4 s to 1.6 s local
This file has no scrypt and no waits. It ran 115 engine boots per run
(init plus a schema sync), about 1.05 s of its wall. 90 of them are
`boot()` with no options, and those now come from the pool. The 25 boots
with options (`readThrough`, `maxScan`, `withIdentityWriteGuard`,
`unguarded`) still boot fresh per call, because their hooks differ.
### The pool: `src/__tests__/sqlite-engine-pool.testkit.ts` (new,
test-only)
The pool keeps one booted sqlite `ObjectQL` engine per file. Before
handing an engine out again, it deletes every row of every table in one
transaction, then runs the suite's own `onReset` (`last-admin-guard`
empties its catalog set there). `take()` hands out an engine no case
holds, or boots another, so a case that takes two engines gets two.
`release()` runs in `afterEach` and `destroy()` in `afterAll`.
A raw row reset cannot undo some engine state, and the pool refuses
those cases loudly:
- an object declaring an `autonumber` field is refused at the first
boot, because the engine keeps the counter in memory;
- a database holding a `sys_organization` row is refused, because the
engine memoizes the install's organization set until an engine write to
that object invalidates it;
- a schema a case created or dropped a table in is refused.
## Measurements
Each figure is vitest's per-file duration (JSON reporter `endTime -
startTime`): `pnpm exec vitest run --maxWorkers=2 FILE
--reporter=verbose --reporter=json`, in `packages/plugins/plugin-auth`,
under `os-verify-lock`. Runs were on a shared 4-vCPU box: another dev's
`check:*` gates ran unlocked beside them, and the load average is given
for each set. The before and after runs at the final head were
**interleaved**, with the before leg being the base file copied to a
temporary name (deleted afterwards), so both sides saw the same load.
| file | before, base `c24598658d` alone (load 2.4) | before,
interleaved at `3545b9c012` (load 2.5 to 2.9) | after, interleaved at
`3545b9c012` | median after |
|:--|:--|:--|:--|--:|
| scim-deactivation-reconcile-user | 18884 / 18449 / 17830 ms | 18442 /
18689 / 19511 ms | 2179 / 2816 / 2118 ms | **2.18 s** |
| last-admin-guard | 2540 / 2635 / 2542 ms | 2351 / 2150 / 2432 ms |
1453 / 1573 / 1660 ms | **1.57 s** |
**CI projection.** The per-file CI captures of recent hourly runs are
NOT MEASURED: `test-core-timings-N-of-6`, the timing-table artifact and
the job logs are all served from
`productionresultssa16.blob.core.windows.net`, which this container's
egress proxy refuses (CONNECT 403). The check-run annotations carry no
per-file line. The ratios below therefore come from the card's own
readings:
- scim: the 35.34 s worst run includes the two waits, 11.0 s of wall
that does not scale with load. Its compute was 24.3 s in CI against 7.7
s locally, a ratio of 3.2. At that ratio the file projects to **7.0 s**.
At 4.0, the worst post-landing ratio the previous slice read for a
concurrent shape, it projects to 8.7 s.
- last-admin-guard: 10.9 s against 2.35 s locally is a ratio of 4.6. At
that ratio the file projects to **7.3 s**.
## Proof that every case still runs and still asserts the same
- **Case lists:** the sorted fullName and status of every case match the
base in every after run: 13 of 13 passed, and 110 of 110 passed.
- **Static lines:** the `it(` / `describe(` / `expect(` lines are
byte-identical to the base and in order. The counts are 13 / 6 / 83 for
scim and 110 / 23 / 277 for last-admin-guard.
- **Runtime assertions:** a temporary copy of each file recorded
`expect.getState().assertionCalls` per case, at the base and at the
head. The per-case counts are identical: 146 assertions over 13 cases,
and 283 over 110 cases. No case asserts nothing.
- **The moved clock reaches the vendor (mutation, restored):**
`scripts/ablation-replace.mjs` removed the clock move (and its
`Date.now()` line) from the positive control only. The control turned
red at `expectSignInAccepted` with `403 BANNED_USER`, and the other 12
cases stayed green. The restore was verified (blob `0fbcf875db` equals
HEAD, and `git diff HEAD` is empty). So the unmutated control is
admitted only because the hook read the moved clock, which is the same
evidence that makes the main face's refusal mean something.
- **Pool equivalence (temporary probe, deleted):** the probe
fingerprinted every engine `take()` handed out, covering per-table row
counts, hook counts per event, middlewares, registered objects, the
organization memo, the autonumber counters, the deviation, attestation
and violation sets, both migration-flag memos, unavailable datasources
and the catalog size. It compared each against a freshly booted engine.
last-admin-guard made 90 takes (2 boots, 88 resets) and scim made 12 (1
boot, 11 resets). Every take matched the fresh fingerprint.
- **Order independence:** both files ran with `--sequence.shuffle.tests`
under seeds 11, 2026 and 23184. Every run passed 123 of 123 tests.
- **The pool's refusals fire (temporary probe, deleted):** an
organization row, a table created by a case and an autonumber field were
each refused with the stated message. Reuse, two takes in one case, and
a full emptying each behaved as described (5 of 5).
## Tests and gates (final head `3545b9c012`)
- `pnpm --filter @objectstack/plugin-auth run typecheck`: VERDICT
command-exit 0. `check:test-typecheck` is OK with the debt ledger
unchanged (10 files / 88 errors / 23 signatures). `tsc --listFilesOnly`:
the 3 changed files are in `tsconfig.test.json`.
- The whole `@objectstack/plugin-auth` suite, all 146 files in 6 chunks
with `--maxWorkers=2`: 146 of 146 files passed, with 2861 tests passed
and 10 skipped (the skips predate this PR). Every chunk exited 0.
- ESLint on the 3 changed files with `--no-inline-config`: exit 0.
`check-nul-bytes`: exit 0, and a control-byte self-scan of the 3 files
found 0 hits.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 53 commands, the same 53 as the
seat's lead. All 53 exit 0, as do the 4 roster families that sit under
these paths (`check:auth-mount-ledger`, `check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`).
`check:dual-build-cjs-loads` first answered PREREQUISITE NOT MET (exit
3) and exited 0 after a full `turbo run build` (75 of 75 tasks). `--ran`
reports 53 derived, 53 run, 0 NOT-MEASURED and 0 UNRUN.
- `check:engine-double-contract` printed its repo-wide census line
verbatim: `UNRECOGNISED [engine-double-contract]: 22 construct(s) in
packages, examples declare a scanned verb (delete, update, findOne)
alongside engine siblings, and this gate could not read the
implementation -- so they are in NEITHER the pinned population nor the
ledger.` None of the 22 is in a file this PR touches.
- The full lint farm, Test Core, the dogfood jobs and the type-check
lanes belong to CI.
## Acceptance notes
- Each of these files also spends about 15 to 20 s in vitest import and
transform on this box, before and after this change. That time falls
outside the per-file reading the budget grades. This is an observation,
not a defect.
- The per-file CI captures (`test-core-timings-N-of-6`) cannot be read
from a cloud container (blob-storage egress is refused), so a seat
measuring a test-budget row there reads only the card's numbers and the
next refresh. This is an environment fact, not a repo defect.
- The pool is opt-in per file and pools only one boot shape. Other
suites in this package that boot a real sqlite engine per case could
adopt it, each with its own equivalence check. This PR does not touch
them.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent f77ba41 commit f1e21c4
3 files changed
Lines changed: 318 additions & 48 deletions
File tree
- packages/plugins/plugin-auth/src
- __tests__
| 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 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| 66 | + | |
66 | 67 | | |
67 | 68 | | |
68 | 69 | | |
| |||
246 | 247 | | |
247 | 248 | | |
248 | 249 | | |
| 250 | + | |
249 | 251 | | |
250 | 252 | | |
251 | 253 | | |
| |||
255 | 257 | | |
256 | 258 | | |
257 | 259 | | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
258 | 274 | | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
259 | 280 | | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
269 | 288 | | |
270 | 289 | | |
271 | 290 | | |
| |||
288 | 307 | | |
289 | 308 | | |
290 | 309 | | |
291 | | - | |
| 310 | + | |
292 | 311 | | |
293 | 312 | | |
294 | 313 | | |
| |||
0 commit comments