PR Patrol
Process automated PR review comments through a 7-gate workflow with phase-based loading.
⚠️ CRITICAL REMINDERS
- READ PHASE FILE FIRST - Check status in state file, read
phases/gate-{N}.mdfor current phase - USE AGENTS FOR VALIDATION - Use
bot-comment-validatorvia Task tool, never manual - ASK ABOUT PR-REVIEW AGENTS - After checks pass, ask about
code-reviewer/silent-failure-hunter - ISSUE COMMENTS NEED @MENTION - GitHub issue comments don't support threading
- UPDATE STATE FILE - After EVERY action (use
scripts/update_state.sh) - NEVER REPLY TO COPILOT - Silent fix only, no reactions, no replies
- TRUST SCRIPT OUTPUT - When scripts return data, DO NOT make extra API calls to "verify"!
Quick Reference
| Bot | Login | Reaction | Reply |
|---|---|---|---|
| CodeRabbit | coderabbitai[bot] |
❌ | ✅ |
| Greptile | greptile-apps[bot] |
👍/👎 FIRST | ✅ THEN |
| Codex | chatgpt-codex-connector[bot] |
👍/👎 FIRST | ✅ THEN |
| Copilot | Copilot |
❌ NEVER | ❌ NEVER |
| Sentry | sentry[bot] |
👍/👎 FIRST | ✅ THEN |
Ignored: vercel[bot], dependabot[bot], renovate[bot], github-actions[bot]
Workflow Diagram
/pr-patrol [PR#]
│
▼
┌─ GATE 0: Init ──────────────────────┐
│ [AskUserQuestion] Mode, scope │ → phases/gate-0-init.md
└─────────────────────────────────────┘
│
▼
┌─ GATE 1: Collect ───────────────────┐
│ [AskUserQuestion] Proceed? │ → phases/gate-1-collect.md
└─────────────────────────────────────┘
│
▼
┌─ GATE 2: Validate ──────────────────┐
│ [Task: bot-comment-validator] │ → phases/gate-2-validate.md
│ [AskUserQuestion] Which to fix? │
└─────────────────────────────────────┘
│
▼
┌─ GATE 3: Fix & Check ───────────────┐
│ Apply fixes → typecheck → lint │ → phases/gate-3-fix.md
│ [AskUserQuestion] Run pr-review? │ ← Gate 3.5!
└─────────────────────────────────────┘
│
▼
┌─ GATE 4: Commit ────────────────────┐
│ [AskUserQuestion] Review & commit? │ → phases/gate-4-commit.md
└─────────────────────────────────────┘
│
▼
┌─ GATE 5: Reply ─────────────────────┐
│ [AskUserQuestion] Post replies? │ → phases/gate-5-reply.md
│ Also read: bot-formats.md │
└─────────────────────────────────────┘
│
▼
┌─ GATE 6: Push ──────────────────────┐
│ [AskUserQuestion] Push? Cycle 2? │ → phases/gate-6-push.md
└─────────────────────────────────────┘
Phase Routing (FOLLOW THIS!)
Step 1: Check State File
STATE_FILE=".claude/bot-reviews/PR-{N}.md"
Step 2: Read Current Phase
| Status | Read This Phase File |
|---|---|
| (no file) | phases/gate-0-init.md |
initialized |
phases/gate-1-collect.md |
collected |
phases/gate-2-validate.md |
validated |
phases/gate-3-fix.md |
fixes_planned |
phases/gate-3-fix.md |
fixes_applied |
phases/gate-3-fix.md (run checks) |
checks_passed |
phases/gate-4-commit.md |
committed |
phases/gate-5-reply.md |
replies_sent |
phases/gate-6-push.md |
pushed |
Check for new comments → Cycle 2? |
Step 3: Follow Phase Instructions
Each phase file contains:
- Detailed step-by-step actions
- MANDATORY AskUserQuestion prompts
- Edge cases and error handling
- State file update commands
Scripts Location
SCRIPTS="${CLAUDE_PLUGIN_ROOT}/skills/pr-patrol/scripts"
# Core scripts
"$SCRIPTS/fetch_pr_comments.sh" owner repo pr # Parallel API fetch
"$SCRIPTS/detect_thread_states.sh" # State detection
"$SCRIPTS/check_new_comments.sh" owner repo pr # New since push
"$SCRIPTS/check_reply_status.sh" owner repo pr # Reply tracking
"$SCRIPTS/update_state.sh" file field value # State updates
Reference Files Location
SKILL_ROOT="${CLAUDE_PLUGIN_ROOT}/skills/pr-patrol"
# Read these files with their FULL paths:
"$SKILL_ROOT/phases/gate-{N}.md" # Phase instructions
"$SKILL_ROOT/bot-formats.md" # Bot protocols (CRITICAL for Gate 5!)
"$SKILL_ROOT/templates.md" # Reply templates
| File | Purpose | When to Read |
|---|---|---|
phases/gate-{N}.md |
Phase-specific instructions | Based on status |
bot-formats.md |
Bot-specific protocols | MUST READ before Gate 5 |
templates.md |
Reply message templates | Gate 5 (replies) |
Critical Rules
- EVERY gate requires AskUserQuestion - Never skip user approval
- ALL comments must be validated - Severity only affects priority
- Copilot gets NO response - Silent fix only, no reactions, no replies
- NEVER commit/push without approval - Explicit consent required
- NEVER force push - Blocked completely
- Typecheck/lint are MANDATORY - Block on failure
- ALWAYS use --paginate - Default GitHub API returns only 30 comments
- Fetch BOTH endpoints - Review comments (
/pulls/{pr}/comments) AND issue comments (/issues/{pr}/comments) - Extract embedded CodeRabbit comments - Use
parse_coderabbit_embedded.shfor nitpicks, duplicates, outside-diff - Issue vs PR review comments - Different reply methods! Issue comments need @mention (no threading)
- Reaction BEFORE reply - For Greptile/Codex/Sentry, add reaction first, then reply
- Short reply format - "Fixed in commit {sha}: {description}" NOT verbose essays
- Use helper scripts -
${CLAUDE_PLUGIN_ROOT}/skills/pr-patrol/scripts/has utilities - TRUST SCRIPT OUTPUT - When scripts return data, DO NOT make verification queries
jq Escaping Warning
CRITICAL: Shell escaping corrupts jq operators. This affects:
- Direct
jqcommands gh api --jqflag- Any inline jq in bash
Forbidden Patterns
# ❌ WRONG - "!=" gets escaped to "\!=" → "unexpected token" error
jq '[.[] | select(.field != null)]'
gh api ... --jq '[.[] | select(.in_reply_to_id != null)]'
# ❌ WRONG - Same issue with "not null"
jq 'select(.x != "value")'
Safe Alternatives
# ✅ CORRECT - Use truthy check (implicit null filter)
jq '[.[] | select(.field)]'
# ✅ CORRECT - Use "| not" for negation
jq '[.[] | select(.bot == "Copilot" | not)]'
# ✅ CORRECT - Use external .jq file for complex queries
jq -f "$SCRIPT_DIR/filter.jq"
Rule: For any jq with != or complex logic, use the scripts in scripts/ instead of inline jq.