diff --git a/lib/merge.py b/lib/merge.py index d2eeb32..b9ff910 100644 --- a/lib/merge.py +++ b/lib/merge.py @@ -560,9 +560,9 @@ async def _merge_no_ff_external(branch: str) -> tuple[bool, str]: merge_sha = merge_sha.strip().split("\n")[0] # Push to synthetic audit ref _merged/{branch} (does not touch contributor's - # gh-pr-N/* branch). Force-with-lease against expected nothing — the audit - # ref is per-PR and shouldn't pre-exist on a fresh merge. If it does (rare: - # a prior failed attempt), force-push is fine since the ref is bot-owned. + # gh-pr-N/* branch). Plain --force: the audit ref is bot-owned and per-PR; + # if a prior aborted attempt left a stale ref, overwriting it is the + # intended behavior, and there's no concurrent writer to lease against. rc, out = await _git( "push", "--force", "origin", f"HEAD:refs/heads/{audit_ref}", cwd=worktree_path, timeout=30, @@ -969,6 +969,14 @@ async def _merge_domain_queue(conn, domain: str) -> tuple[int, int]: audit_ref = m_ref.group(1) if m_ref else None m_pr = re.search(r"external PR #(\d+)", pick_msg) gh_pr_num = m_pr.group(1) if m_pr else None + # Surface drift between dispatch and _merge_no_ff_external if the + # success-message contract changes. Merge already succeeded; this + # is signal-only, not a gate on the close path. + if not (m and m_ref and m_pr): + logger.warning( + "PR #%d sentinel parse incomplete: M=%s, audit_ref=%s, gh_pr=%s, msg=%r", + pr_num, bool(m), bool(m_ref), bool(m_pr), pick_msg, + ) leo_token = get_agent_token("leo") comment_body = (