Review Multiple PRs (Parallel)

Use this skill when the user provides 2 or more PR numbers or URLs to review. Unlike /code-review (which processes PRs sequentially), this skill fans out to parallel agents — one per PR — then merges their findings into a unified summary.


Step 0 — Verify gh auth and repo access

  1. Run gh auth status. If unauthenticated, stop and tell the user.
  2. Resolve the repo identity: if PR URLs were provided, extract owner/repo from them. Otherwise run gh repo view --json nameWithOwner -q .nameWithOwner to get the repo from the current directory context. Store this as {owner}/{repo} — it is required for API calls in Step 5.
  3. Run gh repo view {owner}/{repo} to confirm read access. If it fails, stop and tell the user.

Step 1 — Identify and classify the PRs

PR Discovery (when no PRs are specified)

Use gh and the GitHub search API to find open PRs by the user’s teammates that are awaiting review. Run these steps:

  1. Resolve the org: extract the org from the {owner}/{repo} resolved in Step 0 (e.g. ProjectAussie).

  2. Find teammate usernames: ask the user for a list of names or GitHub handles to search for. If they provide display names (e.g. “Claire”, “Tom McT”), resolve them to GitHub logins by searching org members:
    gh api 'orgs/{org}/members' --paginate --jq '.[] | select(.login | test("{name}"; "i")) | .login'
    
  3. Find open PRs by those authors:
    gh search prs --state open --author {login} --json number,title,url,author,repository --limit 20
    

    Run one search per author. Collect all results.

  4. Filter to actionable PRs only — for each PR, fetch its review status and check whether the current user has already reviewed:

    a. Get the current user’s login:

       gh api user --jq .login
    

    b. Get PR metadata:

       gh pr view <number> -R <owner/repo> --json reviewDecision,isDraft,reviewRequests
    

    c. Get the current user’s most recent review on this PR (if any) and the latest commit date:

       gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.user.login == "{current_user}")] | sort_by(.submitted_at) | last | {state: .state, submitted_at: .submitted_at}'
    
       gh api repos/{owner}/{repo}/pulls/{number}/commits --jq 'last | .commit.committer.date'
    

    d. Keep only PRs where all of the following are true:

    e. Skip PRs where the current user is already waiting on the author:

  5. Bucket by requester type (reuse the current user’s login from step 4a — do not fetch it again):**
  6. Present the list to the user grouped by author and bucket, with URLs. If any PRs were skipped because the current user is waiting on the author, list them separately:

    Skipped (waiting on author after your CHANGES_REQUESTED):

    • {owner}/{repo}#{number} — {title} — last reviewed {date}, no new commits since

    Then ask: “Should I review all of these, or select a subset? I can also include the skipped PRs if you want to re-review them.”

  7. Once the user confirms the set, continue with the normal Step 1 classification flow using those PRs.
Relationship Definition Review strategy
Stacked Each PR targets the previous PR’s branch Review in merge order; pass raw diff context forward
Parallel Multiple PRs for the same feature, split by concern Review independently; flag integration risks
Batch / Unrelated Unrelated changes reviewed together (e.g. release batch) Review fully independently

Step 2 — Fan out: one agent per PR

Spin up one Agent per PR using subagent_type: general-purpose. Name each agent after a unique American outlaw from the 1800s–1900s (e.g. Jesse James, Belle Starr, Black Bart, Dutch Schultz, Pretty Boy Floyd, Billy the Kid, Bonnie Parker). Names must be unique per session — do not reuse a name even if reviewing many PRs.

Each agent receives a self-contained prompt with:

  1. The PR number, repo ({owner}/{repo}), and is_draft flag.
  2. Instructions to:
  3. For stacked PRs only: the raw output of gh pr diff <prior-number> from the prior agent — passed verbatim, not summarized — so the agent understands what the prior layer changed and can attribute findings to the correct PR.
  4. Draft PR behavior: if is_draft: true, the agent must still review and produce findings, but must set verdict: "COMMENT" unconditionally. It should post inline comments on Github (authors expect feedback on drafts) but the summary must clearly label the PR as a draft.
  5. The review dimensions to evaluate (see Step 3 below)
  6. The finding severity scale (see Step 4 below)
  7. Instructions to return findings as structured JSON (see Output Contract below)

Run all agents in parallel (single message, multiple tool calls) unless the PRs are stacked — in that case, run them sequentially in merge order so each agent gets the prior layer’s raw diff as context.

