PR Review Loop
⛔ STOP - READ THIS FIRST ⛔
1. Locate the scripts directory
FIRST, use the Glob tool to find the scripts:
Glob pattern: **/pr-review-loop/*/scripts/commit-and-push.sh
Path: ~/.claude/plugins/cache
This gives you the full absolute path to the scripts directory.
Then use the full literal path for every script call. For example:
/home/user/.claude/plugins/cache/devon-claude-skills/pr-review-loop/1.0.0/skills/pr-review-loop/scripts/commit-and-push.sh "msg"
NEVER use variables like $SCRIPTS/commit-and-push.sh — this breaks permission matching.
NEVER use compound commands like export PATH=... && commit-and-push.sh — this also breaks permissions.
Always inline the full absolute path in every Bash call.
2. No raw git commands
| ❌ FORBIDDEN | ✅ USE INSTEAD |
|---|---|
git commit |
scripts/commit-and-push.sh "msg" |
git commit -m "..." |
scripts/commit-and-push.sh "msg" |
git push |
scripts/commit-and-push.sh "msg" |
git push origin |
scripts/commit-and-push.sh "msg" |
If you use git commit or git push directly, it will be BLOCKED.
3. The PR is the system of record — agents POST their findings
Every agent finding MUST exist as a line comment on the PR (posted by the
agent itself via post-line-comment.sh) before its fix is committed. A
finding that lives only in an agent's return text, the orchestrator's
context, or a commit message does NOT count as reviewed.
| ❌ FORBIDDEN | ✅ USE INSTEAD |
|---|---|
| Instructing agents to "return findings, do not post" | Agents post via post-line-comment.sh, then return a manifest of what they posted |
| Fixing a finding that has no posted comment thread | Post first (the agent's job), then fix, then reply-to-comment.sh |
| Silently dropping a validator-refuted finding | Reply + resolve its thread as withdrawn (reply-to-comment.sh <PR> <id> "Withdrawn — validator refuted: <reason>") |
| Substituting a consolidated "review record" PR comment for line comments | Line comments at the flagged lines; consolidated comments are a supplement, never the record |
Why this is load-bearing, not ceremony:
- The threads ARE the user's review. The operator reviews the PR primarily by reading the problems the reviewers surfaced, in situ on the diff. When findings are absorbed into fixes without comments, the operator is left blind — they see a diff churning across rounds with no visible record of what was wrong, what was contested, or what was withdrawn.
- Audit trail: findings, dispositions, and withdrawals stay attached to the lines they're about, with reopen rights on every thread.
- Round-to-round dedup: each agent's step 1 (
get-agent-comments.sh) checks its own prior comments — if nothing was posted, every later round re-litigates from scratch and reopen/retirement logic silently breaks. - Cost routing: the posting legwork (file/line anchoring, comment bodies) belongs on the cheap per-agent model, not the expensive main-loop model.
- Merge-readiness integrity: the end-of-loop summary counts threads; zero posted threads with nonzero findings is a protocol violation that must be reported, not papered over.
A repo's AGENT-REVIEWERS.md may define an "Output format" / severity
template for finding BODIES — that styles the comment text. It never
overrides the posting requirement.
⚠️ Do NOT Run as a Background Task Agent
The PR review loop MUST run in the main conversation, NOT as a background Task.
Tasks cannot spawn sub-Tasks. If the review loop runs as a Task, agent reviewers (from AGENT-REVIEWERS.md) will silently fail to spawn — they require the Task tool, which is only available in the main conversation.
What to do instead: Execute the review loop steps directly in the main conversation. This allows you to spawn agent reviewer Tasks in parallel (C3 of each round) while keeping Gemini/bot comment handling inline.
Individual agent reviewers (leaf-level Tasks that don't need to spawn further Tasks) should still be spawned via the Task tool — that works because they're called from the main conversation, not from within another Task.
Streamline the push-review-fix cycle for PRs with automated reviewers.
Supported Review Bots
| Bot | Trigger | Priority Format |
|---|---|---|
| Gemini Code Assist | /gemini review comment |
![critical], ![high], ![medium], ![low] |
| Cursor Bugbot | Auto on push | <!-- **High Severity** -->, ### Bug: |
| Claude | Manual via script | 🚨, **Critical**, ### Critical Issues, ⚠️ |
Priority detection automatically parses all formats when summarizing and fetching comments.
Gemini and Cursor can each be turned off per repo via # Configuration .bots
in the root AGENT-REVIEWERS.md — see "Disabling an External Review Bot". A
disabled bot is never triggered and never waited for.
Priority to Exit-Condition Mapping
The quality-weighted exit condition (see ONE MORE LOOP Rule) depends on classifying findings as P1/P2 vs P3/nitpick. Use this table:
| Source label | P-level | Blocks quality-weighted exit? |
|---|---|---|
Gemini ![critical], ![high] |
P1/P2 | Yes — must be resolved or merged with explicit user sign-off |
Gemini ![medium] |
P2 if correctness/security/breaking; else P3 | Yes if correctness/security/breaking; no if style/prose |
Gemini ![low] |
P3/nitpick | No |
Cursor **High Severity**, ### Bug: |
P1/P2 | Yes |
Claude 🚨, ### Critical Issues, **Critical** |
P1/P2 | Yes |
Claude ⚠️ |
P2 if correctness/security/breaking; else P3 | Yes if correctness/security/breaking; no if style/prose |
| Agent comment, no explicit label | Infer from content: correctness/security/breaking → P1/P2; else P3 | Per inferred level |
"Won't fix" on a P1/P2 finding does NOT resolve it on its own — it still blocks quality-weighted exit unless (i) the finding is reclassified to P3 with explicit justification (drop one P-level with reasoning), (ii) the finding is actually fixed in a later round, or (iii) the user explicitly signs off on the carry-forward, in which case the finding is recorded as an acknowledged unresolved item in the merge-readiness summary.
The (iii) escape valve exists so the model isn't forced to relabel a genuine "won't fix" as a reclassification: surface the finding to the user, they sign off, it's recorded in the merge-readiness summary.
Classifying ![medium] (Gemini's most-used label) — use these heuristics:
- P2 (blocking): the comment describes a correctness bug, security concern, breaking change, data loss risk, or incorrect error handling. Example: "this will return nil for empty input", "missing null check could crash on production data", "env var not validated before use".
- P3 (non-blocking): the comment describes naming, phrasing, formatting, alternative implementations of equivalent behavior, documentation polish, or preference-level style. Example: "variable name is ambiguous", "consider using map over for-loop", "comment could be clearer".
- When ambiguous: default to P2 on the first round; downgrade to P3 only if the fix is a judgment call, not a correctness improvement.
Comment Formats: Line Comments vs PR Comments
Different bots use different comment formats:
Line Comments (Gemini, Cursor)
- Posted on specific lines of the diff
- Each issue is a separate comment thread
- Reply using
reply-to-comment.sh <PR> <comment-id> "Fixed" - Thread auto-resolves when replied to
PR Comments (Claude)
- Single large comment on the PR (not on specific lines)
- Contains multiple issues organized by sections (e.g., "Issues & Concerns")
- Issues reference file:line locations in prose (e.g., "Location:
role_summaries_controller.rb:60") - Reply to the comment addressing multiple issues at once
Handling Claude's PR comments:
Fetch the comment to get the comment ID:
gh pr view <PR> --json comments --jq '.comments[] | select(.author.login == "claude") | {id: .id, body: .body}'Parse issues from the structured markdown:
- Look for numbered headings like
### **1. BREAKING CHANGE: ...** - Extract file:line references from
**Location:**lines - Note priority indicators: 🚨 (critical), ⚠️ (warning), etc.
- Look for numbered headings like
Reply with a consolidated response addressing each issue:
gh pr comment <PR> --body "## Response to Claude Review **Issue 1 (Breaking API change):** Fixed - added deprecation support for old parameter name **Issue 2 (Missing validation):** Fixed - added ALLOWED_VIEW_ROLES validation **Issue 3 (Logic change):** Won't fix - this is intentional, documented in PR description **Issue 4 (Missing tests):** Fixed - added RSpec tests for SystemRoles methods "Individual issues can also be addressed via separate replies if preferred:
gh pr comment <PR> --body "**Re: Issue #2 (Missing Input Validation):** Fixed in commit abc123 - added validation for view_role parameter"
The key difference: Claude comments don't have "threads" to resolve - you reply to the main PR comment referencing which issues you're addressing.
Critical: Be Skeptical of Reviews
Not all suggestions are good. Evaluate each review comment critically:
- Does this actually improve the code, or is it pedantic?
- Is this suggestion appropriate for the project's context?
- Would implementing this introduce unnecessary complexity?
Skip suggestions that are:
- Platform-specific when not applicable (Windows comments for Linux-only code)
- Overly defensive (excessive null checks, unlikely edge cases)
- Stylistic preferences that don't match project conventions
- Adding documentation for self-explanatory code
When in doubt, ask the user rather than blindly applying changes.
Self-Contradiction Detection
Track changes across rounds. When a fix in round N reverses or conflicts with a fix from a previous round, this signals that the review loop may be degrading the code rather than improving it.
When a contradiction is detected:
- Identify all involved changes: List the original code, the round N-K change, and the round N change that contradicts it.
- Analyze both positions: Each round may have had valid reasoning. Assess whether the later round caught a genuine mistake in the earlier fix, or whether the loop is oscillating.
- Check against the original: Compare both the round N-K and round N versions against the original pre-review-loop code. Often the original is the correct version.
- Report to the user with a clear summary: what changed, what contradicted it, your assessment of which version is correct and why, and a recommendation.
- Do not silently apply the contradicting change. Pause and get user input.
Parallel valid findings: Multiple reviewers may independently flag different aspects of the same code. This is not a contradiction — it's convergent analysis. The key distinction is whether round N is undoing round N-K's work (contradiction) vs. addressing a different concern in nearby code (parallel findings).
Pattern Analysis: Sweep Before Fixing
When Gemini (or any reviewer) raises a finding, ask: is this finding symptomatic of a broader pattern, or is it truly isolated? Acting on this question before fixing prevents the same pattern from appearing in multiple subsequent rounds.
Batch Before Acting
Do not fix comments one-at-a-time. After collecting all comments for a round (Gemini + other bots + agents), list them together before editing any file:
- Identify patterns across comments (same issue type, multiple files or lines) — plan one sweep fix, not N individual fixes.
- For each planned fix, re-read the new text through each active agent's lens before staging: would code-reviewer flag this phrasing? Would comment-analyzer flag a stale assertion? Revise until the fix itself wouldn't draw a new comment.
- Commit once per round, not once per comment. Note deliberate trade-offs in the commit message body so reviewers see the reasoning rather than re-flagging it.
Indicators of a Broader Pattern
A finding is likely a pattern when:
- It is about a code/naming style (camelCase vs kebab-case,
.foo[]vs.foo[]?, etc.) — style issues almost always recur across files - It is about a consistency rule between files (file A has updated terminology that file B hasn't adopted) — consistency gaps spread
- It is about a structural anti-pattern in examples (missing error handling, incorrect operator usage) — structural patterns repeat
- The comment body says "similar to X elsewhere" or "for consistency with Y"
A finding is likely isolated when:
- It is a specific factual error at one location (e.g., a wrong version number) — unlikely to recur in the same way
- It is about a single missing detail unique to one code path
- It is a judgment call about documentation tone
When a Finding Targets Unchanged Content
If a reviewer flags an issue on content not modified in recent pushes, their initial review pass was incomplete:
- Widen the sweep to the full original PR diff — not just recently-changed files.
- Consider an explicit full-diff review trigger: push-triggered auto-reviews anchor on recently-changed files. Running
trigger-review.sh <PR> --waitprompts a review of the full PR diff and can surface remaining issues sooner. Use the script, not a hand-written/gemini reviewcomment — the script is what honors# Configuration .bots, so posting the comment yourself triggers a bot the repo has disabled (and bills the user for it) with nothing to warn you.
When a Finding Looks Like a Pattern
Before replying to the reviewer or making the fix:
- Sweep all changed files (and closely related files) for the same pattern using grep or a targeted search.
- Fix all occurrences in one commit rather than one per reviewer round. Multiple rounds for the same pattern means this step was skipped.
- Decide whether to re-run a targeted agent-reviewer:
- Re-run if: the pattern was something the agent was supposed to catch (e.g., code-reviewer for guideline violations, silent-failure-hunter for error handling) AND enough new lines were added or changed that a targeted re-run adds coverage
- Skip re-run if: the agent already ran and addressed this area, and the fix is narrow enough that no new review surface was introduced
Examples
| Reviewer Finding | Pattern Type | Sweep Action |
|---|---|---|
.items[] should be .items[]? in config.yaml |
Style/structural | Grep all changed files for .items[] without ? |
userName should be user-name in api.md:42 |
Naming style | Grep all changed files for userName |
Field added to schema in models.py but the docs in README.md still show the old shape |
Cross-file consistency | Check related diagrams/tables in all files modified by the PR |
| Wrong version number at one location | Isolated factual | Fix in place; no sweep needed |
Stopping Heuristics
Use signal quality — not a fixed round cap — to decide when to stop iterating.
Per-Round Assessment
After each round, evaluate:
| Metric | What It Means |
|---|---|
| Fix/rejection ratio | What fraction of comments led to actual code fixes vs. "Won't fix" responses? A declining ratio suggests diminishing returns. |
| Severity trend | Are new comments addressing high-priority issues (correctness, security) or low-priority nitpicks (style, documentation)? |
| Contradiction count | Has this round contradicted any previous round's fixes? If so, investigate before continuing. |
| Net code quality | Is the code measurably better than after the previous round? Or are changes lateral (different but not better)? |
When to Continue
- The current round produced fixes for genuine correctness or security issues
- New comments are addressing aspects not previously reviewed
- The fix/rejection ratio remains above ~50% (most comments are actionable)
When to Stop
- Two consecutive rounds with zero actionable (P1/P2) fixes — i.e., only nitpicks, only "Won't fix" responses, or zero-comment rounds
- A self-contradiction is detected (pause for user input)
- The fix/rejection ratio drops below ~25% (most comments are not actionable)
- All remaining comments are stylistic or theoretical
- The Hard Round Ceiling has fired (see below) — stop regardless of other signals
Hard Round Ceiling (Circuit Breaker)
If you reach 7 total rounds, STOP the loop regardless of state. This is a pure circuit breaker — the quality-weighted exit condition (see ONE MORE LOOP Rule) handles normal termination earlier; this fires only when the loop is stuck. Report to the user:
- Rounds completed and elapsed time
- Total comments received, by priority (P1/P2/P3 — see Priority to Exit-Condition Mapping) and source (Gemini, other bots, each agent)
- Outstanding unresolved items (if any)
- A recommendation on whether to continue, declare "good enough," or escalate
Then ask the user before proceeding further.
ONE MORE LOOP Rule
When a full round (Gemini + other bots + agent reviewers) produces no actionable feedback, do ONE additional "final verification" round to catch any last feedback from the final push.
Actionable feedback = a P1 or P2 finding (per the Priority to Exit-Condition Mapping) that is addressed with a code change. "Won't fix" responses, nitpick (P3) fixes, and zero-comment rounds are NOT actionable for loop-control purposes — they all count toward exit condition (b) below.
Unifying with stopping heuristics: The "Two consecutive rounds with zero actionable (P1/P2) fixes" stopping heuristic and ONE MORE LOOP describe the same exit mechanism from two angles:
- The first qualifying round (zero actionable fixes — i.e., only nitpicks, only "Won't fix", or zero comments) IS the ONE MORE LOOP trigger.
- The second qualifying round IS the final verification — if the full Exit condition (quality-weighted) below is satisfied (all of (a) through (d)), you exit immediately at end of that round. No third round needed.
Tracking state: Use TaskCreate to track whether you're in the "final verification round". Create a task like "Final verification round - if no actionable feedback, ready to merge".
Reset condition: If the final verification round produces P1 or P2 fixes (correctness, security, breaking changes — see Priority to Exit-Condition Mapping), remove the "final verification round" task — you need a fresh "one more" after pushing those fixes. Nitpick-level (P3) fixes do NOT reset the counter.
Exit condition (quality-weighted): You're done when ALL of:
- (a) No P1/P2 findings (correctness, security, breaking changes) in the last round
- (b) The last two rounds had zero actionable (P1/P2) fixes — i.e., they contained only nitpicks, were zero-comment rounds, or all feedback was "Won't fix"
- (c) No contradictions across rounds
- (d) No unresolved P1/P2 Won't-fix findings carried forward from any prior round — every Won't-fix on a P1/P2 must have been (i) reclassified to P3 with explicit justification per the Priority Mapping rule, (ii) actually fixed in a later round, or (iii) explicitly signed off on by the user as an acknowledged carry-forward (recorded in the merge-readiness summary). Carried-forward Won't-fix on a real P1/P2 without one of these three resolutions blocks exit regardless of (a) and (b).
— OR the Hard Round Ceiling has fired (see above).
Proceed to merge readiness checks.
Merge Readiness
When the review loop is complete, check CI status and read repo-specific merge guidance before proceeding.
1. Check CI Status
gh pr checks <PR> --watch
Do not proceed to merge if any checks are still running or failing. If CI is still running or newly failing at this point, fall back to the CI Fix Loop to diagnose and fix before reporting readiness.
2. Read Repo-Specific Merge Guidance
Read the repo's CLAUDE.md and follow any linked files it references (e.g., .ai-knowledge/code-review-guidelines.md). These define the repo's merge policy — what approvals are required, whether auto-merge is permitted, attestation requirements, and any other merge criteria.
Use whatever you find to determine:
- Whether this PR is ready to merge or needs further action
- Whether to merge automatically or ask the user
- What information to include in a merge-readiness summary
3. Prepare Merge-Readiness Summary
Always prepare a summary of what the review loop did and observed:
- Review loop activity: Rounds completed, total comments addressed (fixed / won't fix / out of scope)
- CI status: All checks passed, any failures, any checks still running
- Unresolved items: Any review comments that remain open or contested
- Self-contradictions: Any detected during the loop and how they were resolved
- Beads tickets: Out-of-scope items captured for follow-up
- Branch protection status: Whether all required checks and approvals are satisfied
- Stale defaults pin bypass (if
--skip-stale-checkwas used): note the config and current plugin versions; recommend running/pr-review-loop:audit-agents - Posting integrity: total agent findings vs posted line-comment threads vs replied threads, per round. These MUST match (every finding posted, every thread replied). Any round where findings were fixed without posted threads is a protocol violation — report it explicitly, never paper over it.
- Validator activity (if
independent_validator.enabled): per-flagger acceptance rate (X of Y posted findings survived; Z withdrawn as INVALID; W annotated [validator: uncertain]). A flagger with persistent low acceptance is a candidate for retirement or prompt refinement.
If repo-specific guidance defines additional merge criteria (attestation requirements, approval types, etc.), include the status of those criteria in the summary as well.
4. Merge or Ask
- If repo guidance authorizes auto-merge and all its criteria are met (CI passed, required approvals present, branch protections satisfied): merge
- If any criteria are not met, or no repo-specific guidance exists: present the summary and ask the user
The review loop should not override branch protections or bypass repo-defined merge requirements.
Autonomous Loop Workflow
CRITICAL RULES - NEVER VIOLATE THESE:
- ALWAYS use full absolute paths for scripts - Glob once to find the scripts directory, then inline the full path in every Bash call. NEVER use variables or compound commands (see setup at top of document)
- ALWAYS use
commit-and-push.sh- NEVERgit commitorgit push(see table at top of document) - ALWAYS reply to EVERY comment:
- Line comments (Gemini, agents): use
reply-to-comment.sh - PR comments (Claude): use
gh pr commentwith consolidated response
- Line comments (Gemini, agents): use
- ALWAYS use
--waitflag when checking for comments - this ensures proper 5-minute polling - PR creation automatically triggers Gemini review - use
get-review-comments.sh --waitto wait for the first review
Pre-Loop Setup (do once, before round 1)
- Discover and merge agents. Run
scripts/discover-agents.sh <PR>once. Its output includes:- The full merged agent list (defaults + user agents per C+E semantics)
- The
configurationblock withstale_pin,defaults_version_checked,current_plugin_version, counts - The
language_detectionblock (nullwhen the repo already has anyAGENT-REVIEWERS.md; otherwise{matched: [...], same_directory_polyglot: bool})
- Offer language templates (only when
language_detection != nullANDlanguage_detection.matchedis non-empty). See "Language Template Offer" below for the full flow. - Check the stale pin. If
configuration.stale_pinistrueAND--skip-stale-checkwas not passed:- Emit the stale-pin message (see "Stale Pin Detection" above) with current and pinned versions
- Exit non-zero. Do NOT proceed to round 1.
- Note: when step 2 just installed a template,
install-template.shpinneddefaults_version_checkedto the current plugin version, so the stale-pin check passes by construction.
- Emit the spawning summary. Once cleared to proceed, log a one-line summary of which agents will run (see "Spawning Summary" above).
- Track --skip-stale-check usage. If the bypass flag was used, remember to include the bypass note in the final merge-readiness summary.
After setup, proceed to The Loop.
The Loop
⚠️ CRITICAL: Each round includes ALL reviewer types before triggering the next cycle.
DO NOT run multiple Gemini rounds before checking other reviewers. After addressing Gemini comments, you MUST check for other bot comments and run agent reviewers BEFORE triggering another Gemini review. This interleaving is essential because:
- Fixes made for Gemini may resolve issues other reviewers would have flagged
- Running all Gemini rounds first makes other reviewer feedback stale and irrelevant
EACH ROUND — three phases, in order:
COLLECT PHASE (no edits yet):
┌─────────────────────────────────────────────────────────────┐
│ C1. Get Gemini comments (--wait only on first check) │
│ C2. Check for other bot PR comments (Claude, Cursor, etc.) │
│ C3. Run agent reviewers (defaults + AGENT-REVIEWERS.md); │
│ agents POST findings as line comments + return their │
│ manifests; then validate each POSTED finding (refuted │
│ → withdrawn on-thread) │
└─────────────────────────────────────────────────────────────┘
│
▼
⚠️ BATCH POINT — apply "Batch Before Acting" before any edit:
list all collected comments together, identify cross-source
patterns, plan fixes as a group, self-review each fix through
each active agent's lens before staging.
│
▼
FIX PHASE (apply the batched plan):
┌─────────────────────────────────────────────────────────────┐
│ F1. Apply + reply to Gemini comments │
│ F2. Apply + reply to other bot comments │
│ F3. Apply + reply to agent comments │
│ F4. Commit and push ONCE (if ANY fixes were made) │
│ F5. Wait for CI checks; fix failures (max 3 CI retries) │
│ F6. Trigger next review (--wait) │
│ F7. Inspect F6's output BEFORE applying exit conditions │
└─────────────────────────────────────────────────────────────┘
│
▼
If F6 returned new comments → next COLLECT PHASE (new round).
Otherwise → apply quality-weighted exit condition + Hard Round
Ceiling check (see ONE MORE LOOP Rule in Stopping Heuristics).
Phase order is mandatory. Complete all COLLECT steps (C1, C2, C3) before beginning any FIX step (F1–F7). The BATCH POINT between them is what makes Pattern Analysis (Sweep Before Fixing) work.
For full step-by-step details with commands and example outputs, see references/round-workflow.md. The diagram above is the authoritative execution order; the reference file is the operational companion the model can read on demand when actually running a round.
COMPLETION: When a full round produces no actionable feedback (Gemini + other bots + agents all stable) AND this was the "final verification" round:
- Report all beads tickets created during the loop (if any)
- Ask user about merge
CI Failure Handling
CI Fix Loop
CI FIX SUB-LOOP (max 3 attempts per round):
┌──────────────────────────────────────────────────┐
│ 1. Run check-ci.sh <PR> --wait │
│ 2. If passed → continue to trigger next review │
│ 3. If failed: │
│ a. Read the failure logs from check-ci output │
│ b. Diagnose the root cause │
│ c. Apply a MINIMAL, TARGETED fix │
│ d. commit-and-push.sh "fix: CI failure desc" │
│ e. Go to step 1 (decrement retry counter) │
│ 4. If max retries exhausted → STOP and ask user │
└──────────────────────────────────────────────────┘
Rules for CI Fixes
Be conservative — minimal fixes only; don't change behavior beyond what's needed to pass CI.
Diagnose before fixing — Read the failure logs carefully. Common CI failures:
- Lint/format errors: Run the linter/formatter locally, commit the fix
- Test failures: Read the failing test, understand what broke, fix the regression
- Type errors: Fix the specific type issue flagged
- Build failures: Fix the compilation/build error
Don't break the PR to fix CI — If a CI fix would require significant changes that alter PR behavior, STOP and ask the user. Examples:
- A test is failing because the PR intentionally changed behavior (test needs updating, not the code)
- A lint rule conflicts with the PR's approach (may need a targeted disable)
- CI infrastructure issues (flaky tests, service outages) — not your problem to fix
Max 3 attempts — If CI still fails after 3 fix-push cycles, stop and escalate to the user.
Timeout (exit code 2) — If
check-ci.shtimes out waiting for checks to complete, ask the user whether to wait longer.
Example CI Fix Flow
# After pushing review fixes
scripts/check-ci.sh <PR> --wait
# Output shows: ✗ CI checks failed (1/4 failed)
# === FAILED CI CHECKS ===
# --- lint (FAILURE) ---
# src/utils.py:42:1: E302 expected 2 blank lines, got 1
# Fix it
# (edit the file to add the missing blank line)
# Push the fix
scripts/commit-and-push.sh "fix: lint error in utils.py"
# Check again
scripts/check-ci.sh <PR> --wait
# ✓ All CI checks passed (4/4)
# → Proceed to trigger next review
Reply Templates
For Line Comments (Gemini, agent reviewers)
ALWAYS reply using reply-to-comment.sh. Templates:
- Fixed: "Fixed - [description]"
- Won't fix (bad suggestion): "Won't fix - [reason]"
- Out of scope (good suggestion): "Out of scope - tracked in BD-XXX" (see below)
- Deferred: "Good catch, tracking in #issue"
- Acknowledged: "Acknowledged - [explanation]"
For PR Comments (Claude)
Reply using gh pr comment with a consolidated response:
## Response to Claude Review
**Issue 1 (Breaking API change):** Fixed - added backward compatibility
**Issue 2 (Missing validation):** Fixed - added input validation in controller
**Issue 3 (Behavior change):** Won't fix - intentional, see PR description
**Issue 4 (Missing tests):** Out of scope - tracked in BD-XXX
Out of Scope Suggestions (Beads Integration)
When a review suggestion is good but outside the scope of this PR, capture it for later rather than losing it.
Decision tree:
- Is this suggestion valid and would improve the code? → If NO, use "Won't fix"
- Does it belong in this PR? → If YES, fix it
- It's a good suggestion but out of scope → Create a beads ticket if available
Check if beads is installed:
bd --version
If beads is available, create a ticket:
bd create
Include in the ticket:
- Title: Brief description of the suggested improvement
- Description: Must include ALL context needed to fix the issue later:
- The original review comment text
- File path and line number(s) affected
- PR number and link for context
- The gh command to fetch the exact comment:
gh api /repos/{owner}/{repo}/pulls/{PR}/comments/{comment-id} - Any relevant code snippets or context from the current PR
Then reply to the comment:
scripts/reply-to-comment.sh <PR> <comment-id> "Out of scope - tracked in BD-XXX"
Track the ticket for reporting at the end of the review loop. Keep a list of all created tickets (ID and title) to include in the completion summary.
If beads is NOT installed:
- Still reply to the comment noting the finding is out of scope:
scripts/reply-to-comment.sh <PR> <comment-id> "Out of scope for this PR - see review loop completion summary" - Collect all out-of-scope findings with full context (original comment text, file paths, line numbers, PR number, reviewer, and the
gh apicommand to fetch the comment). Track these viaTaskCreate(one task per finding, with the full context in the task body) so they survive across rounds. - In the completion summary, list all out-of-scope findings and recommend installing beads to track them. Offer to create a follow-up PR that:
- Creates a markdown file (e.g.,
TODO-beads.md) with pre-filledbd createcommands for each out-of-scope finding, using the context collected in step 2 - Includes instructions for the user to run
bd initand execute the commands in the markdown file
- Creates a markdown file (e.g.,
- If the user agrees, create a follow-up PR containing the generated markdown file and documentation updates explaining how users can install and use beads.
Triggering Reviews by Bot Type
Gemini Code Assist (Default)
scripts/trigger-review.sh <PR> --gemini --wait
The script uses two checks to avoid redundant /gemini review triggers and conserve quota (33 reviews/day on free tier):
- Commit check: If Gemini has already reviewed the current HEAD commit, skips the trigger entirely.
- Config check: If
.gemini/config.yamlexists withcode_review: true, the repo has auto-review enabled. When--waitis used, the script waits one poll interval for an auto-review before falling back to a manual trigger.
When quota is exceeded, the skill automatically detects this and suggests alternatives.
Cursor Bugbot
scripts/trigger-review.sh <PR> --cursor --wait
Cursor auto-reviews on push, so --cursor just waits for comments to appear (typically 1-2 minutes).
Claude Agent (Fallback)
scripts/trigger-review.sh <PR> --claude
Uses a Claude agent to review the PR and post comments. Useful when:
- Gemini is rate-limited
- You want a different perspective
- Cursor isn't configured on the repo
Claude Review Workflow
When using Claude fallback:
Run the script to get the prompt:
scripts/claude-review.sh <PR>Use the Task tool with the generated prompt:
Task tool: subagent_type: general-purpose description: Review PR #<PR> prompt: (copy from /tmp/claude_review_prompt_<PR>.txt)The agent will post the review as a PR comment
Continue the normal review loop - address comments using
reply-to-comment.sh
Agent Reviewers (Default + Custom Focused Reviews)
The skill ships 6 default specialist reviewer agents that spawn automatically. Users can author additional agents — or override / disable defaults — via AGENT-REVIEWERS.md.
Default Specialist Reviewers
Located in plugins/pr-review-loop/agents/. Each is a native subagent with frontmatter (name, description, model, color) plus our standard focus/check/flag/skip/suggest template, with touch-it-you-own-it scope rules baked in:
| Default | Model | Focus |
|---|---|---|
code-reviewer |
sonnet | CLAUDE.md compliance + significant bugs; confidence ≥80; quote-the-rule forcing function |
silent-failure-hunter |
opus | Empty / broad catches, optional-chain swallowing, fallback masking |
pr-test-analyzer |
opus | Behavioral coverage gaps, criticality 1-10 |
comment-analyzer |
sonnet | Factual accuracy, comment rot, value-free comments |
type-design-analyzer |
sonnet | Encapsulation, invariant expression / usefulness / enforcement (4 axes 1-10) |
code-simplifier |
opus | Genuine complexity / nested ternaries / dead code; behavior-preserving |
Defaults always spawn unless explicitly overridden or disabled (see "Override and Configuration Semantics" below).
Override and Configuration Semantics (C+E)
User-authored agents in AGENT-REVIEWERS.md compose with defaults via these rules:
| User repo state | What spawns |
|---|---|
No AGENT-REVIEWERS.md |
All 6 defaults |
AGENT-REVIEWERS.md with no # Agents section |
All 6 defaults + the user's # Guidelines / # Context apply |
AGENT-REVIEWERS.md with new custom agents under # Agents |
All 6 defaults + user's custom agents (additive) |
User agent has the same name as a default (## code-reviewer) |
User's version replaces that default; the other 5 still spawn |
User has # Configuration with disabled: ["pr-test-analyzer"] |
5 defaults spawn (pr-test-analyzer skipped) |
| Hierarchical scoping (subtree-specific overrides) | Per-subdirectory rules apply as before; defaults always live at scope / |
# Configuration Section
Project-level config lives in a # Configuration H1 section in the root AGENT-REVIEWERS.md. The section contains a fenced JSON block. Subdirectory AGENT-REVIEWERS.md configurations are warned about and ignored.
# Configuration
```json
{
"defaults_version_checked": "1.2.0",
"disabled": ["pr-test-analyzer"],
"overlap_acknowledged": {
"my_pci_auditor": {
"overlaps_with": "security-reviewer",
"reason": "PCI-compliance-specific scope; we want both running because the default is general security"
}
},
"independent_validator": {
"enabled": true,
"skip_for": ["code-simplifier"],
"uncertain_action": "post_with_annotation"
},
"bots": {
"gemini": false
}
}
```
Fields:
defaults_version_checked— plugin version (matches the value in.claude-plugin/plugin.json) whose defaults the user has reviewed. The/pr-review-loop:audit-agentstool (q2h) bumps this when the user accepts/rejects each recommendation.disabled— list of default agent names the user does NOT want spawned. Per-name opt-out.overlap_acknowledged— map from a user agent name to{ overlaps_with, reason }. Both agents continue to spawn; this entry documents intentional duplication so the audit tool doesn't recommend renaming.reasonis REQUIRED — the parser rejects entries without it, so future readers see why both agents are intentionally running.independent_validator— controls the per-finding validation step (see "Independent Validator Pipeline" below). All three nested fields are optional; defaults areenabled: true,skip_for: [],uncertain_action: "post_with_annotation".bots— map of external review bot name (gemini,cursor) to boolean. Bots default to enabled; an explicitfalseturns one off for the repo. See "Disabling an External Review Bot" below.
Disabling an External Review Bot
Not every repo has Gemini Code Assist or Cursor installed (or wants to pay for
them). Turn one off with # Configuration .bots:
{ "bots": { "gemini": false } }
A disabled bot means, for the whole loop:
- Do NOT post
/gemini reviewor any other manual trigger comment for it — not via a script, not viagh pr comment. - Do NOT wait for its review.
trigger-review.sh <PR> --geminiexits immediately,commit-and-push.sh --trigger-reviewskips the trigger, andget-review-comments.sh --waitskips its 5-minute poll when every external bot is disabled. - Skip its COLLECT step. With Gemini off, C1 stops being a wait on Gemini. Drop
--waitonly when every external bot is off — if Cursor is still enabled it auto-reviews on push and C1 must keep waiting for it. With all of them off, C1 is a plain fetch of existing line comments (agent-posted threads from prior ro
…(truncated)