phase-pr's duplicate fallback can return an earlier phase's PR url #467
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#467
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?
Found triaging IW-437 (recorded during phase 2, deliberately not fixed there).
WHAT. resolvePullRequest's Duplicate branch reads the pull request url off review-state.json via reviewStatePathFor(r, env) (core/commands/PhasePr.scala:143-160). That path is keyed on the ISSUE, not the phase (PhasePr.reviewStatePathFor, :453).
If a create fails as a duplicate while the card still carries an earlier phase's pr_url, resolvePullRequest hands back that stale url. The CI gate then waits on the wrong pull request, and under --batch the merge acts on the wrong pull request.
AGE AND EXPOSURE. The window predates IW-437 phase 2: it existed whenever describesExisting matched. Phase 2 did not create it, but it widened what counts as a duplicate - a 409 status now qualifies on every forge, not only the forges whose CLI happened to phrase the error as "already exists" - so the window is easier to hit after that fix than before it.
WHY IT WAS NOT FIXED THERE. phase-02-context.md scoped the phase to the create-pull-request classification path and instructed: "Record the per-issue card risk; do not fix it."
DESIGN QUESTION, not a one-line fix. A guard costs a forge round trip to read the pull request's head branch before trusting the card's url. Where it belongs is open: inside resolvePullRequest, or as a check on the card itself (does this card's pr_url belong to the phase we are running?).
Severity note: this is the failure mode in which the tool merges a pull request nobody asked it to merge.
The two warnings the review raised against the guard are fixed on
mainas ofcfe98763(PR #506), and the fix changed the question the guard asks.Both warnings had one root: a head-sha comparison asks whether the recorded pull request points at the commit just pushed, and that is not the same question as whether it belongs to this phase.
--batchthat stops the run and needs a person, for a pull request that was correct all along.pr_url. On a lag that is a good url thrown away to work around a delay, and clearing it leads to the other dead end: "the review-state card records no url for it".The branch a pull request is open on says which work it is for, and no lag moves it.
TrackerOps.fetchPrHeadBranchreads it from Forgejo (head.ref), GitHub (head.refthroughgh api) and GitLab (source_branch). The guard compares it againstphaseBranchName. The refusal names both branches and asks forpr_urlto point at the pull request open on this phase's branch, rather than for the loss of the one recorded. A forge that will not answer is still a refusal — guessing is the failure this guard exists to stop.What this does not do is remove the need for a person when the card is genuinely wrong. The forge already knows which pull request is open on the branch, so the card could leave the merge decision entirely. That is #507.