IW-437: The phase CI gate finishes a phase without manual intervention #475
No reviewers
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!475
Loading…
Reference in a new issue
No description provided.
Delete branch "IW-437"
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?
Three defects in the phase CI gate. Together they made
iw phase-prunable to finish a phase on a repository whose pipeline runs longer than the poll window — which is this repository. The gate wrote aci_failedcard although nothing had failed, and a person had to merge by hand.Each defect was reproduced with a failing test before it was fixed.
Defects fixed
1.
phase-prmoved the branch head on an unfinished wait (phase 1, closes #445 #450)markPendingandheartbeatrewrite the card every poll, andReviewStateUpdater.mergealways refresheslast_updated, so the card was dirty on every exit. When the poll window ended before CI decided,finishcommitted and pushed it. The new head cancelled the run in flight (concurrency.cancel-in-progress) and started a fresh one, so a resumed wait could never converge.--batchreached the same end throughCommitPolicy.CommitBeforeGate.The card is no longer pushed on an unfinished wait; it stays uncommitted until the verdict is terminal.
doBatchMergenow commits it immediately before the merge, keeping the clean tree the merge needs.2. A 409 Conflict was not recognised as a duplicate pull request (phase 2, closes #431)
resolvePullRequest's fallback to the card's PR url was gated on the error prose matching "already exists".ForgejoClientcollapsed every unhandled status intoForgejo API error: <code>, so a 409 never matched and the resume failed on this project's own forge.The create error is now typed as
ForgeCreateError, so a duplicate is recognised by what the forge said rather than by how an adapter phrased it.3. The CI wait budget refused before it read the forge, and could not be reset (phase 3, closes #433)
CiGate.loopevaluated both budgets before any forge call, so a card carrying a spent budget returnedTimedOutwithout a single API request — a terminal-green pipeline reported as a timeout. Separately, the start of the wait was derived by subtractingtimeoutMsfromci_deadline, whichmarkPendingadded straight back;timeoutMscancelled, so--timeoutwas inert on exactly the invocation where a larger one is needed.The forge is now read once first, a terminal verdict wins outright, and the budget is checked only on a
Runningverdict.ci_deadlineis replaced byci_wait_started, so this invocation's--timeoutdecides the budget.Breaking change
ci_deadlineis gone with no backward compatibility, by a decision recorded inanalysis.md. A card still carrying it is rejected as an unrecognized root key. The field is only present during an active CI wait, so in-flight waits are what this affects; finish them before upgrading, or re-run the gate to write a fresh card.Testing
./mill core.test— 186/186./iw ./test— core 186, dashboard 241, dashboard.itest 240, BATS e2e 190/190Every regression test was written RED first, and each assertion was proved fallible by mutating the fix and confirming the specific test went red. Four assertions that pinned the old contract ("the budget is checked before the forge is read") were deliberately inverted; each comment was rewritten rather than deleted.
Deliberately not fixed
Recorded in
LESSONS.mdand filed rather than folded in: thestartOfWaitfallback on a corrupt or future timestamp (#473, byte-for-byte unchanged from baseline), unenforceddate-timevalidation on card timestamps (#474), the duplicate fallback reading a card keyed on the issue rather than the phase (#467), and unsanitized forge-controlled console output through thegh/glabadapters (#469).PhaseMerge.isStateOnlyDeltais untouched and stays load-bearing: the terminal paths still push a card-only commit.Release notes
project-management/issues/IW-437/release-notes.md(Czech, user-facing).Closes #437
Closes #431
Closes #433
Closes #445
Closes #450
🤖 Generated with Claude Code
Phase CI gate: phase-pr restarts CI on every resume, cannot resume on Forgejo, and expires against a green pipeline Artifacts: - analysis.md 🤖 Generated with Claude CodeResolved: - analysis.md: What should the CI wait budget measure? → take the recommendation in full — read the forge before refusing (D), keep elapsed wall-clock (A), replace ci_deadline with a stored wait-start, and drop the old field (no backward compatibility) - analysis.md: Does wf-implement Step 3 need to tolerate a dirty card? → no. Step 2 stops on an open PR before Step 3 checks the tree, so no workflow step can meet the dirty card. No dev-docs change needed. 🤖 Generated with Claude CodePhase CI gate: phase-pr restarts CI on every resume, cannot resume on Forgejo, and expires against a green pipeline Artifacts: - tasks.md (3 phases, sequenced defect 3 -> 1 -> 2) - review-state.json (tasks_ready) - analysis.md (total range corrected to 7.5-13h after the budget scope was settled) 🤖 Generated with Claude CodeIssue: IW-437 Phase: 1 - phase-pr moves the branch head on an unfinished wait This commit marks the checkpoint for phase 1 context generation. Use dx-fix IW-437 to return to this point. 🤖 Generated with Claude Codephase-pr moves the branch head on an unfinished wait Artifacts: - phase-01-tasks.md 🤖 Generated with Claude CodeA 409 Conflict is not recognised as a duplicate pull request Artifacts: - phase-02-tasks.md 🤖 Generated with Claude CodeIssue: IW-437 Phase: 3 - The CI wait budget refuses before it reads the forge, and cannot be reset This commit marks the checkpoint for phase 3 context generation. Use dx-fix IW-437 to return to this point. 🤖 Generated with Claude Code--checkpoint replaces the whole phase_checkpoints map, so each phase's Step 6a update destroyed the previous phase's entry. Phase 1 and phase 2 were read back out of the card's git history and re-appended with --append-checkpoint. Recorded as LESSONS entry 11. 🤖 Generated with Claude CodeThe CI wait budget refuses before it reads the forge, and cannot be reset Artifacts: - phase-03-tasks.md 🤖 Generated with Claude Code