fix: replace broken _rebase_and_push call with cherry-pick in conflict retry

_retry_conflict_prs called _rebase_and_push which was never defined,
causing NameError on every conflict retry. Now uses _cherry_pick_onto_main
consistent with the primary merge path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
m3taversal 2026-03-31 12:52:39 +01:00
parent 686ef3fd7f
commit f25a4093c2

View file

@ -178,9 +178,8 @@ async def _claim_next_pr(conn, domain: str) -> dict | None:
"""
# Build prefix filter for pipeline-owned branches only
# Agent branches stay approved but are NOT auto-merged (Leo: PRs #2141, #157, #2142, #2180)
prefix_clauses = " OR ".join(
f"p.branch LIKE '{pfx}%'" for pfx in PIPELINE_OWNED_PREFIXES
)
prefix_clauses = " OR ".join("p.branch LIKE ?" for _ in PIPELINE_OWNED_PREFIXES)
prefix_params = [f"{pfx}%" for pfx in PIPELINE_OWNED_PREFIXES]
row = conn.execute(
f"""UPDATE prs SET status = 'merging', last_attempt = datetime('now')
WHERE number = (
@ -210,7 +209,7 @@ async def _claim_next_pr(conn, domain: str) -> dict | None:
LIMIT 1
)
RETURNING number, source_path, branch, domain""",
(domain,),
(domain, *prefix_params),
).fetchone()
return dict(row) if row else None
@ -329,8 +328,10 @@ async def _cherry_pick_onto_main(branch: str) -> tuple[bool, str]:
if conflict_files and all(f.startswith("entities/") for f in conflict_files):
# Entity conflicts: take main's version (entities are recoverable)
# In cherry-pick: --ours = branch we're ON (clean branch from origin/main)
# --theirs = commit being cherry-picked (extraction branch)
for cf in conflict_files:
await _git("checkout", "--theirs", cf, cwd=worktree_path)
await _git("checkout", "--ours", cf, cwd=worktree_path)
await _git("add", cf, cwd=worktree_path)
dropped_entities.update(conflict_files)
rc_cont, cont_out = await _git(
@ -366,7 +367,9 @@ async def _cherry_pick_onto_main(branch: str) -> tuple[bool, str]:
# Force-push clean branch as the original branch name
# Capture expected SHA for force-with-lease
rc, expected_sha = await _git("rev-parse", f"origin/{branch}")
expected_sha = expected_sha.strip().split("\n")[0] if rc == 0 else ""
if rc != 0:
return False, f"rev-parse origin/{branch} failed: {expected_sha}"
expected_sha = expected_sha.strip().split("\n")[0]
rc, out = await _git(
"push",
@ -972,8 +975,8 @@ async def _merge_domain_queue(conn, domain: str) -> tuple[int, int]:
failed += 1
continue
# Local ff-merge: push rebased branch as main (Rhea's approach, Leo+Rhea: local primary)
# The branch was just rebased onto origin/main by _rebase_and_push,
# Local ff-merge: push cherry-picked branch as main (Rhea's approach, Leo+Rhea: local primary)
# The branch was just cherry-picked onto origin/main,
# so origin/{branch} is a descendant of origin/main. Push it as main.
await _git("fetch", "origin", branch, timeout=15)
rc, main_sha = await _git("rev-parse", "origin/main")
@ -1256,13 +1259,13 @@ async def _handle_permanent_conflicts(conn) -> int:
async def _retry_conflict_prs(conn) -> tuple[int, int]:
"""Retry rebase on conflict PRs that were previously approved.
"""Retry conflict PRs via cherry-pick onto fresh main.
Design: Ganymede (extend merge stage), Rhea (safety guards), Leo (re-eval required).
- Pick up PRs with status='conflict' and both approvals
- Attempt fresh rebase onto origin/main
- If rebase succeeds: force-push, reset to 'open' with verdicts cleared for re-eval
- If rebase fails: increment attempt counter, leave as 'conflict'
- Cherry-pick extraction commits onto fresh branch from origin/main
- If cherry-pick succeeds: force-push, reset to 'open' with verdicts cleared for re-eval
- If cherry-pick fails: increment attempt counter, leave as 'conflict'
- After MAX_CONFLICT_REBASE_ATTEMPTS failures: mark 'conflict_permanent'
- Skip branches with new commits since conflict was set (Rhea: someone is working on it)
"""
@ -1293,8 +1296,8 @@ async def _retry_conflict_prs(conn) -> tuple[int, int]:
await _git("fetch", "origin", branch, timeout=30)
await _git("fetch", "origin", "main", timeout=30)
# Attempt rebase
ok, msg = await _rebase_and_push(branch)
# Attempt cherry-pick onto fresh main (replaces rebase — Leo+Cory directive)
ok, msg = await _cherry_pick_onto_main(branch)
if ok:
# Rebase succeeded — reset for re-eval (Ganymede: approvals are stale after rebase)