diff --git a/lib/merge.py b/lib/merge.py index 9910fb8..f596446 100644 --- a/lib/merge.py +++ b/lib/merge.py @@ -746,12 +746,16 @@ async def _merge_domain_queue(conn, domain: str) -> tuple[int, int]: # The branch ref still points at old commits (not a descendant of main), # so pushing branch_sha:main would fail as non-fast-forward. if pick_msg in ("already merged (all commits empty)", "already up to date"): - mark_merged(conn, pr_num) - db.audit(conn, "merge", "merged", json.dumps({"pr": pr_num, "branch": branch, "note": "content already on main"})) leo_token = get_agent_token("leo") await forgejo_api("POST", repo_path(f"issues/{pr_num}/comments"), {"body": f"Content already on main — closing.\nBranch: `{branch}`"}) - await forgejo_api("PATCH", repo_path(f"pulls/{pr_num}"), {"state": "closed"}, token=leo_token) + result = await forgejo_api("PATCH", repo_path(f"pulls/{pr_num}"), {"state": "closed"}, token=leo_token) + if result is None: + logger.error("PR #%d: Forgejo close failed (already-merged path), skipping DB update", pr_num) + failed += 1 + continue + mark_merged(conn, pr_num) + db.audit(conn, "merge", "merged", json.dumps({"pr": pr_num, "branch": branch, "note": "content already on main"})) await _delete_remote_branch(branch) logger.info("PR #%d already merged (content on main), closed", pr_num) succeeded += 1 @@ -964,12 +968,15 @@ async def _handle_permanent_conflicts(conn) -> int: branch = row["branch"] domain = row["domain"] or "unknown" - # Close PR on Forgejo - await forgejo_api( + # Close PR on Forgejo — check return to prevent ghost PR + close_result = await forgejo_api( "PATCH", repo_path(f"pulls/{pr_number}"), body={"state": "closed"}, ) + if close_result is None: + logger.error("PR #%d: Forgejo close failed (permanent conflict), skipping", pr_number) + continue await forgejo_api( "POST", repo_path(f"issues/{pr_number}/comments"),