The phase gate reads and writes cards that do not belong to the head under test #601
Labels
No labels
agent-ready
bug
contract
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
needs-input
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/iw-cli#601
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The phase gate and the card do not agree on which head they speak about. IW-491
fixed the one case that stopped a batch run:
phase-prpushed a card-only commitafter it read a green CI result, so the green described a stale head. The same
mistake is in four other places. Each one is small. Together they are one defect
with four faces, so they belong in one investigation and one branch.
This issue replaces #592, #593, #594 and #595, which are closed as duplicates of
it. Related: #568, #569.
Phase 1 — one flag clears one wait (was #595)
ReviewStateUpdaterhasclearCiWaitStartedandclearCiWaitSha. The two fieldsdescribe one wait and must be cleared together, but eight call sites (in
PhasePr,PhaseMerge,PhaseMergedCard,CiGateCommand) set the pair by hand.A call site that sets one and not the other leaves a wait start with no head, or
a head with no wait start, and nothing catches it.
Fix: replace the pair with one flag,
clearCiWait, that clears both fields.This phase is first because the three phases after it read and clear the same
wait, and each one would otherwise add a ninth and a tenth hand-set pair.
Phase 2 — a red verdict must not move the head (was #592)
After a red verdict,
PhasePr.stop(core/commands/PhasePr.scala:535-558) callspersist, which commits and pushes the card. The push makes a new head, CI runsagain on it, and when the red was false the new head goes green while the card
still says
ci_failed. Seen on IW-583, run 890.Fix: do on the red and stop path what IW-491 did on the green path — write the
card to disk, commit nothing and push nothing.
Check before the fix:
batch-implementandphase-mergemust still read a dirtyci_failedcard correctly. IW-491 recorded the cost this makes on the green path:a bare
phase-commitafterphase-prstops, because it refuses unstaged changes.The red path must not make that cost worse.
Phase 3 — judge a reading only when it is about the pushed head (was #594)
After a recovery push,
phase-mergecan read the forge before the forge has movedthe pull request head. The reading then returns the old head with its red verdict,
phase-mergecounts it as a failure of the new head, and it spends one of its--max-retriesattempts on a result that was never about the new code.Fix: compare the pull request head with the local pushed sha before judging a
reading. When they differ, the reading is not yet about the pushed commit: wait,
read again, and do not spend a retry.
This is the same rule IW-491 wrote into the wait card as
ci_wait_sha— a verdictbelongs to one commit — applied at the other end, to the reading rather than the
wait.
Phase 4 — the state-only merge must say what it did not judge (was #593)
When only
review-state.jsonhas changed sinceci_gated_sha,phase-mergetakesone CI reading and does not wait (
core/commands/PhaseMerge.scala:187-196). ARunningreading is accepted, so the merge can land a head whose own run neverfinished. Commit
9d577408made this trade knowingly: the changed head differsfrom the gated head only in the card.
Phase 2 removes the push that creates most of these state-only heads, so this
phase must re-measure the trade before it changes anything.
Decision (2026-09-21, Michal): keep the trade and record it. The state-only
path does not wait for a terminal verdict. It merges, and it writes on the merged
card that the merged head was never judged. Reason: phase 2 stops the red path from
pushing a card-only commit, so a state-only head becomes rare, and a wait there
would add a full CI cycle to a merge whose only change is the card.
This phase carries no open question. Implement the decision. Do not raise it as a
CLARIFY.
Phase 4 decision (2026-09-21, Michal): keep the trade and record it.
The state-only path in
phase-mergedoes not wait for a terminal verdict. It merges, and it writes on the merged card that the merged head was never judged.Reason: phase 2 stops the red path from pushing a card-only commit, so a state-only head becomes rare. A wait there would add a full CI cycle to a merge whose only change is the card.
Phase 4 therefore carries no open question. It implements this decision. Do not raise it as a CLARIFY.