Skip to content

test(metadata-protocol): lock-one-resolution takes each distinct read request's reads once, 20 s to 8.4 s (#23182 row 1) - #23755

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23182-lock-one-resolution-budget
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23182-lock-one-resolution-budget

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #23182 (row 1)
Clause-②: no

This PR covers row 1 of #23182 only: @objectstack/metadata-protocol, src/protocol.lock-one-resolution.test.ts, 113.2 s in run 38228297391. It changes one test file and no production file. It takes the file from about 20 s to about 8.4 s alone on this box, and it does not by itself get the file under the 10 s line under CI-shaped contention (27.5 s in the package-suite reading below). The fork that is left is a maintainer's call; it is under "What is left" and in the dev report. #23182 stays open: this PR does not close row 1, and row 5 is its own card of work.

Where the time went

Measured at BASE 534ca13eb5 (vitest module line, default reporter, a shared 4-core box under os-verify-lock):

  • The file is 16 320 generated rows plus 40 named tests. Each row runs under both orders the store can return its stored rows in and both orders its packages can be registered in, so the rows make 28 848 runs of (a fresh protocol instance, two reads, one door).
  • Per category, summed over the run, from an instrumented copy: getMetaItem 6.9 s and getMetaItemLayered 4.9 s (28 875 calls each), the doors 2.25 s, vi.spyOn 0.77 s, building the instances 0.48 s, and about 5.5 s for matchers, the oracle and the runner.
  • None of it is waiting. There is no timer, lease or sleep in the file, and the per-call times (0.1 to 0.4 ms) add up to the module line: it is CPU.
  • The reads take no organization and no verb (envelope ignores its organization argument, and neither read has a verb). The table's rows that differ only in requestScope and operation therefore asked the same two reads of the same data four times each: 28 848 read runs against 7 212 distinct requests, exactly 4 to 1.

