From d0973fafbfcb795965939c5427fc129e1e337d50 Mon Sep 17 00:00:00 2001 From: Thomas SharedInbox Date: Sun, 24 May 2026 10:48:00 +0200 Subject: [PATCH] fix(agent_loop): prevent infinite catch-up merge retry and wrong issue closure Two bugs fixed: 1. Catch-up scan (section 2b) called _merge_pr and immediately returned, claiming success even when fgj exits 0 but the merge silently failed (e.g. branch-protection rules not satisfied). PR #163 was retried 30+ times in a row because the PR stayed open after each attempt. Fix: verify the PR is no longer open after the merge call; if it is still open, set the issue to State/Question instead of looping forever. 2. ci-fix agents wrote "Closes #198" in commit messages, causing Forgejo to auto-close issue #198 ("Unable to load asset: assets/changelog.txt") even though the commit only fixed the unrelated Play Store upload. Fix: both ci-fix prompts now explicitly forbid issue-number references in commit messages and close operations. Also save ci_run_id_at_start in the ci-fix state (was only done for issue agents) so future guard logic can compare run IDs. Co-Authored-By: Claude Sonnet 4.6 --- scripts/agent_loop.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/scripts/agent_loop.py b/scripts/agent_loop.py index 26c3845..d1f1450 100755 --- a/scripts/agent_loop.py +++ b/scripts/agent_loop.py @@ -509,6 +509,9 @@ def _run_loop() -> int: "Fetch the CI logs using the task ci-logs command or the Codeberg API. " "Identify the failure, fix it, commit, and push to the same branch. " "Do NOT push to main, do NOT close the issue, do NOT merge the PR. " + "Do NOT reference any issue numbers in commit messages " + "(no 'closes #N', 'fixes #N', or similar) — auto-closing the wrong " + "issue via a commit message would be a bug. " "Verify locally with 'task check' before pushing. " "When done, stop." ) @@ -597,7 +600,26 @@ def _run_loop() -> int: if pr_run and pr_run.get("status") == "success": print(f"Catch-up: CI passed on PR #{pr_number} ({pr_url}) — merging.") - _merge_pr(pr_number) + try: + _merge_pr(pr_number) + except RuntimeError as e: + print(f"Catch-up: merge of PR #{pr_number} failed: {e} — skipping.") + continue + # Verify the merge actually happened; fgj can exit 0 without merging + # (e.g. branch-protection rules not satisfied). + if _find_pr_for_branch(branch): + print( + f"Catch-up: PR #{pr_number} is still open after merge attempt " + "— skipping to avoid infinite retry." + ) + if issue_num: + _set_labels(issue_num, add=[LABEL_QUESTION], remove=[LABEL_IN_PROGRESS]) + _comment_issue( + issue_num, + f"Automatic merge of PR #{pr_number} failed (PR is still open " + "after the merge command). Please merge manually.", + ) + continue if issue_num: _close_issue(issue_num) print(f"Merged PR #{pr_number} and closed issue #{issue_num}.") @@ -622,10 +644,16 @@ def _run_loop() -> int: "Fetch the CI logs using the task ci-logs command or the Codeberg API. " "Identify the failure, fix it, commit, and push. " "Verify locally with 'task check' before pushing. " + "Do NOT push to main. " + "Do NOT reference any issue numbers in commit messages " + "(no 'closes #N', 'fixes #N', or similar) — this is a CI fix, " + "not an issue fix, and auto-closing the wrong issue would be a bug. " + "Do NOT close any issues. " "When done, stop." ) pid = _start_agent(prompt, "ci-fix") - _write_state(pid, pending_issue, "ci-fix", session_name="ci-fix") + _write_state(pid, pending_issue, "ci-fix", session_name="ci-fix", + ci_run_id=run["id"] if run else None) return 0 # CI is ok (or no run).