Output Contract

Each agent must return a JSON object with exactly these fields:

{
  "pr": 123,
  "title": "...",
  "verdict": "APPROVE",
  "is_draft": false,
  "ci_status": "passing",
  "ci_failures_introduced_by_pr": false,
  "stacked_context_diff": "<raw output of gh pr diff for this PR — included only for stacked PRs, to pass forward to the next agent>",
  "findings": [
    {
      "severity": "BLOCKER",
      "file": "path/to/file.ts",
      "line": 42,
      "body": "..."
    }
  ],
  "prior_discussions": [
    {
      "author": "<github username of the person who raised the concern>",
      "summary": "brief description of the prior comment or concern",
      "status": "accepted | unresolved | addressed_in_code",
      "original_severity": "BLOCKER | HIGH | MEDIUM | LOW | QUESTION",
      "file": "path/to/file.ts (optional — only if the concern was about a specific file)",
      "line": 42,
      "reasoning": "why this status was assigned — e.g. 'reviewer replied OK to defer' or 'no response from reviewer after author acknowledged'"
    }
  ],
  "summary": "2–4 sentence summary of what the PR does and overall quality"
}

Valid verdict values: "APPROVE", "REQUEST_CHANGES", "COMMENT".

Line number constraint: every finding with a line value must reference a line that actually appears in the diff output from gh pr diff. Do not invent or approximate line numbers — a line number not in the diff will cause the GitHub API to reject the comment with a 422 error.

Step 3 — Review dimensions (per PR)

Each agent evaluates:

Correctness

Security

Design & Simplicity

Readability & Maintainability

Test Coverage

Prior Discussion & Deferred Decisions

Agents must complete this section before forming a verdict. Omitting it is a review failure — populate the prior_discussions array in the Output Contract even if empty.

Data sources

Use all three data sources together — each one shows different things:

Source What it shows Command
PR conversation comments Top-level discussion gh pr view <number> --comments
Formal review state APPROVED, CHANGES_REQUESTED, DISMISSED per reviewer gh api repos/{owner}/{repo}/pulls/{number}/reviews
Inline review thread comments Line-level reviewer concerns gh api repos/{owner}/{repo}/pulls/{number}/comments

Identifying deferred or prior concerns

Scan reviewer comment threads (not PR description, not commit messages) for phrases that indicate deferral: “we’ll fix this later”, “out of scope for this PR”, “follow-up ticket”, “known issue”, “accepted risk”, or similar.

Do not treat the following as deferral signals:

Assigning severity to prior concerns

Prior reviewer comments do not use this skill’s severity taxonomy. When a prior concern has no explicit severity, assign one based on impact:

Document your severity assignment and reasoning in the reasoning field of the prior_discussions entry.

Status classification rules

Status Criteria
accepted The reviewer who raised the concern (not the author) explicitly accepted the deferral — e.g., replied “OK to defer”, “fine for now”, submitted a new APPROVED review after discussion. Author acknowledgment alone is not sufficient. If the reviewer went silent after the author acknowledged, classify as unresolved — reviewer silence does not equal acceptance.
addressed_in_code The concern was addressed by a code change. You must verify this — cross-reference the PR diff to confirm the fix is actually present. A comment saying “fixed” or “done” without a corresponding code change means the status is unresolved, not addressed_in_code.
unresolved Anything else: no reply, author disputed it without reviewer resolution, reviewer went silent, or the formal review state is still CHANGES_REQUESTED and not DISMISSED.

Formal review state handling

Verdict interaction

What to surface

Populate the prior_discussions array in the Output Contract for every prior concern found. Include a dedicated “Prior Discussions” subsection in the review output listing each item, its status, original severity, and your reasoning.

Performance (only if relevant)

Step 4 — Finding severity scale

Level Meaning
BLOCKER Must be fixed before merge. Correctness bug, security vuln, broken contract, CI failure introduced by this PR.
HIGH Serious design or reliability issue. Should fix; discuss if deferring.
MEDIUM Real improvement, not blocking. Author should address or explicitly accept risk.
LOW / NIT Style, naming, minor cleanup. Don’t block merge over these.
QUESTION Unclear intent — ask before judging.

Do not manufacture findings to look thorough. If the code is good, say so.

Step 5 — Draft review file and get human approval

Before posting anything to GitHub, write a draft markdown file and present it to the user for review and editing.

Write the draft file

Write all findings to review-draft-{timestamp}.md (e.g. review-draft-2026-04-14T15-44.md) in the current working directory. The file has two sections per PR: a Changes Summary and the Proposed Comments.

File format:

# Review Draft — {date}

---

## {owner}/{repo}#{number} — {title}

**Author:** {login} | **CI:** passing / failing / pending

**Review Event:** `APPROVE` / `REQUEST_CHANGES` / `COMMENT` ← _edit this to control the formal GitHub review event submitted for this PR_

### Changes Summary

<3–6 sentence plain-English description of what the PR actually does  not the PR description copy-pasted, but your own read of the diff. What files changed, what behavior changed, what was added or removed.>

### Proposed Comments

#### Prior Discussions

| Author | Summary | Status | Orig. Severity | Reasoning |
|--------|---------|--------|---------------|-----------|
| @reviewer-login | brief description of concern | accepted / unresolved / addressed_in_code | BLOCKER / HIGH / MEDIUM / LOW | why this status was assigned |

#### Inline Comments

| File | Line | Severity | Comment |
|------|------|----------|---------|
| `path/to/file.ts` | 42 | BLOCKER | Farty Bobo says: ... |

---

## Cross-PR Summary (for human reviewer only — NOT posted to GitHub)

> **Relationship:** Stacked / Parallel / Batch
> **Overall:** ALL APPROVE / MIXED / ALL REQUEST_CHANGES
>
> | PR | Title | Review Event | Blockers | Highs |
> |----|-------|-------------|----------|-------|
> | #123 | ... | APPROVE | 0 | 1 |
> | #124 | ... | REQUEST_CHANGES | 1 | 0 |
>
> **Integration Concerns:** ...

Include every PR in the file, in order. Leave the cross-PR summary at the bottom.

Present and wait for approval

Tell the user:

“Draft written to {filename}. Open it, make any edits you want — remove findings, soften wording, add context. The Review Event field on each PR controls the formal GitHub review action (APPROVE / REQUEST_CHANGES / COMMENT) — change it if you disagree with my recommendation. Tell me to post when ready, or say ‘post as-is’.”

Do not proceed to Step 6 until the user explicitly says to post. This gate is not optional — the whole point is to let the human adjust before anything hits GitHub.

After approval

Re-read the (possibly edited) draft file before posting — use its content as the source of truth for what gets posted, not the original agent outputs. Then delete the draft file after posting completes.

Step 6 — Post inline comments and submit review

After human approval, re-read the draft file. For each PR:

  1. Parse the Review Event field from the draft file header. Valid values: APPROVE, REQUEST_CHANGES, COMMENT. The human may have changed this from the agent’s original recommendation — always use the value in the file, not the agent’s original verdict.

  2. Post inline comments with the review event using gh api. Use side: "RIGHT" for all inline comments. Only post comments for lines confirmed to exist in the diff. Use the content from the approved draft file — not the raw agent output.

gh api repos/{owner}/{repo}/pulls/{number}/reviews \
  --method POST \
  --field body="" \
  --field event="{REVIEW_EVENT}" \
  --field "comments[][path]=path/to/file.ts" \
  --field "comments[][line]=42" \
  --field "comments[][side]=RIGHT" \
  --field "comments[][body]=Farty Bobo says: <finding>"

Where {REVIEW_EVENT} is the value read from the draft file’s Review Event field for that PR (APPROVE, REQUEST_CHANGES, or COMMENT).

  1. If a PR has no inline comments but the review event is APPROVE or REQUEST_CHANGES, submit the review without comments:
gh api repos/{owner}/{repo}/pulls/{number}/reviews \
  --method POST \
  --field body="Reviewed by Farty Bobo" \
  --field event="{REVIEW_EVENT}"

Post reviews for all PRs.

Step 7 — Review event rules

The agent populates the Review Event field in the draft file as its initial recommendation. The human can override it before posting. These are the rules for the agent’s initial recommendation:

Draft PR precedence: if is_draft: true, the review event is always COMMENT regardless of unresolved prior concerns. Prior discussions are still analyzed and surfaced in the output — the draft status overrides the event, not the analysis.

Human override: Whatever value the human leaves in the Review Event field when they approve the draft is what gets submitted to the GitHub API. The agent’s recommendation is just a starting point.

Step 8 — Notify the user

After posting all reviews: