test(service-automation): pin the node comparison with a correlation-less parking, and correct the comment that claimed it was already pinned - #15836
Conversation
…less parking `InMemorySuspendedRunStore.claimSuspension` compares two things, and only one of them was falsifiable. Both existing CONDITION cases re-park with a NEW correlation, so the correlation comparison rejects the stale claim first and masks the node comparison entirely: deleting `run.nodeId !== parkedAt.nodeId` alone left the three-file population fully green. Add the case that makes it falsifiable — a pausing executor that mints NO correlation, the shape `SuspensionParkedAt` documents as legitimate, where the node comparison is the only guard there is — and correct the CONDITION block's lead comment, which asserted a red that did not exist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
…n the 62-test population Adding a case to the population invalidates every total the header quotes, and this file's whole subject is a measurement claim that had gone false. So all five legs were re-driven on origin/main d4f9b2a rather than adjusted on paper: (E) 10 red, (C) both comparisons 3 red, (C-node) 1 red, (C-corr) 1 red, (C2) 7 red. (C-node) and (C-corr) are new rows — splitting (C) into its two halves is what exposed the unfalsifiable one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
…n-node-comparison
📓 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): |
Clause-② contract review — PR #15836 (card #14956) — verdict: PASSHead reviewed: Already verified by the PM and not redone here: head match, merge-base, exactly one file changed, 0 non-test source touched, ADR-0087 exit 0. How this was weighed. A test-only PR whose entire content is claims about which mutations go red, on a card that exists because a comment in this very file asserted a red that did not exist. So the numbers were re-derived, not read. Every leg below was driven on the head tree by my own harness ( 1 ⭐ The bench, both halves — reproduces exactly, and the pin does its jobPre-pin legs swap in the merge-base blob of the test file (
Node-only flips 0 → 1 with the pin present, and the one red is the new case. That is the card's requirement, met. (The dev's bench was driven pre-merge at 2 The pin — it is the right pin, and not a tautology
3 ⭐ The re-measured REVERT-PROOF ledger — every total verified on the head
No total is stale. The ledger this card was filed to fix is right on the 62-test population. 4 The aborted C2 leg — the refusal path exists and works
5 #15832 — filed bare (0 labels, 0 assignees, confirmed); Note 5 holds; ⭐ Note 1's severity is UNDERSTATED
6
|
Fixes #14956
Test-only. No behaviour changes —
suspended-run-store.tsandengine.tsare untouched.The gap
InMemorySuspendedRunStore.claimSuspensioncompares two things, and only one of them was falsifiable. Both existing CONDITION cases inconcurrent-replica-resume-race.test.tsre-park with a new correlation (req_lv1→req_lv2,map:item_1→map:item_2), so the correlation comparison rejects the stale claim first and masks the node comparison entirely. Deletingif (run.nodeId !== parkedAt.nodeId) return 'lost';alone left the whole three-file population green — on the comparison the maintainer's ruling names verbatim ("delete only if still parked at node N"), on the path whose purpose is stopping two replicas resuming one run twice.And the file's own CONDITION comment asserted the opposite: "one per comparison, so a mutation that deletes only one of them still reddens." False for the node half. A comment describing a red that does not exist is worse than none.
Re-derived bench — the card's numbers reproduce exactly
Card's numbers were taken at
feb213d42. Re-driven here onorigin/maind4f9b2a9d— population unchanged at 61, every leg identical, zero delta:InMemorySuspendedRunStore.claimSuspensionfeb213d42)d4f9b2a9d)61 passed (61)2 failed | 59 passed (61)1 failed | 60 passed (61)61 passed (61)— 0 redThe pin, driven and mutation-checked here
Reconstructed rather than accepted as given — the point of the card is that an unverified claim about a red was wrong.
THE CONDITION (node, correlation-less): a pausing executor that mints no correlation — the shapeSuspensionParkedAtdocuments as legitimate ("a row persisted with no correlation has nothing to compare, and the node condition still holds") — with replica B's claim held on a latch until replica A has advanced, firednotify, and re-parked atlv2. The claim B then presents carries no correlation key at all, sonodeIdis the only field the store can test.Green at HEAD; under the node-only deletion it fails at
concurrent-replica-resume-race.test.ts:661with:B's stale claim granted, and
led.firedbecomes['notify','notify']— the action fired twice.With the pin, node-alone is
1 failed | 61 passed (62), and the one red is this case.Every ablation leg mutated the source on disk, proved it landed (anchored
grep -Fccounts plus agit hash-objectdelta against the HEAD blob) before reading any result, restored fromHEADinside atrap … EXIT INT TERMwith absolute paths, and proved the restore by an emptygit diff HEADand a blob hash equal to HEAD's. One leg (C2) initially reportedMUTATION NOT CONFIRMEDbecause my on-disk check anchor (multi: true) was not unique in the file; the harness refused to measure, I narrowed the anchor and re-ran. That refusal working is why the numbers below are trustworthy.The comment, corrected to the measured truth
The false sentence is replaced by the measurement, both readings labelled so neither can be misread:
The file's REVERT-PROOF ledger, re-measured in full
Adding a case to the population invalidates every total the header quotes, and this file's whole subject is a measurement claim that had gone false — so all five legs were re-driven rather than adjusted on paper, on the 62-test population:
10 failed | 52 passed (62)— 8 here, 2 insuspended-run-store.test.ts; staleness stays 8/83 failed | 59 passed (62)— the three CONDITION cases, nothing else1 failed | 61 passed (62)— new row1 failed | 61 passed (62)— new rowmulti: true7 failed | 55 passed (62)— all insuspended-run-store.test.tsSplitting (C) into its halves is what exposed the unfalsifiable one, so the two new rows stay.
node_idfrom itswhereis already caught by that suite's exact-shape assertions (leg C2 above, 7 red). This gap was specific to the in-memory store.Verification
Head
e60ca587d(includes a merge oforigin/mainb2375921b; the closure was rebuilt after that merge — before it, a stale@objectstack/specdist reported two phantomTS2305errors from sibling PR #15792's new exports).pnpm --filter @objectstack/service-automation exec vitest run→110 passed (110)files,1317 passed (1317)testspnpm --filter @objectstack/service-automation typecheck→ exit 0, and the edited file is proven inside the program (tsc -p tsconfig.test.json --listFilesnames it)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(45 owed): 43 exit 0, 2 at exit 3 = PREREQUISITE NOT MET = NOT MEASURED (check:dual-build-cjs-loads,check:type-check-debt— both read built output of the whole workspace, which this worktree does not have). ⛔ Neither is a pass; CI measures them.Also owed by the card, and it did not exist
The card flagged as NOT VERIFIED that a grouped follow-up for the prior review's §5 notes 1 and 5 could not be found. I searched rather than assuming: a targeted issue search returned only #14956 itself, #15222 and an unrelated
domain:skillscard, and a local grep found nothing. It did not exist, so it is now filed bare (no labels — routing is the PM's) as #15832, with both notes re-verified against currentmainrather than carried over on the review's word. Out of scope here: #15832 is not addressed by this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
Generated by Claude Code