Repository navigation
Commit 5b09356
fix(pm): governed-merges self-test refuses as NOT MEASURED when its generator toolchain is absent (#15283)
* fix(pm): refuse the governed-merges self-test as NOT MEASURED when its generator toolchain is absent
In a freshly created worktree, before `pnpm install`, `pnpm
check:pm-governed-merges` exited 1 — a finding's code — and reported
self-test battery "#11705 end to end, against the REAL generator"
registered 5 case(s), below its pinned floor of 7 — cases that used to
run no longer do.
Nothing had stopped running. The battery's live case execs the real
generator (`pnpm --filter @objectstack/spec exec tsx …`), which in an
uninstalled checkout exits 254 with `Command "tsx" not found`; that arrives
at `sinkGeneratorVerdict` wearing the same `outputs: null` shape as every
other generator failure, the battery takes its one-assertion branch, and the
floor renders a NOT-MEASURED condition as a measured red — prescribing a
hunt through the generator for a deleted block that was never deleted.
Three sibling gates met the same worktree in the same minute and answered
correctly with exit 3 PREREQUISITE NOT MET. This gate now does too:
- `generatorToolchainPrerequisite()` asks whether the row's recompute can
run at all, by looking for the same binary `pnpm exec` would. It is
consulted at the top of `selfTest()`, before a single case runs, so the
shared frame's load-bearing clause is literally true when it prints. It is
strictly a narrowing of one exit code into another — no branch in it can
turn a red into a green, and the audit paths never consult it.
- The refusal reuses `scripts/import-prerequisite.mjs` — the exit code, the
frame and the pipe advisory are the repo's, not a sixth spelling.
- The reason string is deliberately NOT classified: an uninstalled checkout
and a generator reporting real drift both arrive as a non-zero `--check`
exit, so reading that text would fold a finding into a refusal.
- `batteryFloorFailures()` is lifted to module scope and takes its ledger as
an argument, so the direction that matters most — a genuinely deleted case
still reds, and still names WHICH battery — is asserted for the first
time. Its behaviour, order and every message it prints are unchanged.
Both directions are pinned (15 new cases, 245 -> 260 assertions): an
uninstalled checkout refuses with exit 3 end to end, on a copy of this
file's own module graph, naming the prerequisite and the remedy and no
longer printing the shrunken-battery words; a ledger one case short of any
floor still reds and still names that battery.
No floor is lowered, no case is deleted or skipped, and the post-merge
audit's verdicts are untouched.
Fixes #15272
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
* fix(pm): name the command that re-runs these cases in the refusal
The shared advisory names the importer by PATH, which is right for the gates
it was written for but incomplete here: this file has modes, and
`node scripts/pm/check-governed-merges.mjs` without `--self-test` runs the
network SWEEP rather than the cases that just refused. The helper has no
seam for an invocation, so the correct entry point is stated in this gate's
own detail lines, where the claim about what went unmeasured already lives.
Pinned end to end: 245 -> 261 assertions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
* fix(pm): stop the end-to-end fixture from spawning an unbounded chain of itself
Found by ablating the probe: the fixture child reaches the prerequisite
battery only when the probe did NOT refuse, and there its own copied tree is
a perfect seed for the next copy. Measured, an ablated probe produced one
self-test per second, each waiting on the next — 67 processes before it was
cut off by hand.
A hang is the worst reading a gate can return: neither green nor red, and
nothing in CI distinguishes it from slow. The child is now marked in its
argv, a marked run refuses to spawn a grandchild, and the cases it would
have measured FAIL with that reason instead — the same number of cases, in
both worlds, and a loud red where there was an unbounded wait.
261 -> 262 assertions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0cbd5f2 commit 5b09356
1 file changed
Lines changed: 312 additions & 41 deletions
0 commit comments