Repository navigation
premerge: Phase 4 is gated on an unreachable predicate, so the simplify pass never runs on any stack that needs it #785
Description
The gap
Phase 4 runs only on STOP_GREEN. STOP_GREEN requires the latest review to return zero blockers. A critic asked "what is wrong with this?" samples from an unbounded set, so blockers == 0 is a property of the sample, never of the artifact — and on any stack big enough to need the loop, it is not a reachable state.
Measured on stack PR #768, four xhigh rounds:
| Attempt | Blockers | Result |
|---|---|---|
| 1 | 17 | FIXED=17 |
| 2 | 11 | FIXED=11 |
| 3 | 6 | FIXED=6 |
| 4 | 7 | budget spent → STOP_EXHAUSTED |
Every round cleared 100% of the blockers it was given, and the next review found more. CI was green and the PR mergeable from attempt 1's repair onward. Phase 4 never became reachable, so the simplify pass — and the 4c VERIFY step, and everything downstream of the green gate — silently did not run.
SKILL.md names this as the pre-loop bug and claims the loop fixed it:
Before it, Phase 4 was gated on a single-shot gate that a surviving blocker or a red build left not-green — so on any stack that needed fixing at all, the simplify pass silently never happened and the run still reported success. The loop is what makes a green gate reachable.
It doesn't. It moves unreachability from one shot to N shots. #718 and #754 describe the same pump from the output-bound and self-reference angles; this issue is about what the pump costs downstream.
Why the gate is testing the wrong thing
It conflates two different propositions:
- "the stack has no known correctness bugs" — a sensible precondition for polishing
- "the newest review sample returned zero blockers" — a property of the sample
Only the first justifies skipping polish. The gate tests the second.
The stated rationale doesn't require the skip
Polishing code that still carries a known correctness bug is wasted work, and the refactor diff buries the bug.
Burying is solved by putting the polish in its own commit, not by skipping the phase. Phase 4 already does exactly that — one refactor(premerge): apply simplify lenses to the stack commit, revertible as a single SHA by 4c. The separation is the real protection; the gate adds nothing on top of it.
What it cost on this run
The pass was run manually after the fact. It found five guards that were provably dead, each demonstrated by mutation rather than argued:
| Guard | Proof it was vacuous |
|---|---|
C11 dual-channel stanza |
passed with StructuredOutput stripped from all eight card bodies — #763's frontmatter satisfied the whole-file grep |
#749 tools-declaration |
passed with a live card's tools: value emptied — the exact state it exists to red on |
ci-shard-diagnose dump |
reported MATCH match=12 miss=0 for a shard that reds 11/12 |
PREMERGE_MAX_BLOCKED_ON lock |
name-grep only; green at any value |
unpinned sort/comm twin |
blind to sort) and end-of-line |
Plus five surfaces documenting a growth-ratio predicate the code does not implement (prose denominator 3 vs code 1 on the same document — 0.33 vs 1.00, straddling the 0.50 ceiling, i.e. the two copies disagreed about whether the loop stops).
The current design would have shipped all of that, on the grounds that the stack "was not clean enough to polish".
Proposed change
- Gate Phase 4 on
CI green AND mergeable, not on blocker-count-zero. Both were true here from attempt 1's repair onward. - Run it whenever the loop stops for any reason —
STOP_GREEN,STOP_EXHAUSTED,STOP_NO_PROGRESS,STOP_SELF_REFERENTIAL— since none of those says anything about whether the polish is safe. - Keep it in its own commit on the stack branch, which it already does. That is what stops it burying anything, and it keeps
4c's single-SHA revert intact. - Keep
4cVERIFY unchanged: re-probe, re-gate on the simplify SHA, revert on harm.
Surviving blockers are already filed as issues by Phase 5, so a polished-but-not-blocker-free stack loses nothing — the blockers outlive the run either way.
Not proposed
Do not relax the gate to "run Phase 4 always". A red build or a CONFLICTING mergeable state are real reasons to skip: the polish cannot be verified on a stack whose CI cannot answer, and 4c would have nothing to gate against.
Related
- premerge: no bound on its own output — the issue budget is per-dispatch inside an N-round loop, and the loop reviews its own machinery #718 — the loop's output is unbounded (same pump, output-budget angle)
- premerge: converge should compute the growth ratio and stop on it — the loop cannot tell self-generated work from real work #754 — the growth ratio (same pump, self-reference angle)
- premerge: STOP_EXHAUSTED files blocker issues for a stop its own docs call resumable #767 —
STOP_EXHAUSTEDfiles blockers for a stop the docs call resumable - /premerge output is unmergeable by /merge — add a distinct premerge trust tier #716 —
/premergeoutput is unmergeable by/merge - PR chore(stack): land #762 #763 #764 #765 #766 (v0.56.0) #768 (the measured run), PR refactor(premerge): three-lens simplify pass over the #768 stack #781 (the manual pass and what it found)
Activity