test(scripts): floor only the unconditional cases of run-with-stall-guard and release-rehearsal-clone (#15317 ruling B) - #15399
Merged
Conversation
`run-with-stall-guard.mjs` (floor 41) and `pm/release-rehearsal-clone.mjs`
(floor 32) each pinned an assertion floor over a battery whose case count
depends on the HOST, not on whether the cases ran: 21 of the stall guard's
cases sit behind `existsSync('/proc')`, and 2 of the rehearsal clone's behind
`existsSync` of the rehearsal doc and of `lint.yml`. Off Linux, or in a
checkout missing either file, those floors red for the ENVIRONMENT — and the
remedy an author reaches for is editing the floor down, the one habit these
floors exist to prevent.
The environment-conditional cases now go through a sink variant that reports
into the same `failures`/`results` sinks and registers nothing
(`checkConditional`, `tConditional`). Their conditions, messages and arguments
are unchanged; the guards are untouched and no condition is inverted. Why they
are outside the roster is stated at the guard and in the roster comment.
Floors re-measured off a run at pin 9999, never derived by subtraction:
41 -> 20 and 32 -> 30, read from the breach line each self-test printed.
The skipped block still names itself: the stall guard keeps its existing
`(process-classification cases skipped: /proc not available)` line, and C10
gains the equivalent `(C10 wiring cases skipped: ... not present)` line so a
guard that skips can never read as one that ran and held.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
This was referenced Sep 4, 2026
baozhoutao
marked this pull request as ready for review
September 4, 2026 14:28
baozhoutao
enabled auto-merge
September 4, 2026 14:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #15317
Ruling B on the two b5 hoisted single batteries PR #15308 landed for batch 8a of #13799.
Both floors counted cases that only run when the ENVIRONMENT provides something. Off Linux,
or in a checkout missing a wiring file, those floors red for the host rather than for a case
that stopped running — and the remedy an author reaches for is editing the floor down, the one
habit these floors exist to prevent. The conditional cases keep asserting exactly as they did;
what changes is that they no longer register against the floor, and the guard now says why.
What moved
scripts/run-with-stall-guard.mjs— 21 of the 41 cases sit behindconst linux = existsSync('/proc'): process classification, the SIGTERM-trapping descendant,the source-side liveness probe and its cap. They now report through
checkConditional, a sinkthat pushes into the same
failures/resultsarrays with the same label and detail, and callsregisterCase()never. Floor 41 -> 20. The block already named itself on skip(
(process-classification cases skipped: /proc not available)); that line is untouched.scripts/pm/release-rehearsal-clone.mjs— C10's two wiring cases sit behindexistsSyncofdocs/releases-maintenance.mdand of.github/workflows/lint.yml. They now report throughtConditional, samefailurestally, no registration. Floor 32 -> 30. C10 gains the equivalent(C10 wiring cases skipped: ... not present)line, so a guard that skipped can never read as onethat ran and held.
The guards themselves are untouched, no condition is inverted, and no assertion condition, message
or argument changed. The only deletions are inside the two sinks: each original body was split into
a non-registering reporter (
record/report) plus a thin registering wrapper that keeps the oldname (
check/t), so every existing call site is unchanged.The floors are MEASURED off a run, never derived by subtraction
Pin the floor to 9999, run, read the breach line the self-test prints:
20 and 30 are those readings, not 41 minus 21 and 32 minus 2 done by hand.
Green lines are byte-identical to the installed base
Both self-tests, stdout+stderr, this branch vs a worktree at the merge base:
run-with-stall-guard --self-test: whole outputcmp-identical, 42 green lines(41 cases +
✓ 41 case(s) passed).release-rehearsal-clone --self-test: 33 green lines, identical except one case that prints awall-clock figure (
C2 a healthy tree is not slowed (102 ms < 10000)). That figure moves betweentwo runs of the BASE at the same commit (102 ms then 95 ms), so it is nondeterminism the base
already had, not a change this diff made. With that one number normalised the green lines are
cmp-identical.Both suites still run every case here: 41 and 32 assertions execute, exactly as before. Only the
registration changed.
Ablations
Each mutation was proven on disk by blob hash plus grep counts before its reading was taken, and
each restore proven by hash equality against the HEAD blob plus an empty
git diff HEAD, under atrap ... EXIT INT TERM.(a) The environment simulated absent — least-invasive path-literal mutation
(
existsSync('/proc')and the twojoin(REPO_ROOT, ...)wiring paths pointed at names that do notexist). Both self-tests go GREEN, both print their
cases skippedline naming what was missing, andboth floors hold:
✓ 20 case(s) passedand✓ self-test passed.The same mutation against the installed base is the counter-control, and it is the defect this card
exists to remove — the base REDS on the floor, for the environment:
(b)
registerCase()removed from the unconditional sink — the floor calls it, and says so:All 41 and all 32 case lines stayed green under that mutation and were
cmp-identical to theunmutated run — which is exactly the point: nothing but the floor can see this.
(c) A conditional case forced to fail with the environment present — the run reds on it, so the
cases are still real assertions rather than decoration:
The instrument does not move
node scripts/measure-self-test-floor.mjs --json, at the merge base vs this branch (170 files bothsides): both target files stay ROSTER, the tally is identical (163 ROSTER / 6 NONE / 1 COUNT), and
nothing entered NONE. Zero floor-class changes attributable to this diff.
Gates
Run at
cf532f2b3, exit codes captured before any pipe. The derived family fromnode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack— all 25 commands,green. The always-runs tail —
check:pm-dispatch-gates,check-self-test-workflow-commands.mjsboth modes,
check:declared-population-live,check-self-test-wired.mjsboth modes,check:ratchet-remedy-authority,check:nul-bytes,check:watch-hint-literal— green. Whole-repopnpm lint— green. Both files' own self-test suites are their test suites; no vitest file in therepo names either script.
The first derivation ran on a tree 13 commits behind and the tool said so, naming
scripts/pm/dispatch-gates.mjsitself among the files that had changed. Re-deriving after mergingorigin/mainaddedpnpm check:driver-memory-censusto the family — so the staleness was real, andthe number above is the fresh one.
skip-changeset: this PR publishes nothing from any package — it edits two files underscripts/.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code