AgentStack
SKILL verified MIT Self-run

Adversarial Review

skill-abhattacherjee-claude-code-skills-adversarial-review · by abhattacherjee

Runs a Claude↔Gemini adversarial code review on a PR diff or working-tree diff, surfacing only findings both models independently confirm (high-precision, both-confirm rule). Use when: (1) reviewing a PR or working-tree diff with adversarial rigor and you want fewer false positives, (2) you want only findings two independent AI models agree on rather than a single-model opinion, (3) replacing a l…

No reviews yet
0 installs
3 views
0.0% view→install

Install

$ agentstack add skill-abhattacherjee-claude-code-skills-adversarial-review

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Adversarial Review? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Adversarial Review

Runs a symmetric 2-round Claude↔Gemini cross-examination on a diff. Both models discover findings independently in R1, then each cross-examines the other's findings in R2. Only findings the opposing model confirms reach the final report. Single-model findings are retained as UNCONFIRMED, never silently dropped.

Prerequisites

Gemini setup is guided automatically via Step 0 below. When the skill runs, ensure-gemini.sh detects whether Gemini is installed and has a headless-capable credential, then the orchestrator prompts the user to install or authenticate with their consent before proceeding. No manual pre-flight needed; the skill degrades to Claude-only mode only if the user declines or setup fails.

Important — interactive Google login is NOT sufficient. The skill's headless calls (gemini -p ... -o json -m ) require a GEMINI_API_KEY (or Vertex AI credentials). ensure-gemini.sh correctly reports GEMINI_AUTHED=no when only OAuth credentials are present.

Recommended credential location: add GEMINI_API_KEY= to ~/.gemini/.env. The gemini CLI auto-loads this file for all invocations including non-login shells and sub-agent contexts — no shell profile changes needed.

Quick Start

# PR mode (auto-detected when branch has an open PR)
/adversarial-review

# Local mode (working-tree diff vs base)
/adversarial-review

# Force large-diff past the size warning
/adversarial-review --force

Pipeline Overview

detect-mode.sh
     │
     ├── MODE=pr   → diff from PR
     └── MODE=local → diff from working tree vs base

R1 (parallel, blind — neither side sees the other):
  Claude: bug-hunter (opus) + convention-reviewer (sonnet) → r1-claude.json [C-001, C-002, ...]
  Gemini: gemini-review.sh --mode find                     → r1-gemini.json [G-001, G-002, ...]
     │
     │  emit R1 DIGEST
     │
R2 (parallel, symmetric cross-examination):
  Claude: adversarial-cross-examiner (opus)  reads r1-gemini.json → r2-claude-verdicts.json
  Gemini: gemini-review.sh --mode judge      reads r1-claude.json  → r2-gemini-verdicts.json
     │
     │  emit R2 DIGEST
     │
CONVERGE: synthesize.py (4 files) → report.md + report.json

sink.sh → PR comments (MODE=pr) | terminal + .md (MODE=local)

Sub-Agent Registry

| Agent | Model | Round | Purpose | Scheduling | |---|---|---|---|---| | adversarial-bug-hunter | opus | R1 | Find bugs, security, perf, correctness in actual source | Parallel with convention-reviewer and Gemini find | | adversarial-convention-reviewer | sonnet | R1 | Find convention, CLAUDE.md, maintainability issues | Parallel with bug-hunter and Gemini find | | adversarial-cross-examiner | opus | R2 | Judges Gemini's R1 findings against actual source; returns confirm/refute verdicts | Parallel with Gemini judge |

Gemini (R1 find + R2 judge) runs via scripts/gemini-review.sh, not a Claude sub-agent.


Orchestration Workflow

Context Discipline

All round outputs are written to files in a single mktemp -d run directory created at the start of the pipeline. The orchestrator passes file paths between sub-agents and reads only counts and digests into the conversation context — never paste full round JSON into the chat. This keeps context windows bounded regardless of finding volume.

RUN_DIR=$(mktemp -d)
# All round files: $RUN_DIR/r1-claude.json, $RUN_DIR/r1-gemini.json, etc.

Step 0 — Ensure the adversary (Gemini) is available

SCRIPTS="$(dirname "$0")/scripts"
eval "$($SCRIPTS/ensure-gemini.sh --check)"
# Exports: GEMINI_INSTALLED  GEMINI_VERSION  GEMINI_AUTHED
#          INSTALL_HINT       AUTH_HINT

Parse the KEY=VALUE output and follow this decision tree:

Case A — GEMINI_INSTALLED=no: Tell the user Gemini CLI is not installed and show the INSTALL_HINT. ASK whether to install it. If the user consents, run:

npm install -g @google/gemini-cli

After install succeeds, re-run ensure-gemini.sh --check to re-evaluate auth. If the user declines, or if install fails, proceed in degraded Claude-only mode (print the loud banner from the Degradation Behavior section) and continue directly to the detect-mode step.

Case B — installed but GEMINI_AUTHED=no: Tell the user Gemini is installed but lacks a headless-capable credential, and show the AUTH_HINT. Emphasise that interactive Google login is NOT sufficient — the skill's headless calls require an API key. ASK the user to:

  • (Recommended) Add GEMINI_API_KEY= to ~/.gemini/.env — auto-loaded for all shells including sub-agents; get a key at Google AI Studio, OR
  • export GEMINI_API_KEY= in the current shell session, OR
  • Configure Vertex AI: export GOOGLE_GENAI_USE_VERTEXAI=true && export GOOGLE_CLOUD_PROJECT=.

Do NOT suggest gemini interactive login — it produces OAuth credentials insufficient for headless -p/-o json calls.

Once the user confirms they've set a credential, re-run ensure-gemini.sh --check to confirm GEMINI_AUTHED=yes. If they decline, proceed in degraded Claude-only mode.

Case C — GEMINI_AUTHED=unknown: No user interaction needed. Proceed normally; rely on the runtime guard: gemini-review.sh exits 3 (ADVERSARY_UNAVAILABLE) if Gemini actually fails.

Case D — GEMINI_INSTALLED=yes and GEMINI_AUTHED=yes: Adversary confirmed available. Continue to Step 1 with no user interaction.

Step 1 — Detect Mode

eval "$($SCRIPTS/detect-mode.sh $FORCE_FLAG)"
# Exports: MODE  PR  BASE  DIFF_FILE  FILES_FILE
# Exit 2 = diff too large → stop unless --force was passed

Parse the KEY=VALUE output. If exit code is 2 and --force was not passed, halt and tell the user the diff is too large; offer --force to continue.

Step 2 — R1: Parallel Independent Discovery

Launch all three discovery tasks in a single message (parallel dispatch). Neither Claude agent nor Gemini sees the other's output at this stage.

(a) Claude finders — two agents in parallel:

Each receives: absolute path to DIFF_FILE, absolute path to FILES_FILE, and read access to the repo.

  • Bug-hunter returns {"findings":[...]} with origin="claude". Assign sequential ids BH-001, BH-002, ...
  • Convention-reviewer returns {"findings":[...]} with origin="claude". Assign sequential ids CR-001, CR-002, ...

Merge both arrays. Renumber with unified prefix: C-001, C-002, ... Set claude_verdict=null, gemini_verdict=null, status="unconfirmed" on every entry. Write to $RUN_DIR/r1-claude.json.

(b) Gemini finder — run in parallel with Claude agents:

$SCRIPTS/gemini-review.sh \
  --diff "$DIFF_FILE" \
  --mode find \
  --out "$RUN_DIR/r1-gemini.json"

If exit code is 3 (ADVERSARY_UNAVAILABLE): print the degradation banner (see Degradation Behavior), emit the Claude findings as the report (all status=unconfirmed), and exit 0.

Script emits {"findings":[...]} with origin="gemini". Renumber ids: G-001, G-002, ... Write to $RUN_DIR/r1-gemini.json.

r1-claude.json and r1-gemini.json format:

{
  "findings": [
    {
      "id": "C-001",
      "path": "src/foo.ts",
      "line": 42,
      "severity": "critical|important|minor",
      "category": "bug|security|perf|convention|maintainability",
      "title": "Short title",
      "rationale": "Why this is a problem, grounded in source",
      "origin": "claude",
      "claude_verdict": null,
      "gemini_verdict": null,
      "status": "unconfirmed",
      "killed_by": null,
      "kill_reason": null
    }
  ]
}

Emit R1 DIGEST (print to conversation after both sides complete):

=== R1 Discovery Digest ===
Claude findings:  total  (critical=X important=Y minor=Z)
  bug=A  security=B  perf=C  convention=D  maintainability=E
Gemini findings:  total  (critical=X important=Y minor=Z)
  (categories if available)

Step 3 — R2: Parallel Symmetric Cross-Examination

Launch both cross-examination tasks in a single message (parallel dispatch).

(a) Claude cross-examines Gemini's findings:

Launch the adversarial-cross-examiner agent (opus). Provide:

  • Absolute path to $RUN_DIR/r1-gemini.json (Gemini's findings)
  • Absolute path to DIFF_FILE
  • Repo read access

Agent returns {"verdicts":[{"id":"G-NNN","claude_verdict":"confirm|refute","reason":"..."}]}. Write to $RUN_DIR/r2-claude-verdicts.json.

(b) Gemini cross-examines Claude's findings:

$SCRIPTS/gemini-review.sh \
  --diff "$DIFF_FILE" \
  --findings "$RUN_DIR/r1-claude.json" \
  --mode judge \
  --out "$RUN_DIR/r2-gemini-verdicts.json"

If exit code is 3 (ADVERSARY_UNAVAILABLE): print the degradation banner, emit Claude findings as unconfirmed report, exit 0.

Script emits {"verdicts":[{"id":"C-NNN","gemini_verdict":"confirm|refute","reason":"...","confidence":...}]}. Written to $RUN_DIR/r2-gemini-verdicts.json.

Emit R2 DIGEST (print to conversation after both sides complete):

=== R2 Cross-Examination Digest ===
Gemini's verdict on Claude's findings ( total):
  confirmed=A  refuted=B  judged=J  confirm_rate=R.RRR  low_signal=true|false  unrecognized=U
  [⚠ LOW SIGNAL — near-unanimous verdicts; judge may be rubber-stamping]
  [⚠ UNRECOGNIZED — U verdict(s) had an unrecognized value; judge output may be malformed]
Claude's verdict on Gemini's findings ( total):
  confirmed=D  refuted=E  judged=K  confirm_rate=S.RRR  low_signal=true|false  unrecognized=V
  [⚠ LOW SIGNAL — near-unanimous verdicts; judge may be rubber-stamping]
  [⚠ UNRECOGNIZED — V verdict(s) had an unrecognized value; judge output may be malformed]

The per-direction fields confirmed, refuted, judged, confirm_rate, low_signal, and unrecognized come verbatim from synthesize.py stdout. The orchestrator may derive unjudged = − judged if it wants to display that count.

The LOW SIGNAL banner line is printed only when synthesize.py reports low_signal=true for that direction (confirm_rate >= 0.950 or = 5 judged findings). Omit the banner line when low_signal=false. The UNRECOGNIZED banner is printed only when unrecognized > 0.

Low-signal escalation: A low_signal=true direction means the judge confirmed (or refuted) nearly everything it judged over a meaningful sample, producing little discriminating signal. Before trusting the Survivors list, re-run that direction's judge with maximum skepticism and re-synthesize:

  • Gemini rubber-stamping Claude's findings: $SCRIPTS/gemini-review.sh --diff "$DIFF_FILE" --findings "$RUN_DIR/r1-claude.json" --mode judge --strict --out "$RUN_DIR/r2-gemini-verdicts.json" (the --strict flag forces the hardened judge prompt on the first call, requiring Gemini to quote the exact offending diff line verbatim for every confirm)
  • Claude rubber-stamping Gemini's findings: re-spawn the adversarial-cross-examiner agent with an explicit instruction for a maximum-skepticism re-judge — refute unless the evidence is unambiguous and cite the proving line

Re-run synthesize.py after the escalation pass and relay the updated digest. The low_signal flag is informational only — it does not change survivor classification; surviving findings are still those confirmed by the opposing model (see Survivor Rule).

Step 4 — Converge: Synthesize

$SCRIPTS/synthesize.py \
  --claude-findings "$RUN_DIR/r1-claude.json" \
  --gemini-findings "$RUN_DIR/r1-gemini.json" \
  --gemini-verdicts "$RUN_DIR/r2-gemini-verdicts.json" \
  --claude-verdicts "$RUN_DIR/r2-claude-verdicts.json" \
  --md "$RUN_DIR/report.md" \
  --json "$RUN_DIR/report.json"

Script applies the survivor rule and prints survivors=N unconfirmed=M rejected=K to stdout, followed by per-direction lines containing confirmed=, refuted=, judged=, confirm_rate=, low_signal=true|false, and unrecognized=. Read and relay these counts and any low_signal=true flags and any unrecognized > 0 count to the user.

Step 5 — Sink

$SCRIPTS/sink.sh \
  --report-md "$RUN_DIR/report.md" \
  --report-json "$RUN_DIR/report.json" \
  --mode "$MODE" \
  [--pr "$PR"] \
  [--branch "$(git branch --show-current)"]

Survivor Rule

A finding reaches the Survivors section only when the opposing model confirms it. Convergence is mechanical — no model adjudicates both sides.

| Finding origin | Survives when | |---|---| | Claude finding (C-NNN in r1-claude.json) | Gemini verdict = confirm in r2-gemini-verdicts.json | | Gemini finding (G-NNN in r1-gemini.json) | Claude verdict = confirm in r2-claude-verdicts.json |

  • Survivors — confirmed by the opposing model; both models agree
  • Unconfirmed — not judged by the opponent, or opponent abstained
  • Rejected — opponent explicitly refuted; retained with refuter and reason

Nothing is ever silently discarded.

Degradation Behavior

If gemini-review.sh exits with code 3 (unauthenticated, network error, unparseable JSON after one retry) at either the R1 find step or the R2 judge step, the skill degrades loudly to Claude-only mode:

╔══════════════════════════════════════════════════════════╗
║  ADVERSARY UNAVAILABLE — single-model review only        ║
║  Gemini did not respond. Showing Claude R1 findings.     ║
║  Re-run after: add GEMINI_API_KEY= to              ║
║  ~/.gemini/.env  (interactive login is NOT enough)       ║
╚══════════════════════════════════════════════════════════╝

Claude findings are reported as-is with status=unconfirmed — they cannot be cross-confirmed without Gemini. The skill exits 0 (not an error).

Same-Diff Invariant

Both Claude agents (R1) and Gemini (R1 find + R2 judge) receive the byte-identical DIFF_FILE path produced by detect-mode.sh. The orchestrator must not re-generate or alter the diff between steps.

Output Locations

| Mode | Output | |---|---| | pr | Review comments posted to the PR via sink.sh (uses pr-review-loop's pr-review-cli.sh if present) | | local | Terminal report + .adversarial-review.md (gitignored) in repo root |

See Also

  • scripts/ensure-gemini.sh — Step 0 detection: emits Gemini install/auth status + hints; never installs or calls the network
  • scripts/detect-mode.sh — diff extraction and mode detection
  • scripts/gemini-review.sh — R1 find + R2 judge Gemini calls
  • scripts/synthesize.py — survivor rule application (4-file symmetric input)
  • scripts/sink.sh — output routing (PR comments or local file)
  • pr-review-loop plugin — PR comment posting used by sink in PR mode

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.