From 5eb272b50f49d9f21ac274a98589c5ebcf56383f Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Thu, 2 Apr 2026 16:50:52 +0000 Subject: [PATCH] fix(agents): add two-phase claim protocol to prevent duplicate PR reviews MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent evolver identified a systematic pattern: - Pattern: Multiple reviewer pool instances claiming the same PR within seconds of each other, leading to duplicate reviews and wasted resources. - Evidence: PRs #1219, #1236, #1247, #1198 were all double-claimed by pr-reviewer-5 and pr-reviewer-4 within 28-64 seconds of each other. The existing 'check before claiming' protocol has a race window that is too small when multiple pools dispatch simultaneously. - Fix: Added a two-phase claim protocol — after posting a claim comment, wait 5 seconds and re-check for competing claims. Ties are broken by lexicographic comparison of claim tokens. This change requires human approval before taking effect. --- .opencode/agents/ca-continuous-pr-reviewer.md | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/.opencode/agents/ca-continuous-pr-reviewer.md b/.opencode/agents/ca-continuous-pr-reviewer.md index 572ffa09e..d453eb014 100644 --- a/.opencode/agents/ca-continuous-pr-reviewer.md +++ b/.opencode/agents/ca-continuous-pr-reviewer.md @@ -196,17 +196,37 @@ LOOP FOREVER: # Take up to N work items batch = work_items[:N] - # Claim all PRs in the batch BEFORE dispatching (distributed lock) + # Claim all PRs in the batch using two-phase locking protocol + claimed_items = [] for item in batch: if item.type in ("review", "re_review"): + token = f"{INSTANCE_ID}-{item.pr.number}-{timestamp}" post comment on PR: - "Review claimed by reviewer pool instance . - Dispatching independent code review. + "🔒 Review claimed by [claim-token: ] --- **Automated by CleverAgents Bot** Supervisor: PR Review | Agent: ca-continuous-pr-reviewer" + # Phase 2: Wait 5 seconds, then verify claims + wait 5 seconds + for item in batch: + if item.type in ("review", "re_review"): + comments = re-fetch PR comments + competing_claims = [c for c in comments if "claim-token:" in c.body + and c.user == our_user + and c.body does not contain our token] + if competing_claims exist: + their_token = extract claim-token from competing_claims[0] + our_token = f"{INSTANCE_ID}-{item.pr.number}-{timestamp}" + if our_token > their_token: + # We lose — delete our claim and skip + delete our claim comment + continue + claimed_items.append(item) + + batch = claimed_items # Only dispatch for PRs we successfully claimed + # Dispatch N parallel ca-pr-self-reviewer sessions (fire-and-forget) active_reviews = {} # pr_number -> session_id for item in batch: @@ -313,12 +333,20 @@ Multiple reviewer pools or standalone reviewers may run simultaneously. To prevent duplicate reviews: 1. **Claim via comment.** Before dispatching a reviewer for a PR, post a - comment: `"Review claimed by reviewer pool instance ."` + comment: `"🔒 Review claimed by [claim-token: ]"` + where `` is a unique value like `--`. 2. **Check before claiming.** Always fetch fresh comments and check for existing claims before posting your own. -3. **Race condition tolerance.** If a dispatched reviewer finds the PR was +3. **Verify after claiming (double-check protocol).** After posting your + claim comment, wait 5 seconds, then re-fetch the PR comments. If another + claim comment appeared from a different instance within the same window, + the instance with the **lexicographically smaller claim-token wins**. The + losing instance must delete its claim comment and skip that PR. This + two-phase protocol eliminates the race condition where two pools claim + the same PR within seconds of each other. +4. **Race condition tolerance.** If a dispatched reviewer finds the PR was already reviewed by someone else, it should yield gracefully. -4. **Stale claims.** If a claim comment is older than 30 minutes with no +5. **Stale claims.** If a claim comment is older than 30 minutes with no subsequent review activity, consider the claim stale and proceed with a new claim. -- 2.52.0