Skip to content

Commit 4723519

Browse files
test(plugin-security): share read-only stacks and read each pair once in the three slowest security suites (test budget, slice 21) (#28421)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} --> _Requested by **Tesla** · [project thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoBeGYBWERTvqddUhodNHgAv)_ Part of #23181 (slice 21, `@objectstack/plugin-security`). The card is a series and stays open. Clause-②: no Metadata-protocol: none Test-only diff: `skip-changeset`. Three test files under `packages/plugins/plugin-security/src/`; `@objectstack/plugin-security`'s `files[]` ships `dist`, `README.md` and `CHANGELOG.md` only. ## 维护者速读 - 改了什么:plugin-security 包 3 个测试文件。`delegated-organization-membership` 里只读或被拒的用例改为按姿态共用一个已启动的栈(启动次数约 65 → 21);`shipped-sets-identity-default-row-scope` 每个栈上同一(对象, 角色)的读只做一次,并与后面两个 describe 共用同形的栈;三个文件都在模块顶层先付首次加载,并在第 2 行加 `// test-budget:` 声明。只动测试文件,不改断言。 - 为什么改:卡 #23181 要求单个测试文件不超过 10 s;最新一次定时全量运行(41183072927)里 plugin-security 整包超出套件上限 1.12 倍,这三个文件是包内最慢的:28.95 s、17.95 s、16.46 s。 - 风险与回滚:断言、超时、分层都没动;三个文件共 371 个用例名前后一致、全绿;把成员资格规则消融掉后,改前与改后红的是同一组 38 个用例。回滚即 revert 本 PR。 - 先看:`delegated-organization-membership.test.ts` 的 `shared()` 注释(哪些用例可共用、哪些仍自启栈);`shipped-sets-identity-default-row-scope.test.ts` 的 `served` 记忆化注释。 - 两分钟验证:`pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2 src/delegated-organization-membership.test.ts`,预期 70 个用例全绿,`tests` 耗时约为 main 上的 45%。 - 需要你做的:无,席位落地。 ## What changes **The reading.** The newest full-battery reading is the scheduled CI run 41183072927 (2026-10-31T08:04Z, `a878ff6c26`); its `Test Core` suite-ceiling report puts `@objectstack/plugin-security` over its ceiling (533.23 s against 474.43 s, 1.12x) and names five of its files over 10 s. This slice takes the three slowest: `src/delegated-organization-membership.test.ts` 28.95 s, `src/objects/shipped-sets-identity-default-row-scope.test.ts` 17.95 s, `src/explain-enforce-parity.test.ts` 16.46 s. (The last weekly refresh, run 40275747937, had the first and third at 16.02 s and 13.48 s.) **Where the time went (local profile, vitest's per-test durations).** `delegated-organization-membership` booted a fresh `SecurityPlugin` + `ObjectQL` + SQL driver stack for nearly every case (about 65 boots at about 70 ms warm). `shipped-sets` reads each persona's secured view of every identity object on real stacks (about 1,500 reads); its restored-copies comparison re-read every pair the per-object sweep had already read, and two later describes booted stacks identical to the sweep's first shape. `explain-enforce-parity` was already shared where it can be (one principal rig per posture source, one on-behalf-of rig per posture); what is left is each row's own explain and enforcement work, and the first rig's cold load inside the first row's clock. **`delegated-organization-membership`: shared read-only stacks.** A `shared(posture, seeded)` stack is booted by its first case and reused by every case that only reads or is refused (a refused write stores nothing, and each such case still asserts that through `stored`). A case whose write lands, that brings its own sharing service, or that pins another rule's refusal by what is stored (the no-organization ADR-0123 D2 cell) still boots its own, as does the parity describe, which writes. Boots go from about 65 to 21. **`shipped-sets-identity-default-row-scope`: each pair read once.** `served(object, persona)` is memoised per stack: no case writes after the stack is seeded, so the second read of a pair answers what the first did. Stacks are cached per (shape, set list), so the explain and must-keep describes reuse the first shape's two stacks instead of booting copies (boots 10 to 7). **`explain-enforce-parity`: first load at module scope.** A throwaway RLS rig answers one explain and one find at module scope, so the first row no longer pays the cold paths (AGENTS.md: clocked windows measure behaviour, never loading). The other two files do the same with their own throwaway boot. **Budget headers (ruling record 6181973363, letter B).** All three stay over 10 s on CI by their CI-to-local ratio (about 2.4, 3.0 and 2.5), and each is a real plugin over a real engine and SQL driver asked once per single-cause fixture: one line at line 2, N = the slowest CI reading x 1.25, rounded up to the next 10 s. | file | change | slowest CI reading (s) | budget | |:--|:--|--:|--:| | `src/delegated-organization-membership.test.ts` | shared read-only stacks + module-scope first boot + header | 28.95 | 40 | | `src/objects/shipped-sets-identity-default-row-scope.test.ts` | read-once memo + shared stacks + module-scope first boot + header | 17.95 | 30 | | `src/explain-enforce-parity.test.ts` | module-scope first rig + header | 16.46 | 30 | ## Tests Per file, vitest's own file clock (what the refresh lane reads), 3 interleaved A/B rounds, isolated, `--maxWorkers=2`, each run under the shared verify lock on a shared container; before = the file at `63a9bcb7`, after = `76f4b439`. | file | before r1/r2/r3 (s) | after r1/r2/r3 (s) | |:--|:--|:--| | delegated-organization-membership | 11.94 / 12.94 / 12.21 | 5.02 / 5.50 / 5.24 | | shipped-sets-identity-default-row-scope | 6.54 / 5.75 / 5.77 | 5.29 / 4.84 / 4.62 | | explain-enforce-parity | 6.78 / 6.44 / 6.49 | 7.28 / 5.99 / 5.53 | `explain-enforce-parity`'s gain is small and inside the box's noise on one round: its floor is the rows' own work, which is why it carries a header. Name census over the A/B JSON reports, before vs after: 70 / 137 / 164 test names on both sides, 0 lost, 0 new, 0 with a changed status. Whole package on `76f4b439`: `Test Files 238 passed (238)`, `Tests 5124 passed | 45 skipped (5169)`. <details><summary>Ablation: the shared stacks keep every refusal cell's red</summary> The membership rule removed (`delegatedOrganizationRefusal` in `src/explain-engine.ts` answering no refusal, through `scripts/ablation-replace.mjs`, marker counted on disk, restored with the blob equal to HEAD): `delegated-organization-membership` goes red on 38 of 70 cases with the base file (`63a9bcb7`) and on the same 38 with this head, the identical set of names. A first run of the shared shape read 40: the two no-organization cells went red too, because ablated inserts landed on the shared stack they read; that cell now boots its own stack, so a regression of this rule reds only this rule's cells. </details> <details><summary>Reuse proof for the read-once memo</summary> A temporary wrapper (not committed) re-read each answer fresh beside the memoised one across the whole file: 1,544 pairs compared, 0 diverged, 137 tests passed. </details> <details><summary>Header parse</summary> All three lines parse through `readTestBudget` (`scripts/measure-test-shard-timings.mjs`): budgets 40, 30 and 30 at line 2, class `spawns the real producer once per single-cause fixture`. </details> ## Acceptance notes - The two other plugin-security files over 10 s in run 41183072927 are not in this slice: `src/rls-check-stored-form.test.ts` 15.50 s (each cell writes and reads its own stored row) and `src/objects/default-row-scope-declarations.test.ts` 11.72 s (one case is about 3 s of real reads; sharing its boots would save about 0.3 s). - `explain-enforce-parity`'s on-behalf-of rows cost about 80 to 90 ms each locally against about 20 ms for the others; a temporary probe put the request-context assembly at about 4 ms of that, so the rest is the two delegated legs being evaluated, which is the subject. - Branch `claude/project-thread-avx4e9` is the card thread's platform-assigned branch; claim 6543049660 names it. ## Verification Local, on `76f4b439` (before the main merge): `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 52 families; each run with `</dev/null` and its exit captured by redirect before any pipe. 50 exit 0; `pnpm check:dual-build-cjs-loads` and `pnpm check:i18n` exit 3 (PREREQUISITE NOT MET: packages outside plugin-security's closure have no `dist`, and the CLI is not built), NOT MEASURED here and CI's. `--ran`: `52 derived famil(ies) accounted for — 50 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)`, exit 0. Scoped lint, the root `lint` script's own command over `packages/plugins/plugin-security`: exit 0. `pnpm --filter @objectstack/plugin-security typecheck`: VERDICT command-exit 0 (`check:test-typecheck: OK`, 0 errors held). On `9ff46f191` (`origin/main` merged; it touched spec, rest, runtime and some plugin-security tests, none of these three files): closure rebuilt (19/19), the three files `Test Files 3 passed (3)`, `Tests 371 passed (371)`; typecheck VERDICT command-exit 0; `check:type-check-debt`, `check:test-source-alias`, `check:cross-package-test-inputs`, `check:nul-bytes` exit 0. --- _Generated by [Claude Code](https://claude.ai/code/session_01L9qibxnQNCbvYbRCUqNNvt)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 86dc686 commit 4723519

3 files changed

Lines changed: 117 additions & 30 deletions

File tree

‎packages/plugins/plugin-security/src/delegated-organization-membership.test.ts‎

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 40 — spawns the real producer once per single-cause fixture: a real SecurityPlugin over a real ObjectQL engine and SQL driver, booted per posture and per fixture (a read-only stack each, plus one per case whose write lands or that brings its own sharing service), every admission probe asked on that stack; slowest CI reading 28.95 s (scheduled full-battery run 41183072927, Test Core suite-ceiling report) x 1.25, rounded up
23

34
/**
45
* [#25063 · ADR-0090 D10] A delegated operation runs only in an organization
@@ -279,6 +280,38 @@ async function boot(posture: Posture, options: { sharing?: Record<string, unknow
279280
return { engine, plugin, stored, security };
280281
}
281282

283+
// [#23181] The first boot's first loads (the driver's and the plugin's cold
284+
// paths) are paid here, at module scope, outside every case's clock: clocked
285+
// windows measure behaviour, never loading. Nothing this stack does is read.
286+
await (await boot('group', { lifetime: 'describe' })).engine.destroy();
287+
288+
/**
289+
* [#23181] One stack per posture (and per `seedForReads` or not), booted by
290+
* its first case and shared by every case that only READS or is REFUSED: a
291+
* refused write stores nothing, and each such case still asserts that
292+
* (`stored`), so a shared stack is the stack it would have booted. A case
293+
* whose write lands, that hands `boot` its own sharing service, or that pins
294+
* another rule's refusal by what is stored, still boots its own. Booting one stack per case was most of this file's wall.
295+
*/
296+
const sharedStacks = new Map<string, Promise<Awaited<ReturnType<typeof boot>>>>();
297+
function shared(posture: Posture, seeded = false) {
298+
const key = `${posture}:${seeded ? 'seeded' : 'boot'}`;
299+
let stack = sharedStacks.get(key);
300+
if (!stack) {
301+
stack = boot(posture, { lifetime: 'describe' }).then(async (b) => {
302+
if (seeded) await seedForReads(b.engine);
303+
return b;
304+
});
305+
sharedStacks.set(key, stack);
306+
}
307+
return stack;
308+
}
309+
afterAll(async () => {
310+
for (const stack of sharedStacks.values()) {
311+
try { await (await stack).engine.destroy(); } catch { /* noop */ }
312+
}
313+
});
314+
282315
interface Outcome { ok: boolean; value?: unknown; code?: string; status?: number; message?: string; developerMessage?: string }
283316
const attempt = async (run: () => Promise<unknown>): Promise<Outcome> => {
284317
try {
@@ -306,25 +339,25 @@ describe('[#25063 · ADR-0090 D10] a delegated operation in an organization the
306339
describe(`\`${posture}\``, () => {
307340
for (const object of ['qa_account', 'qa_vault']) {
308341
it(`${object}: a single insert is refused, and nothing is stored`, async () => {
309-
const b = await boot(posture);
342+
const b = await shared(posture);
310343
expectMembershipRefusal(await attempt(() => b.engine.insert(object, { id: 'r1', name: 'one' }, { context: operator(ORG_D) } as never)));
311344
expect(await b.stored(object)).toEqual(SEEDED(object));
312345
});
313346

314347
it(`${object}: an array insert is refused whole, and nothing is stored`, async () => {
315-
const b = await boot(posture);
348+
const b = await shared(posture);
316349
const rows = [{ id: 'r1', name: 'one' }, { id: 'r2', name: 'two' }];
317350
expectMembershipRefusal(await attempt(() => b.engine.insert(object, rows, { context: operator(ORG_D) } as never)));
318351
expect(await b.stored(object)).toEqual(SEEDED(object));
319352
});
320353

321354
it(`${object}: a read is refused`, async () => {
322-
const b = await boot(posture);
355+
const b = await shared(posture);
323356
expectMembershipRefusal(await attempt(() => b.engine.find(object, { context: operator(ORG_D) } as never)));
324357
});
325358

326359
it(`${object}: an update and a delete are refused, and the stored row is unchanged`, async () => {
327-
const b = await boot(posture);
360+
const b = await shared(posture);
328361
expectMembershipRefusal(await attempt(() => b.engine.update(object, { id: `${object}_in_d`, name: 'renamed' }, { context: operator(ORG_D) } as never)));
329362
expectMembershipRefusal(await attempt(() => b.engine.delete(object, { where: { id: `${object}_in_d` }, context: operator(ORG_D) } as never)));
330363
expect(await b.stored(object)).toEqual(SEEDED(object));
@@ -356,6 +389,9 @@ describe('[#25063] what the rule leaves alone', () => {
356389
});
357390

358391
it(`\`${posture}\`: a delegated context with NO active organization keeps ADR-0123 D2's refusal`, async () => {
392+
// Its own stack, not the shared one: the refusal cells above share it, so
393+
// a regression of THIS file's rule would land rows there and red this
394+
// case too, for a rule (ADR-0123 D2) that did not move.
359395
const b = await boot(posture);
360396
const outcome = await attempt(() => b.engine.insert('qa_account', { id: 'r1', name: 'one' }, { context: operator(undefined) } as never));
361397
expect(outcome.ok).toBe(false);
@@ -411,8 +447,7 @@ describe('[#25106 · ADR-0021 D-C] getReadFilter answers the deny sentinel where
411447
for (const posture of ['group', 'isolated'] as const) {
412448
for (const object of PROVIDER_OBJECTS) {
413449
it(`\`${posture}\` · ${object}: a delegated context in an organization the delegator is not a member of answers RLS_DENY_FILTER itself`, async () => {
414-
const b = await boot(posture);
415-
await seedForReads(b.engine);
450+
const b = await shared(posture, true);
416451
expect(await b.plugin.getReadFilter(object, operator(ORG_D))).toBe(RLS_DENY_FILTER);
417452
});
418453
}
@@ -438,16 +473,14 @@ describe('[#25106] what the rule leaves alone in the read scope', () => {
438473

439474
for (const posture of ['group', 'isolated'] as const) {
440475
it(`\`${posture}\`: a delegated context in an organization the delegator belongs to keeps its scope`, async () => {
441-
const b = await boot(posture);
442-
await seedForReads(b.engine);
476+
const b = await shared(posture, true);
443477
for (const object of PROVIDER_OBJECTS) {
444478
expect(await scopeRows(b, object, operator(ORG_A)), object).toEqual(IN_SET[posture][object]);
445479
}
446480
});
447481

448482
it(`\`${posture}\`: the same caller in \`org_d\` WITHOUT the delegation link keeps its scope`, async () => {
449-
const b = await boot(posture);
450-
await seedForReads(b.engine);
483+
const b = await shared(posture, true);
451484
expect(await scopeRows(b, 'qa_account', operator(ORG_D, false))).toEqual(['qa_account_in_d']);
452485
expect(await scopeRows(b, 'qa_vault', operator(ORG_D, false))).toEqual(['qa_vault_in_a', 'qa_vault_in_c', 'qa_vault_in_d']);
453486
expect(await scopeRows(b, 'qa_catalog', operator(ORG_D, false))).toEqual(['qa_catalog_1', 'qa_catalog_2']);
@@ -458,23 +491,21 @@ describe('[#25106] what the rule leaves alone in the read scope', () => {
458491
// organization-less object keeps its rows. The private object is left
459492
// out: the delegator holds its grant only inside an organization, so
460493
// object admission refuses it (`canReadObject`'s half), not this rule.
461-
const b = await boot(posture);
462-
await seedForReads(b.engine);
494+
const b = await shared(posture, true);
463495
expect(await scopeRows(b, 'qa_account', operator(undefined))).toEqual([]);
464496
expect(await scopeRows(b, 'qa_catalog', operator(undefined))).toEqual(['qa_catalog_1', 'qa_catalog_2']);
465497
});
466498

467499
it(`\`${posture}\`: a system context is unscoped`, async () => {
468-
const b = await boot(posture);
500+
const b = await shared(posture);
469501
for (const object of PROVIDER_OBJECTS) {
470502
expect(await b.plugin.getReadFilter(object, { ...SYS_CTX, onBehalfOf: { userId: DELEGATOR } }), object).toBeUndefined();
471503
}
472504
});
473505
}
474506

475507
it('`single`: there is no wall, so a delegated context in `org_d` keeps its scope', async () => {
476-
const b = await boot('single');
477-
await seedForReads(b.engine);
508+
const b = await shared('single', true);
478509
expect(await scopeRows(b, 'qa_account', operator(ORG_D))).toEqual(['qa_account_in_a', 'qa_account_in_c', 'qa_account_in_d']);
479510
expect(await scopeRows(b, 'qa_vault', operator(ORG_D))).toEqual(['qa_vault_in_a', 'qa_vault_in_c', 'qa_vault_in_d']);
480511
expect(await scopeRows(b, 'qa_catalog', operator(ORG_D))).toEqual(['qa_catalog_1', 'qa_catalog_2']);
@@ -484,8 +515,7 @@ describe('[#25106] what the rule leaves alone in the read scope', () => {
484515
describe('[#25106 · ADR-0021 D-C] parity: the read scope agrees with the engine find on the same contexts', () => {
485516
for (const posture of ['group', 'isolated'] as const) {
486517
it(`\`${posture}\`: refused by find ⇔ the deny sentinel; admitted ⇒ the scope admits exactly the rows find returns`, async () => {
487-
const b = await boot(posture);
488-
await seedForReads(b.engine);
518+
const b = await shared(posture, true);
489519
const contexts: Record<string, Record<string, unknown>> = {
490520
'delegated in org_d': operator(ORG_D),
491521
'delegated in org_a': operator(ORG_A),
@@ -546,7 +576,7 @@ describe('[#25107 · ADR-0090 D10] the probes answer the refusal where the prolo
546576
for (const posture of ['group', 'isolated'] as const) {
547577
for (const object of PROVIDER_OBJECTS) {
548578
it(`\`${posture}\` · ${object}: every admission probe is false, every field projection is empty, and the explanation denies`, async () => {
549-
const b = await boot(posture);
579+
const b = await shared(posture);
550580
const answers = await probeAnswers(b, object, operator(ORG_D));
551581
expect(answers.canReadObject, 'canReadObject').toBe(false);
552582
expect(answers.canWriteInsert, 'canWriteObject insert').toBe(false);
@@ -565,7 +595,7 @@ describe('[#25107 · ADR-0090 D10] the probes answer the refusal where the prolo
565595
}
566596

567597
it(`\`${posture}\`: the narrowing diagnostic reports nothing for a delegated read the prologue refuses`, async () => {
568-
const b = await boot(posture);
598+
const b = await shared(posture);
569599
expect(await b.security().describeDelegationNarrowing('qa_ledger', agent(ORG_D))).toEqual({ narrowed: false });
570600
});
571601

@@ -598,28 +628,28 @@ describe('[#25107] what the rule leaves alone in the probes', () => {
598628

599629
for (const posture of ['group', 'isolated'] as const) {
600630
it(`\`${posture}\`: a delegated context in an organization the delegator belongs to is admitted by every probe`, async () => {
601-
const b = await boot(posture);
631+
const b = await shared(posture);
602632
for (const object of PROVIDER_OBJECTS) expectAdmitted(await probeAnswers(b, object, operator(ORG_A)), object);
603633
});
604634

605635
it(`\`${posture}\`: the same caller in \`org_d\` WITHOUT the delegation link is admitted by every probe`, async () => {
606-
const b = await boot(posture);
636+
const b = await shared(posture);
607637
for (const object of PROVIDER_OBJECTS) expectAdmitted(await probeAnswers(b, object, operator(ORG_D, false)), object);
608638
});
609639

610640
it(`\`${posture}\`: a delegated context with NO active organization is not this rule's — the read probes keep their answer`, async () => {
611641
// Its writes are ADR-0123 D2's, and the private object's grant lives
612642
// only inside an organization, so the public objects' reads are the
613643
// cells this rule could have moved and did not.
614-
const b = await boot(posture);
644+
const b = await shared(posture);
615645
for (const object of ['qa_account', 'qa_catalog']) {
616646
expect(await b.plugin.canReadObject(object, operator(undefined)), object).toBe(true);
617647
expect(await b.plugin.getReadableFields(object, operator(undefined)), object).toEqual(expect.arrayContaining(ALL_FIELDS));
618648
}
619649
});
620650

621651
it(`\`${posture}\`: the narrowing diagnostic still reports a narrowed delegated read in an organization the delegator belongs to`, async () => {
622-
const b = await boot(posture);
652+
const b = await shared(posture);
623653
const verdict = await b.security().describeDelegationNarrowing('qa_ledger', agent(ORG_A));
624654
expect(verdict.narrowed).toBe(true);
625655
expect(verdict.effectiveScope).toBe('own');
@@ -641,7 +671,7 @@ describe('[#25107] what the rule leaves alone in the probes', () => {
641671
}
642672

643673
it('`single`: there is no wall, so a delegated context in `org_d` is admitted by every probe and its narrowing is reported', async () => {
644-
const b = await boot('single');
674+
const b = await shared('single');
645675
for (const object of PROVIDER_OBJECTS) expectAdmitted(await probeAnswers(b, object, operator(ORG_D)), object);
646676
expect((await b.security().describeDelegationNarrowing('qa_ledger', agent(ORG_D))).narrowed).toBe(true);
647677
});

‎packages/plugins/plugin-security/src/explain-enforce-parity.test.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 30 — spawns the real producer once per single-cause fixture: each row asks explain and the real enforcement path of a real SecurityPlugin over a real ObjectQL engine and SQL driver, a row that can write on its own rig and the read-only and on-behalf-of rows on one shared rig per posture; slowest CI reading 16.46 s (scheduled full-battery run 41183072927, Test Core suite-ceiling report) x 1.25, rounded up
23

34
/**
45
* `security.explain` answers what enforcement does — every verdict position,
@@ -1422,6 +1423,20 @@ const TABLE: Row[] = [
14221423
}),
14231424
];
14241425

1426+
// [#23181] The first rig's first loads (the driver's, the plugin's and the
1427+
// explain engine's cold paths) are paid here, at module scope, outside every
1428+
// row's clock: clocked windows measure behaviour, never loading. A throwaway
1429+
// RLS rig answers one explain and one find; nothing it answers is read.
1430+
{
1431+
const warm = await bootRls(SAME_CLASS);
1432+
try {
1433+
await warm.explain('read', 'r1');
1434+
await warm.find();
1435+
} finally {
1436+
await warm.teardown();
1437+
}
1438+
}
1439+
14251440
describe('security.explain answers what enforcement does — the enumeration', () => {
14261441
for (const row of TABLE) {
14271442
const title = `${row.card} · ${row.shape} · ${row.position}` +

‎packages/plugins/plugin-security/src/objects/shipped-sets-identity-default-row-scope.test.ts‎

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 30 — spawns the real producer once per single-cause fixture: a real SecurityPlugin over a real ObjectQL engine and SQL driver, one stack per (posture shape, set list), each persona's secured read of every touched identity object made once on it; slowest CI reading 17.95 s (scheduled full-battery run 41183072927, Test Core suite-ceiling report) x 1.25, rounded up
23

34
/**
45
* [ruling of record 6250748677 on #24164, letter E′ item 3 — the follow-up it
@@ -313,7 +314,7 @@ async function boot(shape: Shape, sets: PermissionSet[]) {
313314
{ id: 'ups:p_admin', user_id: 'p_admin', permission_set: 'admin_full_access', organization_id: null },
314315
]);
315316

316-
const served = async (object: string, persona: Persona): Promise<string[] | string> => {
317+
const read = async (object: string, persona: Persona): Promise<string[] | string> => {
317318
try {
318319
const rows = (await engine.find(object, { context: { ...persona } } as never)) as Array<{ id: string }>;
319320
return rows.map((r) => r.id).sort();
@@ -322,6 +323,21 @@ async function boot(shape: Shape, sets: PermissionSet[]) {
322323
return `refused ${err.code} ${err.status}`;
323324
}
324325
};
326+
// [#23181] Each (object, persona) read once per stack. No case writes after
327+
// the seeding above, so a second read of the same pair answers what the first
328+
// did; the pins that read a pair twice (the per-object sweep and the
329+
// restored-copies comparison) read the same verdict, read once.
330+
const reads = new Map<string, Promise<string[] | string>>();
331+
const served = async (object: string, persona: Persona): Promise<string[] | string> => {
332+
const key = `${object} :: ${JSON.stringify(persona)}`;
333+
let answer = reads.get(key);
334+
if (!answer) {
335+
answer = read(object, persona);
336+
reads.set(key, answer);
337+
}
338+
const value = await answer;
339+
return Array.isArray(value) ? [...value] : value;
340+
};
325341
/** The (object, operation) pairs at which `persona`'s collection is an object default. */
326342
const census = async (persona: Persona, objectNames: readonly string[], operations: readonly string[]): Promise<string[]> => {
327343
const held = await (plugin as any).resolvePermissionSetsForContext({ ...persona });
@@ -343,6 +359,32 @@ async function boot(shape: Shape, sets: PermissionSet[]) {
343359
return { engine, security, served, census, collectedNames };
344360
}
345361

362+
// [#23181] The first boot's first loads (the driver's and the plugin's cold
363+
// paths, the first secured read) are paid here, at module scope, outside every
364+
// case's clock: clocked windows measure behaviour, never loading. Nothing this
365+
// stack answers is read; the file's `afterAll` destroys it with the rest.
366+
await (await boot(SHAPES[0], defaultPermissionSets)).served(TOUCHED[0], PERSONAS.member_default);
367+
368+
/**
369+
* [#23181] One stack per (shape, set list), booted by the first describe that
370+
* asks for it and shared by the rest. Every case here only reads (`served`,
371+
* `explain`, the collections), so a describe reads the stack it would have
372+
* booted; the explain and must-keep describes ask the first shape's two stacks
373+
* the per-shape sweep already booted.
374+
*/
375+
const stacks = new Map<string, Promise<Awaited<ReturnType<typeof boot>>>>();
376+
function stackFor(shape: Shape, sets: 'shipped' | 'restored') {
377+
const key = `${shape.label} :: ${sets}`;
378+
let stack = stacks.get(key);
379+
if (!stack) {
380+
stack = sets === 'shipped'
381+
? boot(shape, defaultPermissionSets)
382+
: registeredObjects().then((objects) => boot(shape, withCopiesRestored(objects)));
383+
stacks.set(key, stack);
384+
}
385+
return stack;
386+
}
387+
346388
/** The user whose rows a persona reads as its own: the delegator, for an agent. */
347389
const selfOf = (persona: Persona): string =>
348390
String((persona.onBehalfOf as { userId?: string } | undefined)?.userId ?? persona.userId);
@@ -376,8 +418,8 @@ describe.each(SHAPES)('the shipped sets serve the identity objects as before —
376418
let shipped: Awaited<ReturnType<typeof boot>>;
377419
let restored: Awaited<ReturnType<typeof boot>>;
378420
beforeAll(async () => {
379-
shipped = await boot(shape, defaultPermissionSets);
380-
restored = await boot(shape, withCopiesRestored(await registeredObjects()));
421+
shipped = await stackFor(shape, 'shipped');
422+
restored = await stackFor(shape, 'restored');
381423
});
382424

383425
// One case per object, not one sweep over all of them: the restored-copies
@@ -418,8 +460,8 @@ describe('explain names the object default where a dropped copy used to be the r
418460
let shipped: Awaited<ReturnType<typeof boot>>;
419461
let restored: Awaited<ReturnType<typeof boot>>;
420462
beforeAll(async () => {
421-
shipped = await boot(shape, defaultPermissionSets);
422-
restored = await boot(shape, withCopiesRestored(await registeredObjects()));
463+
shipped = await stackFor(shape, 'shipped');
464+
restored = await stackFor(shape, 'restored');
423465
});
424466
type RlsLayer = { layer: string; verdict: string; detail: string; record?: { rules: Array<Record<string, unknown>> } };
425467
const rlsLayer = (decision: { layers: RlsLayer[] }): RlsLayer => {
@@ -472,7 +514,7 @@ describe('explain names the object default where a dropped copy used to be the r
472514
describe('what the sets keep (pointer 6280763190, must-keep; the ledger, measured)', () => {
473515
let walled: Awaited<ReturnType<typeof boot>>;
474516
beforeAll(async () => {
475-
walled = await boot(SHAPES[0], defaultPermissionSets);
517+
walled = await stackFor(SHAPES[0], 'shipped');
476518
});
477519

478520
it('a member\'s write collection on each kept `all` object carries the set\'s self scope beside the ownership floor, never the default', async () => {

0 commit comments

Comments
 (0)