Repository navigation
perf(core): read the Default Organization once per engine, not on every authenticated request - #24627
Conversation
Claude-Session: https://claude.ai/code/session_01Ug7sGD6qWVrzQNdihVX3T6 Co-authored-by: Claude <noreply@anthropic.com>
… and the statements golden Claude-Session: https://claude.ai/code/session_01Ug7sGD6qWVrzQNdihVX3T6 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ug7sGD6qWVrzQNdihVX3T6 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 18 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 27 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a8d6a7bb1fbfc66271539f98b8c3912442c1cdb9 && git checkout a8d6a7bb1fbfc66271539f98b8c3912442c1cdb9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 973917c5795f7a440d46a15bc5cf8961af9d8703 9fda5d6b1cb80748bd6ff1b433447cfd5294db85 && git checkout -B drift-repro 973917c5795f7a440d46a15bc5cf8961af9d8703 && git merge --no-ff 9fda5d6b1cb80748bd6ff1b433447cfd5294db85
node scripts/docs-audit/affected-docs.mjs --json 973917c5795f7a440d46a15bc5cf8961af9d8703
|
Contract reviewServed-tier: Rendered on the head the brief named; the PR's Check-runs on the head: 40, all ① Derived judgments
② Semver level
The changeset text does not hold sentence by sentence. Every sentence holds except this one: "A peer replica's write is seen through the The route: replace that sentence with one that is true, for example: "With the ③ Boundary flags
Implemented-by: VERDICT: FAIL Seat note, appended at posting (2026-10-19T18:16Z). It is not part of the record.
|
… the Default Organization memo Claude-Session: https://claude.ai/code/session_01Ug7sGD6qWVrzQNdihVX3T6 Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs:
There is no merge of ① Derived judgmentsWhat moved.
The new text is true.
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 ·
|
| Item | Reading |
|---|---|
| Shape | Draft PR → main. The body opens Fixes #24523. 4 files, +677/−25. Not governed: check-governed-merges matched 0 of 4 paths. |
| Scope | readDefaultOrganizationId is memoised per engine (memoisedDefaultOrganizationId). Retired by: any sys_organization insert, update or delete through the engine (leg 5's seam, generalised as watchedWriteState), and every non-write epoch bump. Bound: a 30 s residual bound. Not memoised: a failed read, and an unregistered object. undefined is memoised. Measured: an organization-owned grant now costs get-session 8 and a data read 10, the same as an organization-less one, with 0 sys_organization statements warm. |
| Seat's own read | The fix commit changes prose only: one changeset sentence and comment lines; no code token moved. The default-composition posture checks out: the authz.invalidated bridge is not attached at a grants-cache TTL of 0. The PR body was patched to match (the "Across replicas" bullet, and the residual bound's default case). |
| Review | CONTRACT_REVIEW_TIER: record 6274104341 was FAIL on ② only (the changeset's false peer-replica sentence). The delta 6274924170 is PASS on this head. |
| Declaration | Clause-②: no on the claim, the PR body and the changeset (@objectstack/core patch). No export, public member or env knob is added. |
| Cross-lane | epic:#15194: territory declaration 6271107528, unanswered. The C8 regions are byte-identical (record ①). The thread has no comment after the declaration and no objection, which meets the declaration's own condition. domain:cli: the new runtime test file is declared at 6274991790 (#6024) and 6275003548 (#22648), posted just before this ACCEPT. No objection on any of them, or on the card. |
| Gates | Dev, at 307260452: 67 derived, 67 run, 67 exit 0. Core test:repo 82/82. Full suites passed: core, plugin-security, plugin-auth, rest and runtime. The fix round re-ran the three changeset gates, the prose gates, the core typecheck and the pin files, all exit 0. |
| CI | At this read on 9fda5d6b1c: ci-failure GREEN, all 40 check-runs completed, none failed. |
Disposition:
open_questions: all five accepted as A, per record 6274104341 ③: the 30 s backstop;undefinedmemoised; the pre-registration window accepted; leg 5's seam generalised; the epic question waited out, as above.- The record's escalation (a boot-time posture line for this memo): not owed by this card, per the seat note on 6274104341. The changeset now states the default posture.
deviations: all accepted (records 6274104341 ③ and 6274924170 ③).out_of_scope_findings: none filed.plugin-auth's own copy runs only on guarded writes (record ③).
Downstream: the repo:cloud#2 seat is pointed at the landing for cloud#3053's +2 statements.
Landing: ordinary queue (pr_ready + automerge_enable through the relay). Fixes #24523 then closes the card.
Generated by Claude Code
Fixes #24523
Clause-②: no (a per-engine memo of an unchanged read: no accept set, standing verdict or served body moves; owes the seat's review of record before the queue, at
CONTRACT_REVIEW_TIERbecause it is the platform-standing anchor)Dispatched by the
domain:engineseat 1 PM (session_01JtSQQ3EWsbNPzyYMug1tXy, claim 6271076320). Dev session:session_01Ug7sGD6qWVrzQNdihVX3T6.⛔ Landing waits on the
epic:#15194seat's answer to the territory declaration 6271107528, or an objection-free read of that thread, and on the seat'sCONTRACT_REVIEW_TIERreview of record. No answer was posted when this PR opened.What changed
resolveUserAuthzGrantsasks for the Default Organization whenever a principal holds an organization-ownedadmin_full_accessgrant undersingle.readDefaultOrganizationIdanswers with onesys_organizationread by slug, plus a second read when the only organization is not sluggeddefault. Nothing carried that answer across requests, so every such request paid one or two reads.This PR keeps the answer per engine, across requests (
memoisedDefaultOrganizationId,packages/core/src/security/resolve-authz-context.ts). The call site inresolveUserAuthzGrantsis the only caller changed.sys_organizationwrite through the engine (insert, update, delete, a slug move included) retires the answer. The write is observed after it settles, by Authenticated data requests issue ~20 DB queries in ~17 sequential legs — per-request auth/session/localization/metadata resolution has no cross-request caching, costing ~1.5s/request on remote Postgres #10757 leg 5's write seam: a per-engineWeakMap, a watched-object set (sys_organization), and a generation read before the reads and checked on the next hit. A write that lands while the read is in flight leaves that answer dead on arrival.sys_organizationwrite arrives only when theauthz.invalidatedbridge is attached, which needs the grants cache on (OS_AUTHZ_GRANTS_CACHE_TTL_MSabove 0) and a cross-process cluster driver. It arrives as aremoteepoch bump. That bump, and every other non-writebump (metadata, which is plugin-security's local declaration bump, andmanual), retires the answer. On the shipped default, no bump carries a peer's write.DEFAULT_ORGANIZATION_MEMO_TTL_MS, a constant, no new knob). It is never the in-process rule. It covers only what no seam can see:AuthzStoreUnavailableError,SERVICE_UNAVAILABLE/ 503) memoises nothing and propagates. The next request reads again.undefinedis memoised. No organization, or several with none sluggeddefault, is a real answer, and a write retires it like any other. A table that was never provisioned reads as zero organizations, which is the true answer there.registerMiddleware(every recording double);sys_organization. "Not registered" is a verdict the boot can still contradict, and registering the object writes no row the seam would see.One copy of the seam.
localizationWriteState's body is nowwatchedWriteState(ql, states, watched). Leg 5's localization cache and this memo each call it with their ownWeakMapand watched set. The localization behaviour is unchanged, and its suite (resolve-localization-cache.test.ts) passes unedited. This touches leg 5's function, which is outside the claim's file-surface list but in the same file. Two copies of the seam would be the drift shape.⛔ Byte-identical:
readDefaultOrganizationId(both reads, filters and limits),grantAnchorsPlatformStanding,grantOrganizationOfandgrantAppliesInTenantare not edited. The diff touches none of their lines. Unit pins assert the two reads byte-for-byte wherever they are issued.Measurement (M, a booted composition)
The composition is
default-organization-boot-invariant.pin.test.ts's plugin set withobjectstack dev's seeded administrator, on sqlite-wasm:memory:. The administrator's first-user promotion grant is owned by the Default Organization. Each count is the number of statements the SQL driver issued inside the request's own async context (knexqueryevents), warm (after two requests).main5a871e4e41get-session, organization-owned grant, slugdefaultGET /data/pin_note?$top=1), organization-owned, slugdefaultget-session, organization-owned, sole organization sluggedacmeacmeget-session, organization-less grant (either slug)The
sys_organizationstatements per warm request go from 1 (slugdefault) or 2 (acme) to 0. After asys_organizationwrite, the next request reads it once.Controls, byte-identical: the resolved authz envelope (positions, permissions,
org_user_ids,accessible_org_ids, posture, tab permissions) on a booted composition. Three personas: the administrator, a member, and a user with no organization. Each was resolved with no tenant and in the Default Organization, twice each, for 12 envelopes. The run used the resolver atBASE(5a871e4e41, blob4029c98d…) and atHEAD(bloba4c2ec74…), with random ids normalized. All 12 are deep-equal: canonical md53fd8be5122375762cf9d49f28bae84e8on both. The raw byte difference is only key order, from per-boot random user ids. This was a one-off scratch file, deleted, and the restore was checked blob-equal toHEAD.Pins
packages/runtime/src/default-organization-statements.golden.test.ts(new, 6 cases, a real kernel):sys_organizationstatement, for both slugs);get-sessionreads the new id (tenancy: the cached default organization id is never revalidated, so after a deleted default organization is recreated new users are bound to the old id #21868);defaultmoved to another organization: the old organization's grant stops anchoring, and the new one's starts.packages/core/src/security/resolve-authz-context.default-organization-memo.test.ts(new, 14 cases). Each case drivesresolveUserAuthzGrantson a recording double that carries the engine's seams. Covered:undefinedmemoised;remoteandmetadataretire it, a barewritebump does not;SERVICE_UNAVAILABLE/ 503, then a re-read);Ablations (each through
scripts/ablation-replace.mjs, restore proven blob ==HEAD,git diff HEADempty)Runtime resolves
@objectstack/coreto source through its vitest alias, and core's tests import it relatively, so nodist/leg applies. The alias was also observed directly: the counts moved with no core rebuild.readDefaultOrganizationId): golden red, 4 of 6. It reads 9 / 11 with 1 organization statement, and 10 / 12 with 2. Both retirement cases go red onorganizationReads: 0.standing: false, organizationReads: 0, and so does the slug move. Unit suite red, 4 of 14..catch(() => undefined): the outage pin is red (expected undefined to match { code: 'SERVICE_UNAVAILABLE', status: 503 });reads: [],standing: false).Verification (final head
307260452,origin/mainc03b17d78merged)pnpm --filter @objectstack/core test: 107 files, 2594 tests passed.pnpm --filter @objectstack/core test:repo: 6 files, 82 tests passed. No new caller of the reference-target census.pnpm --filter @objectstack/plugin-security test: 188 files, 3754 passed, 45 skipped.pnpm --filter @objectstack/plugin-auth test: 165 files, 3165 passed, 10 skipped.pnpm --filter @objectstack/rest test: 300 files, 6095 passed, 326 skipped.pnpm --filter @objectstack/runtime test: 423 files, 6651 passed, 19 skipped.pnpm --filter @objectstack/core typecheckexit 0 andpnpm --filter @objectstack/runtime typecheckexit 0. Both test layers compile undertsconfig.test.jsonwith no new debt.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, at307260452. All 67 were run, each exit 0, captured before any pipe.--ranreports: "67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN" (a derived zero).check:dual-build-cjs-loadsexited 3 on the first pass, before a full build. Afterturbo run build(76 tasks) it exits 0.Acceptance notes
readDefaultOrganizationId, the{ slug: 'default' }read,grantAnchorsPlatformStanding, and the*admin-standing*,*batch-equivalence*,*request-rounds*and*default-organization*tests. None moves: their doubles carry no write seam, so the memo declines and their read multisets hold.resolve-authz-context.request-grants-memo.test.tsandresolve-user-grants-cache.test.tscarry the seams but hold no organization-owned admin grant, and both pass unedited.readDefaultOrganizationId(last-admin-guard.ts) is untouched. It runs on guarded writes (anchorOwnerTest), not on every authenticated request, so it does not pay this per-request cost.Generated by Claude Code