Repository navigation
Commit 4723519
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
- objects
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
279 | 280 | | |
280 | 281 | | |
281 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
282 | 315 | | |
283 | 316 | | |
284 | 317 | | |
| |||
306 | 339 | | |
307 | 340 | | |
308 | 341 | | |
309 | | - | |
| 342 | + | |
310 | 343 | | |
311 | 344 | | |
312 | 345 | | |
313 | 346 | | |
314 | 347 | | |
315 | | - | |
| 348 | + | |
316 | 349 | | |
317 | 350 | | |
318 | 351 | | |
319 | 352 | | |
320 | 353 | | |
321 | 354 | | |
322 | | - | |
| 355 | + | |
323 | 356 | | |
324 | 357 | | |
325 | 358 | | |
326 | 359 | | |
327 | | - | |
| 360 | + | |
328 | 361 | | |
329 | 362 | | |
330 | 363 | | |
| |||
356 | 389 | | |
357 | 390 | | |
358 | 391 | | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
359 | 395 | | |
360 | 396 | | |
361 | 397 | | |
| |||
411 | 447 | | |
412 | 448 | | |
413 | 449 | | |
414 | | - | |
415 | | - | |
| 450 | + | |
416 | 451 | | |
417 | 452 | | |
418 | 453 | | |
| |||
438 | 473 | | |
439 | 474 | | |
440 | 475 | | |
441 | | - | |
442 | | - | |
| 476 | + | |
443 | 477 | | |
444 | 478 | | |
445 | 479 | | |
446 | 480 | | |
447 | 481 | | |
448 | 482 | | |
449 | | - | |
450 | | - | |
| 483 | + | |
451 | 484 | | |
452 | 485 | | |
453 | 486 | | |
| |||
458 | 491 | | |
459 | 492 | | |
460 | 493 | | |
461 | | - | |
462 | | - | |
| 494 | + | |
463 | 495 | | |
464 | 496 | | |
465 | 497 | | |
466 | 498 | | |
467 | 499 | | |
468 | | - | |
| 500 | + | |
469 | 501 | | |
470 | 502 | | |
471 | 503 | | |
472 | 504 | | |
473 | 505 | | |
474 | 506 | | |
475 | 507 | | |
476 | | - | |
477 | | - | |
| 508 | + | |
478 | 509 | | |
479 | 510 | | |
480 | 511 | | |
| |||
484 | 515 | | |
485 | 516 | | |
486 | 517 | | |
487 | | - | |
488 | | - | |
| 518 | + | |
489 | 519 | | |
490 | 520 | | |
491 | 521 | | |
| |||
546 | 576 | | |
547 | 577 | | |
548 | 578 | | |
549 | | - | |
| 579 | + | |
550 | 580 | | |
551 | 581 | | |
552 | 582 | | |
| |||
565 | 595 | | |
566 | 596 | | |
567 | 597 | | |
568 | | - | |
| 598 | + | |
569 | 599 | | |
570 | 600 | | |
571 | 601 | | |
| |||
598 | 628 | | |
599 | 629 | | |
600 | 630 | | |
601 | | - | |
| 631 | + | |
602 | 632 | | |
603 | 633 | | |
604 | 634 | | |
605 | 635 | | |
606 | | - | |
| 636 | + | |
607 | 637 | | |
608 | 638 | | |
609 | 639 | | |
610 | 640 | | |
611 | 641 | | |
612 | 642 | | |
613 | 643 | | |
614 | | - | |
| 644 | + | |
615 | 645 | | |
616 | 646 | | |
617 | 647 | | |
618 | 648 | | |
619 | 649 | | |
620 | 650 | | |
621 | 651 | | |
622 | | - | |
| 652 | + | |
623 | 653 | | |
624 | 654 | | |
625 | 655 | | |
| |||
641 | 671 | | |
642 | 672 | | |
643 | 673 | | |
644 | | - | |
| 674 | + | |
645 | 675 | | |
646 | 676 | | |
647 | 677 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
1422 | 1423 | | |
1423 | 1424 | | |
1424 | 1425 | | |
| 1426 | + | |
| 1427 | + | |
| 1428 | + | |
| 1429 | + | |
| 1430 | + | |
| 1431 | + | |
| 1432 | + | |
| 1433 | + | |
| 1434 | + | |
| 1435 | + | |
| 1436 | + | |
| 1437 | + | |
| 1438 | + | |
| 1439 | + | |
1425 | 1440 | | |
1426 | 1441 | | |
1427 | 1442 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
313 | 314 | | |
314 | 315 | | |
315 | 316 | | |
316 | | - | |
| 317 | + | |
317 | 318 | | |
318 | 319 | | |
319 | 320 | | |
| |||
322 | 323 | | |
323 | 324 | | |
324 | 325 | | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
325 | 341 | | |
326 | 342 | | |
327 | 343 | | |
| |||
343 | 359 | | |
344 | 360 | | |
345 | 361 | | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
346 | 388 | | |
347 | 389 | | |
348 | 390 | | |
| |||
376 | 418 | | |
377 | 419 | | |
378 | 420 | | |
379 | | - | |
380 | | - | |
| 421 | + | |
| 422 | + | |
381 | 423 | | |
382 | 424 | | |
383 | 425 | | |
| |||
418 | 460 | | |
419 | 461 | | |
420 | 462 | | |
421 | | - | |
422 | | - | |
| 463 | + | |
| 464 | + | |
423 | 465 | | |
424 | 466 | | |
425 | 467 | | |
| |||
472 | 514 | | |
473 | 515 | | |
474 | 516 | | |
475 | | - | |
| 517 | + | |
476 | 518 | | |
477 | 519 | | |
478 | 520 | | |
| |||
0 commit comments