What changed (test only)

  • readScenario: one protocol instance per distinct read request, its two reads taken once. The key is the request's real inputs: the environment, the stored rows and the artifacts in the order the store and the registry return them, the type spelling and the package. It is not a description of a row, so two rows share an instance only when every input the reads take is byte-equal. The instance a row's door then runs on is the one its reads ran on, so the header's "ONE protocol instance" still holds (a read still precedes every door on its instance). What is new is that a door can also follow another door on that instance; every row passes under both shapes, and the red sets below are equal on both.
  • The oracle is asked once per row, not once per order (expectedLock was called five times per run).
  • agree: expect(actual, message).toEqual(expected) for the table's hot path. isDeepStrictEqual implies toEqual's equality, so when a pair is strictly equal the matcher would pass and is not built; when a pair differs the real matcher runs and fails with the message and diff it always gave. The table made well over a hundred thousand matchers (about 2 s of the wall).
  • Not changed: every expect that the rows made is still made on the same pair, the 16 360 tests keep their titles and their order, no case is deleted, skipped or moved, and no timeout is touched. door still ends at the real saveMetaItem / deleteMetaItem: a refusal anywhere in the write path is still the verdict (I did not stop the write at the gate, because "end to end" is what the door's doc says it pins).

Wall time

Vitest module line in ms, a shared 4-core box under os-verify-lock, --maxWorkers=2, file alone; BASE and AFTER runs interleaved:

BASE 534ca13eb5 AFTER f214779c15
file alone, 3 runs 19542 / 20389 / 19742 (a first run read 21401) 8404 / 9133 / 8419 (the earlier head 6717655b7f, same code: 8258 / 8359 / 8779)
whole package, --maxWorkers=8 (2x oversubscribed) 58433 27511
heap used at the end of the file 954 MB 774 MB
  • Whole package: 236 files passed and 3 skipped, 20 566 tests passed and 19 skipped, before and after.
  • CI after this change: NOT MEASURED here (job logs redirect to a blob host the egress proxy refuses). The 27.5 s package-suite reading is the nearest stand-in for CI's contention, and it is over the line.
  • What the 8.4 s is made of now. An instrumented copy of the memo-only head (1dc5a6150e, 10.8 s) read: the two reads 3.7 s (7 239 calls of each), the doors 2.0 s, vi.spyOn 0.55 s, the rest of the row bodies 3.2 s and the runner 1.4 s. The hoist and agree then took about 2 s out of the last two. So the protocol's own calls, the reads and the doors, are about 5.7 s of the 8.4 s, and they cannot be shared further without changing what a row asks.

Ablation: the pins still go red, on the same rows

Each leg mutated production source through scripts/ablation-replace.mjs (anchor 1 to 0, the replacement counted, the blob changed), ran a temporary copy of the BASE file and this branch's file in one vitest run, and was restored to the HEAD blob with git diff HEAD empty. The subject is imported by relative path from src, so no dist is in play. The temporary BASE copy is deleted.

leg mutation red tests, BASE file red tests, this branch failing titles
A1 the item lock joins only the first shipping package's answer (item-lock.ts) 2384 2384 equal sets
A2 the write doors ignore the stored rows (item-lock.ts, resolveItemLockLazily) 1823 1823 equal sets
A3 the served body no longer carries the resolved lock family (item-lock.ts, withItemLockFamily) 7744 7744 equal sets
A4 an organization-scoped write is no longer refused first (protocol.ts, temporary, restored) 8160 8160 equal sets

The first failure of A2 reads "the door: expected 'admitted' to deeply equal ..." on both files, so the failing path of agree reports the message and diff the plain matcher did.

Verification at f214779c15

  • Typecheck: pnpm --filter @objectstack/metadata-protocol typecheck exits 0; tsc --listFiles names the test file once.
  • Lint (narrowed to the one file): eslint --no-inline-config --format json reports 1 file, 0 errors and 0 warnings. --print-config returns a config for the file, and its parserOptions are ecmaVersion and sourceType only, so type-aware linting is off and this diff cannot move any other file's result.
  • Gates: dispatch-gates over the one path derives 51 commands. All 114 distinct commands (the 51, plus the 53 artifact-roster and 11 wide-population families it names, one shared) ran at this head after a full turbo run build (74 tasks): 110 exit 0 and 4 exit non-zero. The 4 are PR-context guards that judged nothing and say so: check-closing-target-claim (2) and check-single-claim-paths (2) need a PR number and a token, check-governed-queue-guard (1) needs the event payload, check-partof-closing-keyword (2) needs a PR body (re-run below with this body). They are NOT MEASURED, not red.
  • Bytes: check:nul-bytes exits 0 (inside the 114).

No changeset

This diff changes only a test file under packages/metadata-protocol/src, which files (dist, README.md, CHANGELOG.md) does not ship. skip-changeset is the label, as on the earlier rows.

What is left (the fork)

The single file's floor on this box is about 8.4 s, of which 5.7 s is the protocol's own calls over the 7 212 distinct reads and 28 848 doors. Under CI's contention that is about 27 s. Two routes get the file off the next refresh's list, and neither is mine to take:

Acceptance notes

  • This row's pin is the next refresh PR's list no longer naming the file. This PR alone is not expected to meet it.
  • Outside the module line: a single-file run spends about 13 s in vitest's import phase here (transform 10 s, cold). Observation only; carrier: none.

Generated by Claude Code

…request's reads once (#23182 row 1)

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-authored-by: Claude <noreply@anthropic.com>
… and skips matchers for equal pairs (#23182 row 1)

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-authored-by: Claude <noreply@anthropic.com>
… request's reads once and skips matchers for equal pairs (#23182 row 1)

The file's 16 320 generated rows run 28 848 times (reads, then a door)
once the store and registration orders are counted. The two reads take
no organization and no verb, so the four rows that differ only in those
share one protocol instance and one pair of reads (7 212 distinct
requests); each row still asserts for itself. The oracle's answers are
asked once per row, and the matcher is built only when a pair differs.
Same 16 360 tests, same order, same titles.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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): node scripts/docs-audit/affected-docs.mjs --json 2906cd8d2e568054a4894f01c801a35b83470692 → packageMentionDocs.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 00:31
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 00:31
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 4fe8b3d Oct 16, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23182-lock-one-resolution-budget branch October 16, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants