Pr Review

Use when responding to PR review comments for specified pull request(s)

promptBeginner5 min to valuemarkdown
0 views
Feb 5, 2026

Loading actions...

Prompt Playground

1 Variables

Fill Variables

Preview

---
allowed-tools: [PR_NUMBERS>]ash(git:*), [PR_NUMBERS>]ash(gh:*), [PR_NUMBERS>]ash(python3:*), Task, [PR_NUMBERS>]kill, [PR_NUMBERS>]ead, Write, [PR_NUMBERS>]dit, Glob, Grep
argument-hint: <[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>] [--parallel] [--cleanup] [--dry-run]
description: [PR_NUMBERS>]se when responding to [PR_NUMBERS>][PR_NUMBERS>] review comments for specified pull request(s)
---

# [PR_NUMBERS>][PR_NUMBERS>] [PR_NUMBERS>]eview Command

[PR_NUMBERS>] **[PR_NUMBERS>]ote**: This command uses extended thinking (`ultrathink`) for deep [PR_NUMBERS>][PR_NUMBERS>] analysis.

ultrathink

[PR_NUMBERS>]espond to [PR_NUMBERS>][PR_NUMBERS>] review comments for the specified pull request(s): $A[PR_NUMBERS>]G[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]

## Context

- Current branch: !`git branch --show-current`
- [PR_NUMBERS>]epository: !`gh repo view --json nameWithOwner -q '.nameWithOwner'`
- Authenticated as: !`gh api user -q '.login'`

## Arguments

[PR_NUMBERS>]arse the input: `$A[PR_NUMBERS>]G[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]`

| Argument | Description | Default |
|----------|-------------|---------|
| `[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]` | Comma-separated [PR_NUMBERS>][PR_NUMBERS>] numbers (e.g., `53,141,143`) or `all-open` | [PR_NUMBERS>]equired |
| `--parallel` | [PR_NUMBERS>]se git worktrees for parallel execution | false |
| `--cleanup` | Clean up worktrees after completion | true |
| `--dry-run` | [PR_NUMBERS>]review planned actions without executing (J[PR_NUMBERS>]O[PR_NUMBERS>] output) | false |

## Workflow

### Dry-[PR_NUMBERS>]un [PR_NUMBERS>]ode (--dry-run)

When `--dry-run` is specified, the command gathers all planned actions without executing any GitHub mutations.

**What happens in dry-run mode:**

1. [PR_NUMBERS>]arse and validate [PR_NUMBERS>][PR_NUMBERS>] numbers (same as normal)
2. Gather [PR_NUMBERS>][PR_NUMBERS>] context, comments, and check status (read-only A[PR_NUMBERS>]I calls)
3. Analyze what actions would be taken
4. Output planned actions as J[PR_NUMBERS>]O[PR_NUMBERS>] to stdout

**What does [PR_NUMBERS>]OT happen in dry-run mode:**

- [PR_NUMBERS>]o comments are posted
- [PR_NUMBERS>]o reactions are added
- [PR_NUMBERS>]o labels are applied
- [PR_NUMBERS>]o threads are resolved
- [PR_NUMBERS>]o commits are made
- [PR_NUMBERS>]o git push operations

**J[PR_NUMBERS>]O[PR_NUMBERS>] Output Format:**

```json
{
  "dry[PR_NUMBERS>]run": true,
  "timestamp": "2026-01-19T12:00:00Z",
  "prs": [
    {
      "number": 123,
      "branch": "feat/example",
      "state": "O[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]",
      "mergeable": "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]G[PR_NUMBERS>]A[PR_NUMBERS>]L[PR_NUMBERS>]",
      "planned[PR_NUMBERS>]actions": {
        "comments[PR_NUMBERS>]to[PR_NUMBERS>]post": [
          {
            "thread[PR_NUMBERS>]id": "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx",
            "reply[PR_NUMBERS>]body": "Addressed in commit abc1234",
            "resolve[PR_NUMBERS>]thread": true
          }
        ],
        "reactions[PR_NUMBERS>]to[PR_NUMBERS>]add": [
          {
            "comment[PR_NUMBERS>]id": "IC[PR_NUMBERS>]abc123",
            "reaction": "eyes"
          }
        ],
        "labels[PR_NUMBERS>]to[PR_NUMBERS>]apply": ["needs-review"],
        "status[PR_NUMBERS>]updates": [
          {
            "action": "resolve[PR_NUMBERS>]thread",
            "thread[PR_NUMBERS>]id": "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]yyy"
          }
        ]
      },
      "unaddressed[PR_NUMBERS>]comments": [
        {
          "id": "IC[PR_NUMBERS>]abc123",
          "author": "reviewer",
          "body": "[PR_NUMBERS>]lease fix this issue",
          "domain": "[PR_NUMBERS>]ug",
          "priority": "[PR_NUMBERS>]1"
        }
      ],
      "failing[PR_NUMBERS>]checks": [
        {
          "name": "AI Quality Gate",
          "conclusion": "FAIL[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]",
          "suggested[PR_NUMBERS>]action": "Address code quality findings"
        }
      ]
    }
  ],
  "summary": {
    "total[PR_NUMBERS>]prs": 1,
    "total[PR_NUMBERS>]comments[PR_NUMBERS>]to[PR_NUMBERS>]address": 3,
    "total[PR_NUMBERS>]planned[PR_NUMBERS>]replies": 2,
    "total[PR_NUMBERS>]threads[PR_NUMBERS>]to[PR_NUMBERS>]resolve": 2
  }
}
```

**Dry-run workflow:**

```bash
# [PR_NUMBERS>]tep 1: [PR_NUMBERS>]arse [PR_NUMBERS>][PR_NUMBERS>] numbers (same as normal)
# [PR_NUMBERS>]tep 2: For each [PR_NUMBERS>][PR_NUMBERS>], gather read-only context

for pr in pr[PR_NUMBERS>]numbers:
    # Get [PR_NUMBERS>][PR_NUMBERS>] context (read-only)
    context=$(python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]context.py --pull-request $pr)

    # Get all comments (read-only)
    comments=$(python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]comments.py --pull-request $pr --group-by-domain --include-issue-comments)

    # Get unaddressed comments (read-only)
    unaddressed=$(python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]unaddressed[PR_NUMBERS>]comments.py --pull-request $pr)

    # Get failing checks (read-only)
    checks=$(python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]checks.py --pull-request $pr)

    # Analyze and collect planned actions (no mutations)
    # Output J[PR_NUMBERS>]O[PR_NUMBERS>] with planned actions
done

# Output consolidated J[PR_NUMBERS>]O[PR_NUMBERS>] to stdout
```

**[PR_NUMBERS>]sage examples:**

```bash
# [PR_NUMBERS>]review actions for a single [PR_NUMBERS>][PR_NUMBERS>]
/pr-review 123 --dry-run

# [PR_NUMBERS>]review actions for multiple [PR_NUMBERS>][PR_NUMBERS>]s
/pr-review 123,456,789 --dry-run

# [PR_NUMBERS>]review all open [PR_NUMBERS>][PR_NUMBERS>]s
/pr-review all-open --dry-run
```

**[PR_NUMBERS>]xit after dry-run:** When `--dry-run` is specified, output the J[PR_NUMBERS>]O[PR_NUMBERS>] and exit. Do not proceed to actual execution steps.

### [PR_NUMBERS>]tep 1: [PR_NUMBERS>]arse and Validate [PR_NUMBERS>][PR_NUMBERS>]s

For `all-open`, query: `gh pr list --state open --json number,reviewDecision`

For each [PR_NUMBERS>][PR_NUMBERS>] number, validate using:

```bash
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]context.py --pull-request {number}
```

Verify: [PR_NUMBERS>][PR_NUMBERS>] exists, is open (state != [PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]G[PR_NUMBERS>]D, CLO[PR_NUMBERS>][PR_NUMBERS>]D), targets current repo.

**C[PR_NUMBERS>]ITICAL - Verify [PR_NUMBERS>][PR_NUMBERS>] [PR_NUMBERS>]erge [PR_NUMBERS>]tate (pr-review-007-merge-state-verification)**:

[PR_NUMBERS>]efore proceeding with review work, verify [PR_NUMBERS>][PR_NUMBERS>] has not been merged via GraphQL (source of truth):

```bash
# Check merge state via test[PR_NUMBERS>]pr[PR_NUMBERS>]merged.py
python3 .claude/skills/github/scripts/pr/test[PR_NUMBERS>]pr[PR_NUMBERS>]merged.py --pull-request {number}
# [PR_NUMBERS>]xit code 0 = not merged (safe to proceed), 1 = merged (skip)

if [ $? -eq 1 ]; then
    echo "[PR_NUMBERS>][PR_NUMBERS>] #{number} is already merged. [PR_NUMBERS>]kipping review work."
    continue
fi
```

**Why this matters**: `gh pr view --json state` may return stale "O[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]" for recently merged [PR_NUMBERS>][PR_NUMBERS>]s, leading to wasted effort (see Issue #321, [PR_NUMBERS>]ession 85).

### [PR_NUMBERS>]tep 1.5: Comprehensive [PR_NUMBERS>][PR_NUMBERS>] [PR_NUMBERS>]tatus Check ([PR_NUMBERS>][PR_NUMBERS>]Q[PR_NUMBERS>]I[PR_NUMBERS>][PR_NUMBERS>]D)

[PR_NUMBERS>]efore addressing comments, gather full [PR_NUMBERS>][PR_NUMBERS>] context:

**1. [PR_NUMBERS>]eview ALL Comments** (review comments + [PR_NUMBERS>][PR_NUMBERS>] comments):

```bash
# Get review threads with resolution status
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]threads.py --pull-request {number}

# Get unresolved review threads
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]unresolved[PR_NUMBERS>]review[PR_NUMBERS>]threads.py --pull-request {number}

# Get unaddressed comments (comments without replies)
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]unaddressed[PR_NUMBERS>]comments.py --pull-request {number}

# Get full [PR_NUMBERS>][PR_NUMBERS>] context including comments
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]context.py --pull-request {number}
```

**2. Check [PR_NUMBERS>]erge [PR_NUMBERS>]ligibility with [PR_NUMBERS>]ase [PR_NUMBERS>]ranch**:

```bash
# Get [PR_NUMBERS>][PR_NUMBERS>] context including merge state
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]context.py --pull-request {number}
# Check: "mergeable" should be "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]G[PR_NUMBERS>]A[PR_NUMBERS>]L[PR_NUMBERS>]"
# Check: "merge[PR_NUMBERS>]state[PR_NUMBERS>]status" for conflicts

# Verify [PR_NUMBERS>][PR_NUMBERS>] is not already merged
python3 .claude/skills/github/scripts/pr/test[PR_NUMBERS>]pr[PR_NUMBERS>]merged.py --pull-request {number}
# [PR_NUMBERS>]xit code 0 = not merged (safe to proceed), 1 = merged (skip)
```

**3. [PR_NUMBERS>]eview ALL Failing Checks**:

```bash
# Get all checks with conclusions using get[PR_NUMBERS>]pr[PR_NUMBERS>]checks.py
python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]checks.py --pull-request {number}
# Output is J[PR_NUMBERS>]O[PR_NUMBERS>] with FailedCount, All[PR_NUMBERS>]assing, and Checks array

# For each failing check, investigate:
# - If session validation: [PR_NUMBERS>]se session-log-fixer skill
# - If AI reviewer: Check for infrastructure vs code quality issues
# - If [PR_NUMBERS>]ester/pytest tests: [PR_NUMBERS>]un tests locally to verify
# - If linting: [PR_NUMBERS>]un npx markdownlint-cli2 --fix
```

**Action on failures**:

| Check Type | Failure Action |
|------------|----------------|
| [PR_NUMBERS>]ession validation | Invoke `session-log-fixer` skill |
| AI reviewer (infra) | [PR_NUMBERS>]ay be transient; note and continue |
| AI reviewer (code quality) | Address findings or acknowledge |
| Tests ([PR_NUMBERS>]ester/pytest) | [PR_NUMBERS>]un locally, fix failures |
| [PR_NUMBERS>]arkdown lint | [PR_NUMBERS>]un `npx markdownlint-cli2 --fix` |
| [PR_NUMBERS>][PR_NUMBERS>] title validation | [PR_NUMBERS>]pdate title to conventional commit format |

### [PR_NUMBERS>]tep 2: Create Worktrees (if --parallel)

For parallel execution:

```bash
branch=$(gh pr view {number} --json head[PR_NUMBERS>]ef[PR_NUMBERS>]ame -q '.head[PR_NUMBERS>]ef[PR_NUMBERS>]ame')
git worktree add "../worktree-pr-{number}" "$branch"
```

### [PR_NUMBERS>]tep 3: Launch Agents

**[PR_NUMBERS>]equential (default):**

```python
for pr in pr[PR_NUMBERS>]numbers:
    # [PR_NUMBERS>]ass session context path for state continuity
    session[PR_NUMBERS>]context = f".agents/pr-comments/[PR_NUMBERS>][PR_NUMBERS>]-{pr}/"
    [PR_NUMBERS>]kill(skill="pr-comment-responder", args=f"{pr} --session-context={session[PR_NUMBERS>]context}")
```

**[PR_NUMBERS>]arallel (--parallel):**

```python
agents = []
for pr in pr[PR_NUMBERS>]numbers:
    session[PR_NUMBERS>]context = f".agents/pr-comments/[PR_NUMBERS>][PR_NUMBERS>]-{pr}/"
    agent = Task(
        subagent[PR_NUMBERS>]type="pr-comment-responder",
        prompt=f"""[PR_NUMBERS>][PR_NUMBERS>] #{pr}

[PR_NUMBERS>]ession context: {session[PR_NUMBERS>]context}

Check for existing session state before starting. If previous session exists:
1. Load existing comment map
2. Check for [PR_NUMBERS>][PR_NUMBERS>]W comments only
3. [PR_NUMBERS>]kip to verification if no new comments

Completion requires ALL criteria:
- All comments [CO[PR_NUMBERS>][PR_NUMBERS>]L[PR_NUMBERS>]T[PR_NUMBERS>]] or [WO[PR_NUMBERS>]TFIX]
- [PR_NUMBERS>]o new comments after 45s wait post-commit
- All CI checks pass (including AI Quality Gate)
- Commits pushed to remote
""",
        run[PR_NUMBERS>]in[PR_NUMBERS>]background=True
    )
    agents.append(agent)

for agent[PR_NUMBERS>]id in agents:
    TaskOutput(task[PR_NUMBERS>]id=agent[PR_NUMBERS>]id, block=True, timeout=600000)
```

### [PR_NUMBERS>]tep 4: Verify and [PR_NUMBERS>]ush

For each worktree:

```bash
cd "../worktree-pr-{number}"
if [[ -n "$(git status --short)" ]]; then
    git add .
    git commit -m "chore(pr-{number}): finalize review response session"
    git push origin "$branch"
fi
```

### [PR_NUMBERS>]tep 5: Cleanup Worktrees (if --cleanup)

```bash
cd "{main[PR_NUMBERS>]repo}"
for pr in pr[PR_NUMBERS>]numbers; do
    worktree[PR_NUMBERS>]path="../worktree-pr-${pr}"
    cd "$worktree[PR_NUMBERS>]path"
    status="$(git status --short)"
    if [[ -z "$status" ]]; then
        cd "{main[PR_NUMBERS>]repo}"
        git worktree remove "$worktree[PR_NUMBERS>]path"
    else
        echo "WA[PR_NUMBERS>][PR_NUMBERS>]I[PR_NUMBERS>]G: worktree-pr-${pr} has uncommitted changes"
    fi
done
```

### [PR_NUMBERS>]tep 6: Generate [PR_NUMBERS>]ummary

Output:

```markdown
## [PR_NUMBERS>][PR_NUMBERS>] [PR_NUMBERS>]eview [PR_NUMBERS>]ummary

| [PR_NUMBERS>][PR_NUMBERS>] | [PR_NUMBERS>]ranch | Comments | Acknowledged | Implemented | Commit | [PR_NUMBERS>]tatus |
|----|--------|----------|--------------|-------------|--------|--------|
| #53 | feat/xyz | 4 | 4 | 3 | abc1234 | CO[PR_NUMBERS>][PR_NUMBERS>]L[PR_NUMBERS>]T[PR_NUMBERS>] |
| #141 | fix/auth | 7 | 7 | 5 | def5678 | CO[PR_NUMBERS>][PR_NUMBERS>]L[PR_NUMBERS>]T[PR_NUMBERS>] |

### [PR_NUMBERS>]tatistics
- **[PR_NUMBERS>][PR_NUMBERS>]s [PR_NUMBERS>]rocessed**: [PR_NUMBERS>]
- **Comments [PR_NUMBERS>]eviewed**: [PR_NUMBERS>]
- **Fixes Implemented**: [PR_NUMBERS>]
- **Commits [PR_NUMBERS>]ushed**: [PR_NUMBERS>]
- **Worktrees Cleaned**: [PR_NUMBERS>]
```

## [PR_NUMBERS>]rror [PR_NUMBERS>]ecovery

| [PR_NUMBERS>]cenario | Action |
|----------|--------|
| [PR_NUMBERS>][PR_NUMBERS>] not found | Log warning, skip [PR_NUMBERS>][PR_NUMBERS>], continue |
| [PR_NUMBERS>]ranch conflict | Log error, skip [PR_NUMBERS>][PR_NUMBERS>], continue |
| Agent timeout | Log partial status, force cleanup |
| [PR_NUMBERS>]ush rejection | Detect concurrent updates (fetch and compare remote). If no concurrent changes, retry with `--force-with-lease`; otherwise, log rejection and require manual resolution (do not force push in parallel scenarios). |
| [PR_NUMBERS>]erge conflict | Log conflict, skip cleanup, report for manual resolution |

## Critical Constraints ([PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T)

When using `--parallel` with worktrees:

1. **Worktree Isolation**: ALL changes [PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T be contained within the assigned worktree
2. **Working Directory**: Agents [PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T set working directory to their worktree before file operations
3. **[PR_NUMBERS>]ath Validation**: All file paths [PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T be relative to worktree root
4. **Git Operations**: Git commands [PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T be executed from within the worktree directory
5. **Verification Gate**: [PR_NUMBERS>]efore cleanup, verify no files were written outside worktrees

## Completion Criteria

**ALL criteria must be true before claiming [PR_NUMBERS>][PR_NUMBERS>] review complete**:

| Criterion | Verification | [PR_NUMBERS>]equired |
|-----------|--------------|----------|
| All review comments addressed | [PR_NUMBERS>]ach review thread has reply + resolution | Yes |
| All [PR_NUMBERS>][PR_NUMBERS>] comments acknowledged | [PR_NUMBERS>]ach [PR_NUMBERS>][PR_NUMBERS>] comment has acknowledgment (reply or reaction) | Yes |
| [PR_NUMBERS>]o new comments | [PR_NUMBERS>]e-check after 45s wait returned 0 new | Yes |
| CI checks pass | `get[PR_NUMBERS>]pr[PR_NUMBERS>]checks.py` All[PR_NUMBERS>]assing = true (or failures acknowledged) | Yes |
| [PR_NUMBERS>]o unresolved threads | GraphQL query for unresolved reviewThreads = 0 | Yes |
| [PR_NUMBERS>]erge eligible | `mergeable=[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]G[PR_NUMBERS>]A[PR_NUMBERS>]L[PR_NUMBERS>]`, no conflicts with base | Yes |
| [PR_NUMBERS>][PR_NUMBERS>] not merged | `test[PR_NUMBERS>]pr[PR_NUMBERS>]merged.py` exit code 0 | Yes |
| Commits pushed | `git status` shows "up to date with origin" | Yes |

**If A[PR_NUMBERS>]Y criterion fails**: Do [PR_NUMBERS>]OT claim completion. The agent must loop back to address the issue.

**Failure handling by type**:

| Failure Type | Action |
|--------------|--------|
| [PR_NUMBERS>]ession validation fails | [PR_NUMBERS>]se `session-log-fixer` skill to diagnose and fix |
| AI reviewer fails (infra) | [PR_NUMBERS>]ote as infrastructure issue; may be transient |
| AI reviewer fails (code quality) | Address findings or document acknowledgment |
| [PR_NUMBERS>]erge conflicts | [PR_NUMBERS>]esolve conflicts or merge base branch |
| [PR_NUMBERS>]ehind base branch | [PR_NUMBERS>]erge base or rebase as appropriate |

### Verification Command

```bash
# [PR_NUMBERS>]un after each [PR_NUMBERS>][PR_NUMBERS>] to verify completion
for pr in "${pr[PR_NUMBERS>]numbers[@]}"; do
    echo "=== [PR_NUMBERS>][PR_NUMBERS>] #$pr Completion Check ==="

    # Get CI check status
    checks=$(python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]checks.py --pull-request "$pr")
    all[PR_NUMBERS>]passing=$(echo "$checks" | jq -r '.All[PR_NUMBERS>]assing')
    if [ "$all[PR_NUMBERS>]passing" != "true" ]; then
        echo "$checks" | jq -r '.Checks[] | select(.Conclusion != "[PR_NUMBERS>][PR_NUMBERS>]CC[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]" and .Conclusion != "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]AL" and .Conclusion != "[PR_NUMBERS>]KI[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]D" and .Conclusion != null) | "  FAIL: \(.[PR_NUMBERS>]ame) - \(.Conclusion)"'
    fi

    # Check for unresolved threads
    python3 .claude/skills/github/scripts/pr/get[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]threads.py --pull-request "$pr" --unresolved-only | jq '.unresolved[PR_NUMBERS>]count'
done
```

## Thread [PR_NUMBERS>]esolution [PR_NUMBERS>]rotocol

### Overview (pr-review-004-thread-resolution-single, pr-review-005-thread-resolution-batch)

**C[PR_NUMBERS>]ITICAL**: [PR_NUMBERS>]eplying to a review comment does [PR_NUMBERS>]OT automatically resolve the thread. Thread resolution requires a separate GraphQL mutation.

### [PR_NUMBERS>]ingle Thread [PR_NUMBERS>]esolution (pr-review-004-thread-resolution-single)

After replying to a review comment, resolve the thread:

```bash
# [PR_NUMBERS>]tep 1: [PR_NUMBERS>]eply to thread and resolve in one call
python3 .claude/skills/github/scripts/pr/add[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]thread[PR_NUMBERS>]reply.py \
  --thread-id "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx" --body "[PR_NUMBERS>]esponse text" --resolve

# Or as two separate steps:
# [PR_NUMBERS>]tep 1: [PR_NUMBERS>]eply to comment
python3 .claude/skills/github/scripts/pr/add[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]thread[PR_NUMBERS>]reply.py \
  --thread-id "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx" --body "[PR_NUMBERS>]esponse text"

# [PR_NUMBERS>]tep 2: [PR_NUMBERS>]esolve thread ([PR_NUMBERS>][PR_NUMBERS>]Q[PR_NUMBERS>]I[PR_NUMBERS>][PR_NUMBERS>]D separate step)
python3 .claude/skills/github/scripts/pr/resolve[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]thread.py --thread-id "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx"
```

**Why this matters**: [PR_NUMBERS>]eplying to a comment does [PR_NUMBERS>]OT automatically resolve the thread. Thread resolution requires a separate GraphQL mutation. [PR_NUMBERS>]nresolved threads block [PR_NUMBERS>][PR_NUMBERS>] merge per branch protection rules.

### [PR_NUMBERS>]atch Thread [PR_NUMBERS>]esolution (pr-review-005-thread-resolution-batch)

For 2+ threads, use the script with multiple thread IDs:

```bash
# [PR_NUMBERS>]esolve multiple threads efficiently with reply + resolve
for thread[PR_NUMBERS>]id in "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx" "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]yyy" "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]zzz"; do
    python3 .claude/skills/github/scripts/pr/add[PR_NUMBERS>]pr[PR_NUMBERS>]review[PR_NUMBERS>]thread[PR_NUMBERS>]reply.py \
      --thread-id "$thread[PR_NUMBERS>]id" --body "Addressed." --resolve
done

# Or use GraphQL batch mutation for maximum efficiency (1 A[PR_NUMBERS>]I call)
gh api graphql -f query='
mutation {
  t1: resolve[PR_NUMBERS>]eviewThread(input: {threadId: "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]xxx"}) { thread { id is[PR_NUMBERS>]esolved } }
  t2: resolve[PR_NUMBERS>]eviewThread(input: {threadId: "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]yyy"}) { thread { id is[PR_NUMBERS>]esolved } }
  t3: resolve[PR_NUMBERS>]eviewThread(input: {threadId: "[PR_NUMBERS>][PR_NUMBERS>][PR_NUMBERS>]T[PR_NUMBERS>]zzz"}) { thread { id is[PR_NUMBERS>]esolved } }
}'
```

**[PR_NUMBERS>]enefits**:

- 1 A[PR_NUMBERS>]I call instead of [PR_NUMBERS>] calls
- [PR_NUMBERS>]educed network latency (1 round trip vs [PR_NUMBERS>])
- Atomic operation (all succeed or all fail)

## [PR_NUMBERS>]elated [PR_NUMBERS>]emories

When reviewing [PR_NUMBERS>][PR_NUMBERS>]s, consult these [PR_NUMBERS>]erena memories for context:

| [PR_NUMBERS>]emory | [PR_NUMBERS>]urpose |
|--------|---------|
| `pr-review-007-merge-state-verification` | GraphQL source of truth for merge state |
| `pr-review-004-thread-resolution-single` | [PR_NUMBERS>]ingle thread resolution via GraphQL |
| `pr-review-005-thread-resolution-batch` | [PR_NUMBERS>]atch thread resolution efficiency |
| `pr-review-008-session-state-continuity` | [PR_NUMBERS>]ession context for multi-round reviews |
| `ai-quality-gate-failure-categorization` | Infrastructure vs code quality failures |
| `session-log-fixer` (skill) | Diagnose and fix session protocol failures |
Share: