Skip to content

Check a conflict Repair against the commit it merged, not the moved ref - #175

Merged
JacobStephens2 merged 2 commits into
mainfrom
origin-main-not-merged-in
Sep 30, 2026
Merged

JacobStephens2 merged 2 commits into
mainfrom
origin-main-not-merged-in

Conversation

@JacobStephens2

Copy link
Copy Markdown
Owner

Summary

Merge runs for issues 147, 148 and 158 failed with origin/main is not merged into issue-N and left draft PRs, even though each conflict Repair had resolved and pushed a correct merge. origin/main is shared by every worktree. During the roughly 20-minute Repair, other Runs merged their PRs and fetched, so the ref moved. The check after the Repair then compared the branch against a newer main that the Repair was never asked to merge.

 RepairLoop::run
-  if merge_base_branch(base) == Conflicted
+  if let Conflicted(pending) = merge_base_branch(base)   # pending = MERGE_HEAD, pinned at merge time
     run_session(conflict Repair)
-    ensure_base_branch_merged(base)     # is origin/main (now moved) an ancestor of HEAD?
+    ensure_merged(&pending)             # is the commit we asked it to merge an ancestor of HEAD?
     continue                            # next round fetches and merges whatever main moved to

The same change applies to the conflict Repair for new commits pushed to the Issue branch (take_in_foreign_commits). The two ensure_*_merged wrappers are now one ensure_merged(&PendingMerge).

Evidence

  • Before: the new test merges_a_base_branch_that_moved_again_during_the_conflict_repair (main moves on during the conflict Repair, then the Repair fetches) fails with the same error seen in production:

    thirdshift: repair-1: session ended after 0s
    thirdshift: pushing issue-7
    thirdshift: origin/main is not merged into issue-7
    

    After: it passes. The Run merges the newer main in the next round, and origin's issue-7^2 == main. The full cargo test suite passes. The existing tests for a Repair that aborts the merge or leaves it unfinished still fail the Run as they should.

  • The production timeline for issue-148: the Repair began merging main at e37f549. c935a2b (Release script refuses a release that can't be cut #166) landed on main at 03:03Z. The Repair committed its merge of e37f549 at 03:12:54Z, and the check against c935a2b failed at 03:13:03Z.

Merge Danger

Door: two-way

Only the check after a Repair changes. A plain revert restores the old behaviour.

Blast Radius: narrow

This affects only Runs that hit a merge conflict. The main risk would be accepting a bad Repair, but an aborted merge still fails (the pinned commit isn't in HEAD) and so does an unfinished one (MERGE_HEAD is still present). The foreign-commits path has no dedicated test; it uses the same check.

🤖 Generated with Claude Code

JacobStephens2 and others added 2 commits September 30, 2026 12:02
After a conflict Repair, thirdshift checked that origin/<base> was an
ancestor of HEAD. origin/<base> is shared by every worktree, so a
concurrent Run's fetch (or the Repair's own) could move it during a
long Repair, failing a Run whose merge was complete and correct. The
check now uses the commit the merge began with (MERGE_HEAD), and the
Repair loop's next round merges whatever the base has moved on to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JacobStephens2
JacobStephens2 merged commit 8314f1e into main Sep 30, 2026
13 checks passed
@JacobStephens2
JacobStephens2 deleted the origin-main-not-merged-in branch September 30, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant