adversarial-review
adversarial-review takes an artifact you hand it (a claim, a design doc, a
root-cause analysis, a plan, a spec, a diff) and drives it to a consensus
verdict. Three adversarial reviewers attack it in parallel from distinct
lenses (logic, evidence, risk), an independent verifier re-checks their
load-bearing claims against source, and a synthesizer issues one verdict:
CONFIRMED, CONFIRMED-WITH-CHANGES, or REFUTED, with the surviving claims,
the dissent, and what stays unverified.
It is a reviewer, not a builder: distinct from pr-review (which posts to GitHub). Its shape encodes a lesson from running the pattern by hand: parallel independent reviewers can’t take turns clearing their throats, and a separate verifier is needed because a debate’s own citations are often wrong.
Invoke it
Section titled “Invoke it”# A file path, with no argument-channel limit:keelson workflow run adversarial-review --arguments docs/design/some-proposal.md
# …or inline text, for something short:keelson workflow run adversarial-review --arguments "Claim: dropping detached on Windows fixes the console flashing."
# …or a directory of evidence files, bundled verbatim (the claim, a diff, CI logs):keelson workflow run adversarial-review --arguments ./review-bundle/
# Reviewing a change? Pin the tree the claims are about with `subject`:keelson workflow run adversarial-review --arguments ./review-bundle/ --inputs subject=refs/pull/50/head
# Persist the five review artifacts:keelson workflow run adversarial-review --arguments ./proposal.md --inputs out=./review-out
# Keep the artifact's author out of the panel:keelson workflow run adversarial-review --arguments ./proposal.md --inputs author=claude-opus-5
# Combine author-safe seating with persisted artifacts:keelson workflow run adversarial-review --arguments ./proposal.md --inputs author=claude-opus-5 --inputs out=./review-outThe argument is a path to a file, a directory, or the artifact text itself. A
directory is an evidence bundle: every file in it (one level) is concatenated
under labeled separators, so primary artifacts travel verbatim instead of being
transcribed into prose. Transcription is where fidelity dies. Inline text
travels the argument channel, which caps at 16 KiB; paths are read up to a
~1 MB limit (intake rejects larger input rather than review only a prefix,
and a large artifact is also bounded by the reviewers’ model context).
Run it from the repository the artifact is about, so verify can read the
source it cites. When the artifact concerns a specific change, also pass the
optional subject input (any git ref: a branch, a SHA, refs/pull/N/head).
intake snapshots that ref read-only into the run’s artifacts, and verify
checks code claims against the snapshot instead of whatever branch the working
directory happens to have checked out. It needs a Copilot subscription; the
verdict lands in the run record as structured output (verdict, headline,
must_fix, open_questions) with the full prose in report.
Set author to the model id that wrote the artifact. A matching default seat
moves to a same-vendor alternate, every effective seat excludes the author, and
the run reports the moved seat. When author is omitted or blank, the workflow
normalizes it to seated and keeps the default panel.
Set out to a directory to save review-logic.md, review-evidence.md,
review-risk.md, verification.md, and verdict.md. The collector runs after
all five lanes settle. A successful run writes all five files. If a lane fails
or is skipped, its file is absent and any same-name file from an earlier run is
removed, so a reused directory never presents stale output as current.
The shape
Section titled “The shape”Ten nodes: intake captures the artifact, subject-status records snapshot
availability, and author-seat normalizes the optional author. Three reviewers
fan out in parallel, verify checks their claims against source,
verify-gate enforces a substantive result, and synthesize issues the
verdict. The final save collector persists the five prompt outputs when
requested. The reviewers never see each other’s work; the only thing that
crosses between stages is each node’s output.
Node by node
Section titled “Node by node”| Node | Kind | What it does |
|---|---|---|
intake | bash | Captures the artifact (file path, evidence directory, or inline text), rejects empty or truncated input, snapshots the optional subject ref read-only via git archive, and emits the artifact as its output for the rest to inline. |
subject-status | bash | Publishes whether the optional subject snapshot succeeded, failed, or was not requested. |
author-seat | bash | Trims author and emits seated when it is omitted or blank, giving every model selector a defined value. |
reviewer-logic | prompt | model: claude-opus-5, effort: xhigh: attacks internal soundness (does the conclusion follow, what is assumed) with a per-claim verdict. No tools. |
reviewer-evidence | prompt | model: gpt-6-sol, effort: xhigh: separates what the artifact shows from what it asserts, and lists the checkable claims. No tools. |
reviewer-risk | prompt | model: grok-4.6, effort: high: assumes the artifact is adopted and finds what breaks, tracing bad inputs, retries, and partial completion through the proposal, weighted by cost of failure; where the artifact chooses an approach, it also names the strongest alternative ignored. Each issue carries a repro, or none plus the reason. No tools. |
verify | prompt | model: gpt-6-luna, effort: xhigh: re-checks the reviewers’ load-bearing claims against real source with Read/Glob/Grep, trusting no one, and starts each check from the reviewer’s repro; one that does not reproduce is REFUTED. Checks against the subject snapshot when one was taken, else the working directory (where absence of the change is UNVERIFIABLE-HERE, never refutation). |
verify-gate | bash | Requires literal Claim: and Result: records before synthesis. Leading list and bold markers are accepted. |
synthesize | prompt | model: claude-opus-4.8, effort: xhigh: weighs the lenses against the verification and issues the verdict as structured output (verdict / headline / must_fix / open_questions + full prose report), a terse ship/no-ship call with dissent; a comparative artifact must get a choice, not a fence-sit. No tools. |
save | bash | Runs after all five prompt lanes settle and writes their current outputs to out, preferring the full output spill over the capped environment value and removing stale files for lanes with no current output. |
Every prompt node is pinned to the highest effort tier its model serves (xhigh,
or high for grok-4.6) with a generous idle_timeout: a no-tool reviewer at
high effort is one long think followed by text, exactly the chunk-sparse shape a
default idle timeout kills. The pins put each lens on a different vendor,
because lenses sharing one model share its blind spots and can wave the same
flaw through unanimously; the verifier re-derives their claims on
gpt-6-luna and the judge presides on claude-opus-4.8, models no lens uses,
so no node checks or judges its own reasoning. All five ids are
account-dependent: swap or drop any to auto (dropping its effort: tier with
it) if your Copilot list lacks it.
When author matches one of those five defaults, that seat moves to a
same-vendor alternate from the workflow’s closed model_by map. Other seats
keep their defaults, and the collector names the effective move in the run
record.
The parts worth a second look
Section titled “The parts worth a second look”Parallel, not turn-taking. The three reviewers run with context: fresh, so
none sees another’s take. Independence is the point: it removes the
throat-clearing a turn-based debate produces and gets three genuinely separate
attacks instead of a conversation that converges too early.
The verifier trusts no one. verify re-derives the checkable claims from
source rather than relaying the reviewers’ citations, or the artifact’s. It is
the load-bearing node: a debate’s participants are confidently wrong often
enough that an independent source check is what separates a verdict from a
vibe. Ambiguous or partial evidence never rounds up to CONFIRMED; claims it
cannot check with its tools it marks UNVERIFIABLE-HERE rather than guessing.
Minimal privilege for untrusted input. The artifact can carry
prompt-injection, so the reviewers and the synthesizer run with no tools at
all. It reaches them inlined as $intake.output, so no prompt node reads it
from disk. No-tools removes the side-effect surface (injected text can’t write
files or run shell), but it does not stop the artifact from trying to steer a
verdict, so every prompt also frames the artifact as untrusted data to review,
not instructions to follow. The deterministic intake bash node reads a
user-supplied path; only verify holds filesystem tools, and only to check
source claims, the one node where reading the repo is the point.
The verdict is weighted to evidence. Synthesis is told to weight the verifier over the reviewers wherever they conflict, and to surface dissent rather than smooth it over: a split panel reads as a split verdict, not a false consensus.
Patterns it demonstrates
Section titled “Patterns it demonstrates”- Fan-out then converge: three independent lanes over one input, a converge node that references each lane’s
$nodeId.output. The same skeleton as pr-review. - Bound an agent’s reach: the reviewers and synthesizer get no tools; only
verifycan touch the filesystem.
Adapt it
Section titled “Adapt it”- Add a lens. A fourth reviewer is one more node
depends_on: [intake]withallowed_tools: []; add it toverify’s andsynthesize’s references. - Re-seat the panel. The pins are choices, not requirements: any model in your Copilot list can hold a lens. Keep the seating rule when you swap: no two lenses on one model, and the verifier and judge on models no lens uses.
- Keep author-aware seating aligned. When a default or alternate changes,
update its
model_bycase and the collector’s moved-seat message together. Every effective seat must exclude the model supplied throughauthor. - Cheaper runs. The maximum-effort posture buys depth; for a quick
pressure-test, drop the lenses to a lighter
effort:tier (orauto) and keepverifyandsynthesizestrong; the verdict degrades last.
Related
Section titled “Related”- pr-review: the fan-out → synthesize sibling that posts to GitHub.
- Authoring workflows: the read-only rail and safe-output recipes.
- Workflow nodes:
depends_on,trigger_rule,allowed_tools, and$nodeId.output.