Repository navigation
test(organizations): the two upload-wall suites run under 10 s (two #23184 rows) - #23721
Conversation
… load at module scope Both storage-upload wall files boot the same walled stack in beforeAll, and the first bootStack in a process is 2.3 to 2.8 s slower than every later one: lazily imported drivers and better-auth entries, sql.js's WASM compile. A new test-only helper boots a throwaway app on the same composition, under the same two environment variables, at module scope, so that cost is paid during collection rather than in the clocked hook. Claude-Session: https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ Co-authored-by: Claude <noreply@anthropic.com>
…nt back bootStack sets NODE_ENV itself and never restores it. The warm-up now snapshots the environment before its boot and restores all of it after the stop, so each file's first hook starts from the environment it started from before: measured, every variable equal at the first hook, base copy against head. Claude-Session: https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ Co-authored-by: Claude <noreply@anthropic.com>
…st-budget-organizations
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对
Verdict: ACCEPT at head
Generated by Claude Code |
Refs #23184 (organizations rows: storage-upload-organization-change, storage-upload-door-ownership)
Clause-②: no
Rows answered
Both rows of the card's
@objectstack/organizationslist:src/storage-upload-organization-change.wall.test.ts, listed at 15.9 s (run38222448090);src/storage-upload-door-ownership.wall.test.ts, listed at 15.2 s (run38222448090).The pin is the next refresh PR's list: it should name neither file. The card stays open for its other rows: the three remaining
plugin-authrows (org-create-posture-gate, implicit-account-linking, dev-admin-seed-credential-gate) and the twoplugin-securityrows.The diff is tests only, +116 / -0: one new test-only helper,
src/__tests__/upload-wall-warm-up.testkit.ts, and 7 added lines in each of the two files (an import, a 4-line comment and one module-scope call). No production file, option, constant, timeout or export changes. Everyit(,describe(,expect(,beforeAll(andafterAll(line in both files is byte-identical to the base and in the same order.Which number this PR reads
The graded number is the file's module line in CI's
test-core.log: vitest's default reporter, hooks plus cases, collection excluded. Earlier slices of this card read the JSON reporter'sendTime - startTimeas the same number. For these two files it is not: that span covers the cases only, and most of these files' wall sits inbeforeAll. At the base, the JSON span read 1.36 to 1.39 s and 0.47 to 0.59 s, while the module line read 5.1 to 6.4 s and 4.4 to 4.9 s. Every figure below is the module line.Where the time went
Both files boot the same walled stack in
beforeAll:bootStackwith this package'sOrganizationsPluginand the realStorageServicePlugin, underOS_TENANCY_POSTURE=isolatedandOS_AUTH_MEMBERSHIP_POLICY=invite-only. A probe at the base (a temporary file, deleted; one run per shape) repeated each file's arrangement three times in one process and timed every step:bootStacksignInget-session, sign-ups, invitation, organizations,set-active)So the first boot in a process costs 2.3 to 2.8 s more than every later one, and the difference is first use, not the boot. The default datasource's driver and better-auth's entries are
import()ed lazily from inside the boot, sql.js compiles its WASM, and every path runs cold. The file'sbeforeAllwas the first boot in its process, so it paid all of that inside a clocked hook. The cases themselves were about 1.1 to 1.4 s (organization-change) and 0.47 to 0.59 s (door-ownership) of real HTTP round trips, and neither file waits on a timer.What changed
Each file now calls
await warmUpUploadWallBoot()once at module scope, so the first load is paid during collection. This follows the AGENTS.md convention that clocked windows measure behaviour, never loading. The helper is this package's copy of@objectstack/verify'swarmUpBoot(packages/verify/src/boot-warm-up.testkit.ts, landed in72329942f9as the@objectstack/verifyslice of a sibling test-budget card; that package does not export it):bootStacksetsNODE_ENVitself before it loads anything, so a module that reads its environment once at first load reads the same values as at the base.The files' own
beforeAllstill boots, signs in and arranges exactly as before, inside the clock.Measurements
Each figure is the vitest module line:
pnpm exec vitest run --maxWorkers=2 FILE --reporter=default --reporter=json, inpackages/plugins/organizations, underos-verify-lock, on a shared 4-vCPU box. In the final rounds, before and after were interleaved at the final head, with the before leg being the base file copied to a temporary name (deleted afterwards), so both legs saw the same load.43d75ef274alone (load 0.8 to 1.6)56e42d33bf(load 1.1 to 1.9)56e42d33bfWhat remains in the clock, from a probe of the first cut
e4409fcac0(temporary copies, deleted):beforeAll.The load is moved, not removed. vitest's whole-file
Duration(collection plus run) went from 27.1 to 29.3 s before to 27.6 to 31.6 s after in the final rounds. Theimportphase grew from 22.8 to 24.3 s to 25.5 to 28.7 s. The extra second is the throwaway boot's own work. What the change buys is that no hook or case clock contains a cold load any more. It does not make the package's test job faster.CI projection. The per-file CI captures of recent hourly runs are NOT MEASURED: this container has no authenticated read of the timing artifacts, and the card's two previous slices measured that their blob-storage redirect is refused here. The ratios therefore come from the card's own readings:
All projections are under the 10 s line. Organization-change has the smaller margin. Its remaining wall is one warm boot plus ten cases of real requests against one uploader, which no further test-side change shortens without changing what the file measures.
Considered and not done:
plugin-authslices. Only the seeded admin's sign-in would hit it, because every sign-up draws a fresh salt. That saves about 0.1 s per file, which is not worth patching a builtin.describe.concurrent. The organization-change cases share one uploader whose active organization is session state, so they cannot overlap. The door-ownership cases total about 0.5 s.Proof that every case still runs and still asserts the same
it(/describe(/expect(/beforeAll(-afterAll(lines are byte-identical to the base and in order. Organization-change has 10 / 5 / 59 / 2 and door-ownership has 10 / 4 / 44 / 2. The diff to each file is 7 added lines and nothing removed.expect.getState().assertionCallsper case. The per-case counts are identical: 127 assertions over 10 cases for organization-change, and 86 over 10 for door-ownership. No case asserts nothing.beforeAll, base and head read the same values. That covers the row count and column set ofsys_user,sys_account,sys_session,sys_organization,sys_member,sys_invitation,sys_fileandsys_upload_session, thetenancyservice'sposture/isolationActive/degraded, andNODE_ENV,OS_TENANCY_POSTURE,OS_AUTH_MEMBERSHIP_POLICYandOS_PLATFORM_OWNER_EMAILafter the boot.Tests and gates (final head
56e42d33bf)pnpm --filter @objectstack/organizations run typecheck: exit 0.check:test-typecheckis OK, with 0 files in the debt ledger.tsc --listFilesOnlylists all 3 changed files intsconfig.test.json, and the helper is also intsconfig.json.@objectstack/organizationssuite with--maxWorkers=2: 13 of 13 files, 161 of 161 tests passed.--no-inline-config: exit 0. The control-byte scan of the 3 files found 0 hits, andcheck:nul-bytesis OK.src/__tests__/, whichcheck:undeclared-dep-importsreads as test code, so its imports of the devDependencies@objectstack/verifyand@objectstack/service-storageare legal there.skip-changeset: the package shipsdist,README.mdandCHANGELOG.md. The helper's name has 0 hits in the builtdist, and the positive controlOrganizationsPluginhas 23.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 53 commands, the same 53 command lines as the seat's lead. All 53 exit 0 at56e42d33bf, each run separately with the exit code captured before any pipe, and--ranreports 53 derived, 53 run, 0 NOT-MEASURED and 0 UNRUN.check:dual-build-cjs-loadsfirst answered exit 3 (PREREQUISITE NOT MET, an unbuilt workspace) and exit 0 after a fullturbo run build(75 of 75 tasks). No gate's output names any of the 3 changed files.check:engine-double-contractprinted 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.This diff adds no construct.Acceptance notes
beforeAll, the JSON reporter's per-fileendTime - startTimereads several times lower than the module linereport-test-timings.mjscaptures, because it spans the cases only. This is an observation for later slices' measurements, not a defect: the CI capture reads the module line, and that is the right number. No carrier is named.bootStack:walled-default-org-owner-bind-in-flight.pin.test.ts. It is not on the card's list, and it is not measured here. Noted, not filed.Generated by Claude Code