Found while implementing #14004 (PR #14280). Filed rather than ridden along: it is a different surface than that card's declared one, and it is pre-existing — that PR adds roughly 90s to the figure below, it did not create the condition.
Measured
At 4b6dca186 (the #14004 branch), pnpm check:pm-dispatch-gates — which spawns scripts/pm/dispatch-gates.mjs --self-test — run under the shared verify lock:
✓ dispatch-gates self-test: 1174 cases pass.
os-verify-lock: VERDICT command-exit 0 · held the lock 687s (11m27s) · waited 1s
Two facts follow from that number on this container, both measured on this box today:
- A foreground run cannot finish. The container caps a foreground command at about 10 minutes and then SIGTERMs it. Two attempts at this gate died that way (
exit 143) before it was moved to a detached run. - A killed run yields ZERO diagnostic.
selfTest() accumulates every case into an array and prints the whole battery only after the last case, so an interrupted run writes not one ✓/✗ line. Measured: a 9-minute foreground attempt produced 11 lines of output, all of them the lock wrapper's preamble, and zero case lines. A dev cannot tell a hang from a slow pass, and cannot tell which case was in flight.
Two more costs worth stating beside those:
- It is the longest member by far of the gate family a
scripts/** diff derives (the other 14 in that family each finish in seconds), so it dominates the local cost of any scripts/** card. - It holds the shared verify lock for its full 11m27s. One sibling agent was queued behind it for most of that hold during this run.
Why it matters
Every dispatch brief tells a dev to derive the gate union with dispatch-gates and run it. Followed exactly, on this container, that instruction produces a SIGTERM with no output on the one gate that guards the derivation tool itself — the failure mode the tool's own header calls out one level up: an instrument whose result cannot be read is indistinguishable from an instrument that was not run. In practice a dev either skips it (unmeasured, silently) or discovers detaching for themselves, which is a per-card cycle on the highest-cost gate in the family.
Note the cost is inherent rather than accidental: the battery re-runs the tool's real CLI as a child process many times, and a full derivation of this tree is about 30s a spawn. That is deliberate — the same header argues that only a real run can tell a live rendering from a pure-function pin — so ⛔ shrinking the coverage is not obviously the right answer.
Options, not a decision
- Stream each case verdict as it is decided rather than buffering to the end, so an interrupted run still reports how far it got and what failed. Smallest change; does not touch the runtime; turns a silent
143 into a partial reading. - Share one derivation across the end-to-end cases where the card under test is the same, cutting spawn count. Reduces runtime; risks coupling cases that are deliberately independent runs.
- Record it as CI-measured in practice for local purposes — i.e. document that this gate is run detached, with the command to do it — and leave the runtime alone.
Unassigned and untriaged on purpose.
Dedup: full enumeration of the 436 open issues via the REST list endpoint with explicit &page=N (not the Link: rel=next cursor, per #13900), plus local grep. Positive control: #14004 present in the listing. Zero hits for pm-dispatch-gates; the three hits for the timeout/SIGTERM terms are #14213 (hook timeouts), #12337 (cold closure build under the same lock) and a PM seat post, none of them this. #13798 and #13799 are adjacent — self-tests that exit 0 early, and self-tests with no assertion floor — and both are about a battery that did not really run, where this is about one that runs correctly and cannot be read.
Generated by Claude Code
Generated by Claude Code
Found while implementing #14004 (PR #14280). Filed rather than ridden along: it is a different surface than that card's declared one, and it is pre-existing — that PR adds roughly 90s to the figure below, it did not create the condition.
Measured
At
4b6dca186(the #14004 branch),pnpm check:pm-dispatch-gates— which spawnsscripts/pm/dispatch-gates.mjs --self-test— run under the shared verify lock:Two facts follow from that number on this container, both measured on this box today:
exit 143) before it was moved to a detached run.selfTest()accumulates every case into an array and prints the whole battery only after the last case, so an interrupted run writes not one✓/✗line. Measured: a 9-minute foreground attempt produced 11 lines of output, all of them the lock wrapper's preamble, and zero case lines. A dev cannot tell a hang from a slow pass, and cannot tell which case was in flight.Two more costs worth stating beside those:
scripts/**diff derives (the other 14 in that family each finish in seconds), so it dominates the local cost of anyscripts/**card.Why it matters
Every dispatch brief tells a dev to derive the gate union with
dispatch-gatesand run it. Followed exactly, on this container, that instruction produces a SIGTERM with no output on the one gate that guards the derivation tool itself — the failure mode the tool's own header calls out one level up: an instrument whose result cannot be read is indistinguishable from an instrument that was not run. In practice a dev either skips it (unmeasured, silently) or discovers detaching for themselves, which is a per-card cycle on the highest-cost gate in the family.Note the cost is inherent rather than accidental: the battery re-runs the tool's real CLI as a child process many times, and a full derivation of this tree is about 30s a spawn. That is deliberate — the same header argues that only a real run can tell a live rendering from a pure-function pin — so
⛔shrinking the coverage is not obviously the right answer.Options, not a decision
143into a partial reading.Unassigned and untriaged on purpose.
Dedup: full enumeration of the 436 open issues via the REST list endpoint with explicit
&page=N(not theLink: rel=nextcursor, per #13900), plus local grep. Positive control: #14004 present in the listing. Zero hits forpm-dispatch-gates; the three hits for the timeout/SIGTERM terms are #14213 (hook timeouts), #12337 (cold closure build under the same lock) and a PM seat post, none of them this. #13798 and #13799 are adjacent — self-tests that exit 0 early, and self-tests with no assertion floor — and both are about a battery that did not really run, where this is about one that runs correctly and cannot be read.Generated by Claude Code
Generated by Claude Code