Skip to content

Instantly share code, notes, and snippets.

@pat-eason
Created August 12, 2026 17:05
Show Gist options
  • Select an option

  • Save pat-eason/1877815e02e84d3a953002665b7e690f to your computer and use it in GitHub Desktop.

Select an option

Save pat-eason/1877815e02e84d3a953002665b7e690f to your computer and use it in GitHub Desktop.
pr-code-review — multi-model adversarial + consensus PR review skill for opencode/Claude Code

pr-code-review

A multi-model adversarial + consensus pull request review skill for opencode and Claude Code.

Four different model families independently review your PR, then an adjudicator deduplicates, resolves disagreements, validates fixes against the surrounding code, and ranks every finding on a 0–5 severity scale. You get one action plan and one question: which fixes to apply.

Why multi-model adversarial review

Models from the same family share blind spots. A single reviewer (or three same-family variants) will miss the same class of bugs. This skill deliberately dispatches diverse model families — DeepSeek, Kimi, Qwen, and GPT — so their disagreements surface real issues that any one model would miss. The adjudicator then merges their findings into a single, confidence-scored plan.

What it does

  1. Resolves the PR from a URL, number, or the current branch/worktree (auto-detection, no arg needed).
  2. Fetches the diff via gh and ingests existing reviewer comments — CodeRabbit, architecture-reviewer, Gemini, and human reviewers. Unresolved findings become prior context so adversaries validate/refute them rather than rediscovering (or missing) them.
  3. Gathers context — pulls the related Linear story (if a ticket ID is on the PR/branch) and any linked Notion/plan docs, then classifies the PR's surface area (web / mobile / distributed) to apply the right lens.
  4. Dispatches 4 adversary models in parallel, each with a unique review lens, all against the 7 Engineering Concerns rubric.
  5. Adjudicates consensus — dedupes by agreement, resolves conflicts by reading the actual code, validates against the Linear story/plan, and runs a controlled-entropy check (reads ±30 lines around each proposed fix to ensure it won't ripple).
  6. Presents a severity-ranked action plan (0 = critical → 5 = optional), each finding with: concern, location, why, proposed fix, blast radius, confidence, and which adversaries/reviewers flagged it.
  7. Asks one question — fix critical+high, fix through medium, adjust the plan, or report only. Fixes are applied in parallel, then committed and pushed automatically.

The 7 Engineering Concerns

Every adversary reviews against these (adapted from Luxury Presence's engineering framework):

Concern One-line test
Correctness Is the spec itself right? Does the solution match what the code actually does?
Maintainability & extensibility Can the next change be made safely and cheaply?
Security Can only the right principals do or see this? (IDOR, vacuous auth, unauth endpoints)
Performance Is the access pattern right for the read/write shape, and does it survive N× load?
Reliability & failure modes What happens when a dependency is slow, down, or half-done?
Testability & verifiability Can this code prove itself correct without a human reading every line?
Operability Can we run this as a team and with agents? Do we know if something is actually failing?

Requirements

  • opencode or Claude Code with skill support
  • gh CLI, authenticated (gh auth login)
  • Python 3 (for the adversary helper script — stdlib only, no pip packages)
  • A litellm-compatible gateway with the adversary models deployed (see below)
  • Optional: Anthropic provider configured in opencode (for Opus 5 adjudication — the skill works without it)

Installation

1. Install the skill

Clone or download this gist, then place the files in your skills directory:

# For opencode (global skills)
mkdir -p ~/.claude/skills/pr-code-review
# Place SKILL.md and adversary.py in that directory

# Verify
ls ~/.claude/skills/pr-code-review/
# → SKILL.md  adversary.py

chmod +x ~/.claude/skills/pr-code-review/adversary.py

2. Configure the litellm gateway

The adversary helper (adversary.py) calls models via a litellm-compatible OpenAI gateway. It reads the URL and API key from your opencode config at ~/.config/opencode/opencode.json:

{
  "enabled_providers": ["litellm"],
  "provider": {
    "litellm": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "LiteLLM gateway",
      "options": {
        "baseURL": "https://your-litellm-gateway.example.com/v1",
        "apiKey": "your-api-key"
      },
      "models": {
        "fireworks/models/deepseek-v4-pro": { "name": "fireworks/models/deepseek-v4-pro" },
        "fireworks/kimi-k3": { "name": "fireworks/kimi-k3" },
        "fireworks/qwen3.7-plus": { "name": "fireworks/qwen3.7-plus" },
        "gpt-5.6-sol": { "name": "gpt-5.6-sol" }
      }
    }
  }
}

Non-LP users: The default gateway URL in adversary.py is LP-specific (https://litellm.luxurycoders.com/v1). Edit the LITELLM_URL constant at the top of adversary.py to point at your own gateway, and update the model IDs in SKILL.md section 6b to match models you have deployed. The skill degrades gracefully — if a model is unavailable, it's skipped and the review proceeds with the rest (minimum 2 adversaries required).

3. (Optional) MCP integrations

The skill can pull Linear and Notion context if you have those MCP servers configured. Both are optional — if absent or unauthorized, the review proceeds without them:

  • Linear MCP — retrieves the related story's acceptance criteria for PR-accuracy validation
  • Notion MCP — fetches linked plan/RFC docs

Usage

Invoke the skill either way:

/pr-code-review https://github.com/owner/repo/pull/123

or natural language:

code review on this PR

Blank argument → the skill auto-resolves the PR from your current branch/worktree.

Then it runs end-to-end with one question at the end: which fixes to apply.

Model roster

Diversity of model family is the point.

Role Model Lens
Adversary 1 DeepSeek-v4-pro Logic correctness, edge cases, data flow
Adversary 2 Kimi-k3 Cross-file impact, long-range dependencies
Adversary 3 Qwen3.7-plus Maintainability, extensibility, coupling, boundaries
Adversary 4 GPT-5.6-sol Security + reliability/failure modes
Adjudicator Orchestrator (inline) or Claude Opus 5 (optional) Consensus, dedupe, rank, controlled-entropy check

If any adversary is unavailable, it's skipped (minimum 2 must succeed). The adjudicator runs with file access so it can perform the controlled-entropy check — a stateless API call can't read your surrounding code.

Optional: Claude Opus 5 adjudication

By default, the orchestrator model adjudicates inline. If you have the Anthropic provider configured in opencode, you can optionally dispatch a real anthropic/claude-opus-5 subagent for stronger adjudication. The skill auto-detects whether Anthropic is configured and degrades gracefully if not — no prompts, no warnings.

To enable, add custom agents to ~/.config/opencode/opencode.json (see SKILL.md section 11 for the full config). The four adversary agents work with litellm alone; the pr-review-opus adjudicator requires Anthropic.

Customization

  • Models / lenses — edit the table in SKILL.md section 6b and the adversary.py calls to use different models or lenses
  • Gateway URL — edit LITELLM_URL in adversary.py
  • 7 Concerns rubric — edit SKILL.md section 5 to match your team's engineering principles
  • Surface-area detection — edit SKILL.md section 3c to match your repo's path conventions
  • Severity scale — edit SKILL.md section 7, step 5

Files

File Purpose
SKILL.md The skill definition — opencode/Claude Code reads this to run the review
adversary.py Helper script that calls one litellm model with the review prompt (stdlib only)
README.md This file

How a review flows

PR URL/number/branch
        │
        ▼
  Resolve PR (gh) ──► Fetch diff + reviewer comments
        │
        ▼
  Gather context (Linear, Notion, surface area)
        │
        ▼
  Build review payload (diff + context + 7 Concerns + existing findings)
        │
        ▼
  Dispatch 4 adversaries in parallel ──► each returns JSON findings
        │
        ▼
  Adjudicate: dedupe by agreement, resolve conflicts (read code),
  validate vs Linear/plan, controlled-entropy check (±30 lines)
        │
        ▼
  Ranked action plan (S0–S5) ──► ONE question: which fixes to apply
        │
        ▼
  Apply fixes in parallel → commit → push

License

MIT — adapt freely for your own team and gateway.

#!/usr/bin/env python3
"""Adversarial PR review helper — calls one litellm model with the review prompt.
Usage:
python3 adversary.py <model_id> "<lens instruction>" <prompt_file> [output_file] [--temperature <n>]
Reads the litellm API key from ~/.config/opencode/opencode.json.
Temperature is OMITTED by default (each model uses its own default — some models,
e.g. gpt-5.6-sol, reject non-default temperature values). Pass --temperature to override.
Writes JSON to stdout (or output_file if given):
{"model": ..., "findings": [...]} or {"model": ..., "error": "...", "findings": []}
"""
import json
import os
import sys
import urllib.request
LITELLM_URL = "https://litellm.luxurycoders.com/v1/chat/completions"
OUTPUT_FORMAT = """Return ONLY a JSON array of findings. Each finding:
{
"concern": "correctness|maintainability|security|performance|reliability|testability|operability",
"location": "file:line or concept description",
"severity": 0-5,
"what": "one-line description",
"why": "why it's a problem, with surrounding-code reasoning",
"fix": "what it should be changed to",
"blast_radius": "what else would be affected by the fix",
"confidence": "low|med|high"
}
Severity scale: 0 = critical (security hole, data loss, prod-breaking, spec violation),
1 = high (likely bug, missing critical test), 2 = medium (maintainability/coupling, perf under load),
3 = low (improvement, minor edge case), 4 = nit (style, naming), 5 = optional (suggestion, non-blocking).
If you find no issues, return []."""
def main():
if len(sys.argv) < 4:
print(json.dumps({"error": "usage: adversary.py <model> <lens> <prompt_file> [output_file]"}))
sys.exit(1)
model = sys.argv[1]
lens = sys.argv[2]
prompt_file = sys.argv[3]
output_file = sys.argv[4] if len(sys.argv) > 4 and not sys.argv[4].startswith("--") else None
temperature = None
args = sys.argv[4:]
for i, a in enumerate(args):
if a == "--temperature" and i + 1 < len(args):
try:
temperature = float(args[i + 1])
except ValueError:
pass
config_path = os.path.expanduser("~/.config/opencode/opencode.json")
try:
with open(config_path) as f:
config = json.load(f)
api_key = config["provider"]["litellm"]["options"]["apiKey"]
except Exception as e:
print(json.dumps({"model": model, "error": f"config read: {e}", "findings": []}))
sys.exit(0)
with open(prompt_file) as f:
prompt = f.read()
full_prompt = f"""{prompt}
YOUR REVIEW LENS (in addition to the 7 concerns): {lens}
{OUTPUT_FORMAT}"""
payload = {
"model": model,
"messages": [{"role": "user", "content": full_prompt}],
"max_tokens": 8000,
}
if temperature is not None:
payload["temperature"] = temperature
body = json.dumps(payload).encode()
req = urllib.request.Request(
LITELLM_URL,
data=body,
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"x-litellm-tags": "harness=opencode-skill=pr-code-review",
},
)
try:
with urllib.request.urlopen(req, timeout=180) as resp:
data = json.loads(resp.read())
content = data["choices"][0]["message"]["content"]
start = content.find("[")
end = content.rfind("]") + 1
findings = json.loads(content[start:end]) if start >= 0 and end > start else []
result = {"model": model, "findings": findings}
except Exception as e:
result = {"model": model, "error": str(e), "findings": []}
out = json.dumps(result)
if output_file:
with open(output_file, "w") as f:
f.write(out)
print(out)
if __name__ == "__main__":
main()
name pr-code-review
description Comprehensive multi-model adversarial + consensus PR review. Invoke with /pr-code-review <PR URL> or when the user asks for "code review on this PR", "review this PR", "adversarial PR review", "multi-model PR review", "consensus review on this PR", or similar. Dispatches 4 adversary models (DeepSeek-v4-pro, Kimi-k3, Qwen3.7-plus, GPT-5.6-sol) for independent findings, then consensus adjudication with full file access for controlled-entropy validation. Claude Opus 5 is used for adjudication when Anthropic is configured; otherwise the orchestrator model adjudicates inline. Reviews against the 7 Engineering Concerns, retrieves Linear + Notion context, ranks findings 0-5 severity (0 = most severe), and produces an action plan.
argument-hint <GitHub PR URL or PR number>

PR Code Review — Multi-Model Adversarial + Consensus

Comprehensive PR review: 4 adversary models independently surface findings, then consensus adjudication with full file access for controlled-entropy validation. Reviews against LP's 7 Engineering Concerns, is surface-area-aware, context-informed (Linear + Notion), and produces a severity-ranked action plan.

Opus 5 is optional. Adjudication runs inline on the orchestrator model by default. When Anthropic is configured in opencode (see step 2b), the skill can dispatch a real anthropic/claude-opus-5 subagent for adjudication instead. If Anthropic is not configured, the skill degrades gracefully — no errors, no blocking prompts.


Model roster

Diversity of model family is the point — same-family models share blind spots.

Role Model ID (litellm) Lens
Adversary 1 fireworks/models/deepseek-v4-pro Logic correctness, edge cases, data flow
Adversary 2 fireworks/kimi-k3 Cross-file impact, long-range dependencies, large-context
Adversary 3 fireworks/qwen3.7-plus Maintainability, extensibility, coupling, boundaries
Adversary 4 gpt-5.6-sol Security (IDOR, vacuous auth, unauth endpoints) + reliability/failure modes
Adjudicator Orchestrator inline (default) or anthropic/claude-opus-5 subagent (if Anthropic configured) Consensus, dedupe, resolve disagreements, rank, controlled-entropy check

The adjudicator runs with file access so it can perform the controlled-entropy check (reading ±30 lines around each proposed fix) — a stateless API call can't do this. By default the orchestrator model does adjudication inline. When Anthropic is configured, step 2b may dispatch a real Opus 5 subagent for stronger adjudication.

If an adversary model is unavailable via litellm, its call errors and is skipped — the review proceeds with the remaining adversaries (minimum 2 for adversarial value; if fewer than 2 succeed, tell the user and stop).


1. Parse input

Argument Action
GitHub PR URL Parse owner/repo/number
Bare number Current repo: gh pr view <num> --json ...

Blank — resolve from current branch/worktree (try in order, stop at first hit):

  1. gh pr view --json number,headRefName,baseRefName,url,title -q . — looks up the PR for the current branch in the current repo's default remote.
  2. If that fails (no PR for the branch, or worktree/remote mismatch), determine the repo from the worktree's remote: git rev-parse --show-toplevelgit -C <root> remote get-url origin → parse owner/repo. Then gh pr list --repo OWNER/REPO --head <current-branch> --state open --json number,title,url -q '.[0]'.
  3. If the current branch name looks like a worktree-generated name (e.g. feature/x@2, contains a worktree suffix), strip the suffix and retry with the base branch name.
  4. If still nothing, check git branch --show-current — if on a default branch (main/master/develop), tell the user they're on a default branch and ask them to check out a PR branch or pass a PR URL/number.
  5. Only after all of the above fail: stop and ask the user for a PR URL or number.

Report which method resolved the PR so the user can sanity-check.


2. Preflight

  1. gh pr view <NUM> --repo OWNER/REPO --json number,title,headRefName,baseRefName,body,url,labels,files,additions,deletions,comments,reviewThreads,reviews
  2. gh pr diff <NUM> --repo OWNER/REPO — capture full diff
  3. If diff exceeds ~120K chars, truncate to changed hunks + file headers for the API payload (keep the full diff for local controlled-entropy checks in step 7). Note the truncation.

2a. Ingest existing reviewer comments

From the JSON in step 1, extract all reviewer feedback already on the PR. Group by source:

  • CodeRabbitreviewThreads + comments where author login is coderabbitai (or body contains the CodeRabbit signature). Capture each as {file, line, severity-if-tagged, text}.
  • architecture-reviewer — comments/reviews from github-actions[bot] / the architecture-reviewer workflow, or comments containing architecture-reviewer / architecture-bypass. Capture {file, line, check, text, not-code-fixable?}.
  • Other bot reviewers (Gemini, Claude, etc.) — capture similarly.
  • Human reviewers — capture all unresolved review threads and review bodies.

For every captured item, tag its state: unresolved (no reply / thread open) vs addressed (has a reply or the thread is resolved). Drop addressed items — they're done.

The unresolved set is prior context for the adversaries and the adjudicator, not the final word:

  • Each adversary's prompt includes the unresolved reviewer findings so it can validate, refute, or build on them (an adversary that agrees with a CodeRabbit finding AND finds new evidence → higher confidence; an adversary that refutes it with code evidence → flag as skip-wrong).
  • The adjudicator (step 7) treats unresolved reviewer findings as input findings to dedupe against the adversaries' findings, with source: coderabbit / architecture-reviewer / etc. noted.
  • This catches the case where a bot flagged a real issue that the PR author hasn't addressed yet — the adversarial review confirms it and ranks it, rather than re-discovering it from scratch or missing it.

2b. Detect Anthropic configuration (for optional Opus 5 adjudication)

Check whether the Anthropic provider is configured in opencode, so the skill knows whether real Opus 5 adjudication is available:

  1. Read ~/.config/opencode/opencode.json (and ~/.config/opencode/opencode.jsonc if present).
  2. Check if anthropic is in enabled_providers OR if an anthropic/* model is present in provider.anthropic.models OR if a custom agent pinned to anthropic/claude-opus-5 exists under an agent key.
  3. Store the result as opus5_available: true|false.

If opus5_available: false: adjudication (step 7) runs inline on the orchestrator model. Do NOT prompt the user about Opus 5, do NOT block, do NOT warn — the review is fully functional without it. The only place Opus 5 is mentioned is a single line in the final report's reviewer list: Adjudicator: orchestrator (inline).

If opus5_available: true: the skill MAY dispatch a real Opus 5 subagent for adjudication (step 7) per the upgrade path in section 11. Default to inline adjudication unless the user has explicitly opted in (via the custom-agent config in section 11) — inline is more reliable because it guarantees file access without an extra dispatch round-trip.


3. Gather context

3a. Linear story

  1. Search PR body + branch name for a Linear ticket ID (regex [A-Z]+-\d+, e.g. MMC-123).
  2. If found → retrieve via mcp-hub_linear-get-issue with includeRelations: true, includeCustomerNeeds: true. Extract: title, description, acceptance criteria, labels, project, related issues.
  3. If NOT found → do not ask the user. Silently record linear: none and continue. The review proceeds without Linear context; the final report notes "Linear: none" so the user can see it wasn't attached. (The user can paste a ticket ID in a follow-up turn if they want a re-run with context.)

3b. Notion / plan docs

  1. If PR body or Linear story links to a Notion doc → fetch via mcp-hub_notion-notion-fetch.
  2. If user points to a doc → fetch it.
  3. If Notion MCP returns an auth error → tell the user: Connect Notion: https://mcp.luxurypresence.com/oauth/authorize/1e0148abea2044d7a2367f072c964569 and skip. Do not block the review.

3c. Surface area detection

Classify from changed file paths:

Signal in paths Surface Extra lens
auggie-platform, dashboard, home-search, websites, SSR Full-stack web Read-path optimization, white-label constraints (nothing hardcoded per client)
lpmobileV2, lpmobile, OTA, native modules, deep-link, push Mobile Contract changes hit shipped binaries (last quarter's app sends last quarter's query); OTA vs native
contact-ingestion, MLS ETL, notifications, audit-logging, lambdas, queues, subgraphs, gateways, jobs, pipelines Distributed systems Idempotency (at-least-once), backpressure, partial-failure, data-store access patterns

Multiple surfaces → note all; each applies its lens.


4. Build the review payload

Assemble a single review prompt containing:

  • PR title, body, labels
  • Surface area classification
  • Linear story context (acceptance criteria especially)
  • Notion / plan doc summary (if retrieved)
  • The full diff (or truncated hunks for very large PRs)
  • Unresolved reviewer findings from step 2a (CodeRabbit, architecture-reviewer, Gemini, humans) — framed as: "These issues were already flagged by existing reviewers and are NOT yet addressed. Validate each (confirm/refute with code evidence), and independently surface any NEW issues they missed."
  • The 7 Engineering Concerns rubric (section 5)

Write this prompt to /tmp/pr-review-prompt.txt using the Write tool.


5. The 7 Engineering Concerns rubric

Embed this verbatim in the review prompt (it goes to every adversary):

Review this diff against these 7 concerns. Each has a one-line test.

1. CORRECTNESS — Is the spec itself right? Do we have the real problem, the third party's actual semantics, or a solution that matches what the code actually does?
2. MAINTAINABILITY & EXTENSIBILITY — Can the next change be made safely and cheaply? (Over half of all review energy goes here: structure, coupling, duplication, boundaries. Is this a script or something meant to be extended?)
3. SECURITY — Can only the right principals do or see this? Have we identified the actors? (Watch for: IDOR — caller reaching someone else's object by supplying its ID; vacuous auth checks that can never fail; unauthenticated endpoints. Security comments are rare but never shallow.)
4. PERFORMANCE — Is the access pattern right for the read/write shape, and does it survive N× load? (The winning argument is arithmetic or a dashboard link, not opinion.)
5. RELIABILITY & FAILURE MODES — What happens when a dependency is slow, down, or half-done? How do error states impact customers?
6. TESTABILITY & VERIFIABILITY — Can this code prove itself correct without a human reading every line?
7. OPERABILITY — Can we run this as a team and with agents? Do we know if something is actually failing and can solve it in minutes?

SURFACE-AREA LENS (apply the matching one(s)):
- WEB: read-path decisions, white-label constraints (nothing hardcoded per client), presentation layer.
- MOBILE: contract changes hit shipped binaries — last quarter's app sends last quarter's query. OTA vs native. Deep-link/push.
- DISTRIBUTED: idempotency (assume at-least-once delivery), backpressure, partial-failure, data-store access patterns.

6. Adversarial review — dispatch models

Each adversary is called via the litellm gateway so it truly runs on its own model family. The helper script is at ~/.claude/skills/pr-code-review/adversary.py.

6a. Verify the helper exists

If ~/.claude/skills/pr-code-review/adversary.py is missing, stop and tell the user the skill installation is incomplete.

6b. Run all 4 adversaries in parallel

python3 ~/.claude/skills/pr-code-review/adversary.py \
  "fireworks/models/deepseek-v4-pro" \
  "Focus on logic correctness, edge cases, data flow. Trace how data moves through the diff." \
  /tmp/pr-review-prompt.txt /tmp/pr-review-deepseek.json &

python3 ~/.claude/skills/pr-code-review/adversary.py \
  "fireworks/kimi-k3" \
  "Focus on cross-file impact, long-range dependencies. What does this change break downstream?" \
  /tmp/pr-review-prompt.txt /tmp/pr-review-kimi.json &

python3 ~/.claude/skills/pr-code-review/adversary.py \
  "fireworks/qwen3.7-plus" \
  "Focus on maintainability, extensibility, coupling, boundaries. Can the next change be made safely?" \
  /tmp/pr-review-prompt.txt /tmp/pr-review-qwen.json &

python3 ~/.claude/skills/pr-code-review/adversary.py \
  "gpt-5.6-sol" \
  "Focus on security (IDOR, vacuous auth, unauthenticated endpoints) and reliability/failure modes." \
  /tmp/pr-review-prompt.txt /tmp/pr-review-gpt.json &

wait

Read all 4 result files. Note any that errored — skip them in consensus. If fewer than 2 succeeded, tell the user and stop.


7. Consensus adjudication

Adjudication mode is determined by opus5_available from step 2b:

  • opus5_available: false (default for users without Anthropic): Adjudicate inline on the orchestrator model. Skip to step 7a. Do not mention Opus 5 anywhere except the single reviewer-list line in section 8.
  • opus5_available: true AND a pr-review-opus custom agent is configured (section 11): Optionally dispatch the Opus 5 subagent to produce an adjudication pass, then the orchestrator applies the controlled-entropy check (the subagent's reply is reasoning input; the orchestrator still reads the files). If the dispatch fails or times out, fall back to inline silently.
  • opus5_available: true but no custom agent configured: Adjudicate inline (same as false). The difference is the user could set up the agent later.

7a. Adjudicate (inline, always runs)

Read all adversary findings. As the adjudicator:

  1. Dedupe — same finding from multiple adversaries, OR an adversary finding that matches an existing reviewer comment (CodeRabbit / architecture-reviewer / human) → merge. Note agreement count and sources (e.g. "3/4 adversaries + CodeRabbit" → very high confidence). An existing-reviewer finding that no adversary touched is still kept, but flagged low-confidence unless the adjudicator validates it by reading the code.
  2. Resolve conflicts — if adversaries disagree (one says fix, one says fine), read the actual code at the cited file:line using the Read tool. Decide with evidence. Cite the evidence.
  3. Validate against context — does the PR satisfy the Linear story's acceptance criteria? Does it match the Notion plan/RFC? Flag mismatches as correctness findings (severity 0-1).
  4. Controlled-entropy check (critical) — for each proposed fix, use the Read tool to read the file at the cited line, ±30 lines of context. Verify the fix doesn't ripple. If the fix would require changes elsewhere, note the blast radius and adjust severity up if the fix itself is risky. A fix that creates more entropy than the issue it fixes → downgrade or mark needs-human.
  5. Final severity ranking (0-5, 0 = most severe):
    • 0 — Critical: security hole, data loss, prod-breaking, correctness violation vs spec
    • 1 — High: likely bug, significant failure-mode gap, missing test for critical path
    • 2 — Medium: maintainability/coupling that will hurt the next change, perf concern under load
    • 3 — Low: improvement, minor edge case, testability gap
    • 4 — Nit: style, naming, minor refactor
    • 5 — Optional: suggestion, preference, non-blocking
  6. Produce the final action plan (section 8).

8. Present the action plan

PR Code Review — #<NUM> "<title>" (<branch> → <base>)
Surface: <web|mobile|distributed | mix>
Linear: <ticket ID + title, or "none">
Context docs: <list, or "none">
Reviewers: <adversaries that succeeded> + <orchestrator (inline) | Opus 5 (subagent)> (adjudicator)
Existing reviewer findings ingested: CodeRabbit <n> · architecture-reviewer <n> · human <n> (unresolved)

═══════════════════════════════════════════════
Severity 0 — Critical (<n>)
═══════════════════════════════════════════════

[<concern>] <location> — <what>
  Sources: <adversaries + existing reviewers that flagged it, e.g. "deepseek, qwen, CodeRabbit">
  Why: <reasoning, with surrounding-code context>
  Fix: <what it should be changed to>
  Blast radius: <what else is affected / "none — isolated">
  Confidence: <low|med|high> (agreement: <n>/4 adversaries + <m> existing reviewers)
  Concept: <cross-cutting concept, if not a single line>

(continue for each finding at this severity)

═══════════════════════════════════════════════
Severity 1 — High (<n>)
═══════════════════════════════════════════════
...

(repeat through 2, 3, 4, 5)

═══════════════════════════════════════════════
PR accuracy vs Linear/plan
═══════════════════════════════════════════════
<Does this PR satisfy the story's acceptance criteria? Mismatches? If no Linear story, say so.>

═══════════════════════════════════════════════
Existing reviewer findings — disposition
═══════════════════════════════════════════════
CodeRabbit: <n> unresolved → <x> confirmed & ranked above, <y> refuted (skip-wrong), <z> already-addressed
architecture-reviewer: <n> unresolved → <x> confirmed, <y> refuted/bypass-only, <z> addressed
Human: <n> unresolved → <x> confirmed, <y> refuted, <z> addressed

═══════════════════════════════════════════════
Summary
═══════════════════════════════════════════════
Total findings: <n> (new from adversaries: <n>, confirmed-from-existing: <n>)
S0: <n> · S1: <n> · S2: <n> · S3: <n> · S4: <n> · S5: <n>
Top concern cluster: <which concern had the most findings>
Adversary coverage: <which adversaries succeeded/failed>

Then ask via the question tool:

  • Fix S0-S1 now — dispatch subagents to fix critical + high findings (Recommended)
  • Fix all (S0-S2) — fix critical, high, and medium
  • Adjust plan first — reclassify/add/remove before executing
  • Report only — no changes

9. Execute fixes (if approved)

For each approved finding, dispatch a fix subagent via the task tool (subagent_type: "general").

Each fix agent receives:

  • file:line, the finding, the proposed fix, the blast radius
  • Instruction: make ONLY this change; respect surrounding code; controlled entropy — do not refactor beyond the fix
  • Instruction: stage but do NOT commit

Run all fix agents concurrently. After they complete:

  1. Re-read the changed files to verify fixes don't introduce new issues (lightweight controlled-entropy recheck)
  2. Stage, commit, and push automatically — the user already approved fixes via the single gate in step 8, so no second confirmation:
    git add -p
    git commit -m "Address PR code review findings"
    git push
    If the working tree is dirty with unrelated changes, stage only the files touched by the fix agents (git add <file>...) rather than git add -p to avoid sweeping in unreviewed work.

10. Summary

PR Code Review — done
Fixed: <n> (S0: <n>, S1: <n>, S2: <n>)
Open: <n> (S3-S5 or needs-human)
PR accuracy: <met | partial | mismatch | no-story>

End — no trailing filler.


11. Setup notes & upgrade path

Current approach (no config changes needed, works without Anthropic): Adversaries are called via the litellm gateway (https://litellm.luxurycoders.com/v1) using the API key from ~/.config/opencode/opencode.json. The helper script adversary.py handles this. Adjudication is done inline by the orchestrator (it has file access for controlled-entropy checks). This is the default and requires zero Anthropic configuration.

If gpt-5.6-sol is not available via litellm: the call errors and is skipped — the review proceeds with the other 3 adversaries. The same applies to any unavailable model.

Optional: use the actual Claude Opus 5 model for adjudication. This requires the Anthropic provider to be configured in opencode. The skill detects this in step 2b (opus5_available). If Anthropic is NOT configured, this upgrade path is silently skipped — do not prompt the user or mention Opus 5. If Anthropic IS configured, the user can opt in by adding the pr-review-opus custom agent below.

Upgrade path — custom opencode agents for native multi-model subagents with file access:

This is optional and only fully works when Anthropic is configured. The adversary agents (pr-review-deepseek, pr-review-kimi, pr-review-qwen, pr-review-gpt) work with litellm alone. The pr-review-opus adjudicator requires Anthropic.

Configure custom agents in ~/.config/opencode/opencode.json under an agent key, each pinned to a model:

"agent": {
  "pr-review-deepseek": { "model": "litellm/fireworks/models/deepseek-v4-pro", "prompt": "Adversarial PR reviewer focused on logic correctness, edge cases, data flow." },
  "pr-review-kimi": { "model": "litellm/fireworks/kimi-k3", "prompt": "Adversarial PR reviewer focused on cross-file impact and long-range dependencies." },
  "pr-review-qwen": { "model": "litellm/fireworks/qwen3.7-plus", "prompt": "Adversarial PR reviewer focused on maintainability, extensibility, coupling, boundaries." },
  "pr-review-gpt": { "model": "litellm/gpt-5.6-sol", "prompt": "Adversarial PR reviewer focused on security and reliability/failure modes." }
}

Then optionally add the Opus 5 adjudicator only if Anthropic is configured:

  "pr-review-opus": { "model": "anthropic/claude-opus-5", "prompt": "PR review adjudicator. Dedupe, resolve disagreements, rank severity, validate controlled entropy by reading surrounding code." }

When pr-review-opus is present and opus5_available: true, step 7 dispatches it for the adjudication pass (the orchestrator still applies the controlled-entropy file-read check on top of its output). When it's absent or Anthropic isn't configured, step 7 adjudicates inline — no degradation in correctness, only in adjudicator model strength.

Offer to set this up via the customize-opencode skill if the user wants the upgrade — and check enabled_providers for anthropic before proposing the pr-review-opus agent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment