Skip to content
 
 

Repository files navigation

adversarial-spec

An agent skill (shipped as a Claude Code plugin) that iteratively refines product specifications through multi-model debate until consensus is reached.

Key insight: A single LLM reviewing a spec will miss things. Multiple LLMs debating a spec will catch gaps, challenge assumptions, and surface edge cases that any one model would overlook. The result is a document that has survived rigorous adversarial review.

The host agent is an active participant, not just an orchestrator. It provides independent critiques, challenges opponent models, and contributes substantive improvements alongside external models.

Quick Start

# 1. Install the skill into your host — Claude Code, Copilot CLI, or Codex CLI (see Hosts below)

# Claude Code:
claude plugin marketplace add vftiago/adversarial-spec
claude plugin install adversarial-spec

# Copilot CLI:
git clone https://github.com/vftiago/adversarial-spec
copilot skill add ./adversarial-spec/skills/adversarial-spec

# Codex CLI:
codex plugin marketplace add vftiago/adversarial-spec
codex plugin add adversarial-spec@adversarial-spec

# 2. Run it — zero config if an agent CLI you subscribe to is installed (codex, copilot, claude)
/adversarial-spec "Build a rate limiter service with Redis backend"

On Copilot CLI and Codex CLI there is no slash command — just ask for the skill by name (e.g. "use adversarial-spec to refine this spec"). Instead of copilot skill add, you can also drop skills/adversarial-spec/ into a project skill directory (.github, .agents, .claude) or ~/.copilot/skills/.

Opponents run on the agent CLIs you're already logged into — no API keys needed. To field models from other vendors instead (or as well), set any provider's API key; see API Opponents.

How It Works

You describe product --> Host drafts spec --> Multiple LLMs critique in parallel
        |                                              |
        |                                              v
        |                              Host synthesizes + adds own critique
        |                                              |
        |                                              v
        |                              Revise and repeat until ALL agree
        |                                              |
        +--------------------------------------------->|
                                                       v
                                            User review period
                                                       |
                                                       v
                                            Final document output
  1. Describe your product concept or provide an existing document
  2. (Optional) Start with an in-depth interview to capture requirements
  3. The host agent drafts the initial document (PRD or tech spec)
  4. Document is sent to opponent models (GPT, Gemini, Grok, etc.) for parallel critique
  5. The host provides independent critique alongside opponent feedback
  6. The host synthesizes all feedback and revises
  7. Loop continues until ALL opponent models AND the host agree
  8. User review period: request changes or run additional cycles
  9. Final converged document is output

Hosts

The skill is host-agnostic: any coding agent that can run shell commands, read/write files, and ask the user questions can drive the debate as the "host agent". The host drafts the document, runs debate.py for opponent critiques, synthesizes the feedback, and revises.

  • The skill runs on Claude Code (recommended, most tested), GitHub Copilot CLI, or Codex CLI. Note that the host is the CLI harness, not a model family — Copilot itself can run several LLMs, including Claude models. These three are the verified hosts; the CLI adapters they map to are the only supported opponents on the CLI side.
  • Pick opponents that don't run on the host's own CLI or subscription. A host/opponent pair sharing one subscription draws quota from both sides, and a long debate can exhaust it mid-run — e.g. a Copilot host should not field copilot/ opponents, and a Codex host should not field codex/ opponents, whichever models each side runs. Watch the Codex case in particular: codex/ is a common auto-detect default, so on a Codex host always pass an explicit --models list. The skill instructs the host to exclude such opponents by default.

In the normal skill flow you never run debate.py yourself — the host does, translating plain-language requests ("focus on security", "run codex at xhigh reasoning") into the right flags. The command examples in the rest of this README serve two purposes: they show what you can ask the host for, and they work as-is if you want to run the script directly (one-off critiques, scripting, debugging).

The command examples below refer to the debate script as "$DEBATE_PY". Resolve it once (project-local skill directories first, then personal ones):

DEBATE_PY="$(find .claude .github .agents ~/.claude ~/.codex ~/.copilot ~/.agents -name debate.py -path '*adversarial-spec*' 2>/dev/null | head -1)"

Requirements

  • Python 3.10+
  • litellm package: pip install litellm
  • At least one opponent transport: a supported agent CLI installed (codex, copilot, or claude) OR an API key for any provider

CLI Opponents (subscriptions, no API keys)

Models prefixed with a CLI transport run through local agent CLIs on the subscription you're already logged into. Installing and authenticating a CLI is between you and its vendor — this project assumes it's already on your PATH and logged in; a missing transport fails fast before any inference, with a hint naming what's missing. Unlike the API side, this list is exhaustive — each transport is a dedicated adapter, and these three are the only ones:

Transport Subscription Model Syntax
Codex CLI ChatGPT codex/ (CLI's default), or codex/<name> from the /model picker
Copilot CLI GitHub Copilot copilot/auto, or copilot/<slug> from the /model picker
Claude CLI Claude claude-cli/ (CLI's default), or claude-cli/<name> (alias or full name)

Check installation status of all transports and configured keys:

python3 "$DEBATE_PY" providers

Codex CLI Support

Codex CLI allows ChatGPT Pro subscribers to use OpenAI models without separate API credits. Models prefixed with codex/ are routed through the Codex CLI.

Usage:

# Prefix with codex/; bare codex/ uses the CLI's configured default
python3 "$DEBATE_PY" critique --models codex/,gemini/gemini-2.0-flash < spec.md

Reasoning effort:

--codex-reasoning sets how much thinking time codex models get. When the flag is omitted, no override is passed — the Codex CLI uses whatever your own ~/.codex config specifies (or codex's stock default). xhigh is worth asking for whenever you can afford the extra latency — it produces materially deeper critiques. In the skill flow, just tell the host (e.g. "run codex at xhigh reasoning"). If running the script directly, pass the flag:

python3 "$DEBATE_PY" critique --models codex/ --codex-reasoning xhigh < spec.md

The value is passed through to codex verbatim (as -c model_reasoning_effort="..."), so any level your codex version supports works (currently minimal, low, medium, high, xhigh); an invalid value fails fast at the CLI before any inference runs.

The same advice applies to your other adversaries, but the knob lives elsewhere: --codex-reasoning only affects codex/ models. copilot/ and claude-cli/ opponents take their effort/thinking settings from their own CLI configuration, and API models run at provider defaults — so pick the strongest reasoning configuration each adversary offers where you control it. All available flags are listed in the CLI Reference.

Model names:

Bare codex/ omits the --model flag entirely, so the Codex CLI uses its own configured default — always valid, with no model name to go stale. To pin a specific model, the name after the codex/ prefix is passed directly to codex --model, so any model your ChatGPT subscription offers works. Run codex and type /model to see the current list; invalid names fail fast with a clear error before any inference runs.

Independence: critiques run from a neutral working directory, so your project's AGENTS.md can't steer the review and codex's workspace write access is confined to an empty throwaway directory.

Note: if Codex CLI is your host (see Hosts), don't use codex/ opponents — they share the host's subscription quota, and codex/ is a common auto-detect default, so pass an explicit --models list.

Copilot CLI Support

GitHub Copilot CLI allows GitHub Copilot subscribers to use OpenAI and Anthropic models without separate API credits. Models prefixed with copilot/ are routed through the Copilot CLI in non-interactive mode.

Usage:

# Prefix with copilot/
python3 "$DEBATE_PY" critique --models copilot/auto,gemini/gemini-2.0-flash < spec.md

Model names:

The name after the copilot/ prefix is passed directly to copilot --model, so any model available to your Copilot subscription works:

  • copilot/auto - always valid; lets Copilot pick a model
  • For a specific model, run copilot and type /model to see what your subscription offers (there is no non-interactive listing), then lowercase the display name and replace spaces with hyphens (e.g. "GPT-5.6 Terra" becomes copilot/gpt-5.6-terra)

Invalid names fail fast with Model "..." is not available before any inference runs, so no quota is consumed.

Independence: critiques run from a neutral working directory, so your project's .github/copilot-instructions.md (or AGENTS.md) can't steer the review.

Note: if Copilot CLI is your host (see Hosts), don't use copilot/ opponents — they share the host's subscription quota.

Claude CLI Support

Claude Code's CLI allows Claude subscribers to field Claude models as opponents without separate API credits — most usefully from a non-Claude host (e.g. Copilot CLI), so a debate can include Claude without any ANTHROPIC_API_KEY. Models prefixed with claude-cli/ are routed through claude -p in print mode.

Usage:

# Prefix with claude-cli/; bare claude-cli/ uses the CLI's configured default
python3 "$DEBATE_PY" critique --models claude-cli/,gemini/gemini-2.0-flash < spec.md

Model names:

The name after the claude-cli/ prefix is passed directly to claude --model:

  • claude-cli/ (bare) - the CLI's own configured default model, always valid
  • claude-cli/sonnet, claude-cli/opus, claude-cli/haiku - aliases for the latest model of each family
  • claude-cli/<full-model-name> - a specific version

Models your plan doesn't include fail fast with a clear error before any inference runs (e.g. some plans don't offer every model).

Independence: the critique runs isolated from your local Claude Code context — from a neutral working directory, with settings and tools disabled — so project CLAUDE.md/AGENTS.md files can't leak into the "independent" review.

Note: if Claude Code is your host (see Hosts), don't use claude-cli/ opponents — they share the host's subscription quota.

Exported ANTHROPIC_API_KEY: opponent CLIs inherit your environment, and Claude CLI gives an exported ANTHROPIC_API_KEY precedence over your claude.ai login (verified on claude 2.1.x — a warning, not an error). If you set the key for API opponents and also use claude-cli/, those critiques bill the key rather than your subscription, and an invalid key hangs until --timeout instead of failing fast. Unset it for the run (env -u ANTHROPIC_API_KEY python3 "$DEBATE_PY" ...) or use Anthropic API model names instead. Codex and Copilot are unaffected: both use their login state even when OPENAI_API_KEY or a GitHub token is exported.

API Opponents

Any provider with a configured API key works — set whichever key(s) you prefer; none is ranked above another. Keys are read from the process environment: export them in your shell profile, or scope them per project with a tool like direnv. A .env file in the project is not read. Model IDs are passed through verbatim, so any model the provider offers works. The table shows the prefix syntax, not a list of supported models.

The table is also not a support boundary: it lists the providers that get pre-flight key validation, providers-listing status, and auto-detection. Any other litellm-supported provider works too — set the env var litellm expects for it and use litellm's model prefix (e.g. PERPLEXITY_API_KEY + perplexity/<model>); a missing key then surfaces as a call-time error instead of a pre-flight check.

Provider Env Var Model Syntax
OpenAI OPENAI_API_KEY bare model ID, e.g. gpt-4o
Anthropic ANTHROPIC_API_KEY bare model ID, e.g. claude-sonnet-4-20250514
Google GEMINI_API_KEY gemini/<model>
xAI XAI_API_KEY xai/<model>
Mistral MISTRAL_API_KEY mistral/<model>
Groq GROQ_API_KEY groq/<model>
OpenRouter OPENROUTER_API_KEY openrouter/<vendor>/<model>
Deepseek DEEPSEEK_API_KEY deepseek/<model>
Zhipu ZHIPUAI_API_KEY zhipu/<model>

One key is enough for a full debate: opponents can all come from a single provider, mix providers if you hold several keys, or mix freely with CLI opponents. Aggregators (e.g. OpenRouter) reach many vendors' models through one key; native keys talk to each vendor directly — trade-offs like free tiers, fees, and data governance are your call.

OpenAI-Compatible Endpoints

For models that expose an OpenAI-compatible API (local LLMs, self-hosted models, alternative providers), set OPENAI_API_BASE:

# Point to a custom endpoint
export OPENAI_API_KEY="your-key"
export OPENAI_API_BASE="https://your-endpoint.com/v1"

# Use with any model name
python3 "$DEBATE_PY" critique --models gpt-4o < spec.md

This works with:

  • Local LLM servers (Ollama, vLLM, text-generation-webui)
  • OpenAI-compatible providers
  • Self-hosted inference endpoints

Usage

Start from scratch:

/adversarial-spec "Build a rate limiter service with Redis backend"

Refine an existing document:

/adversarial-spec ./docs/my-spec.md

You will be prompted for:

  1. Document type: PRD (business/product focus) or tech spec (engineering focus)
  2. Interview mode: Optional in-depth requirements gathering session
  3. Opponent models: Multi-select from the transports you have available
  4. Round cap per cycle: Hard limit on debate rounds (default: 5), enforced by debate.py itself. On the last allowed round opponents switch to final-round mode — agree unless something is factually wrong, contradictory, or unimplementable. If the debate still hasn't converged, the host stops, shows you the unresolved objections, and only continues if you explicitly grant more rounds. A general "until convergence" instruction does not override the cap

More models = more perspectives = stricter convergence.

Document Types

PRD (Product Requirements Document)

For stakeholders, PMs, and designers.

Sections: Executive Summary, Problem Statement, Target Users/Personas, User Stories, Functional Requirements, Non-Functional Requirements, Success Metrics, Scope (In/Out), Dependencies, Risks

Critique focuses on: Clear problem definition, well-defined personas, measurable success criteria, explicit scope boundaries, no technical implementation details

Technical Specification

For developers and architects.

Sections: Overview, Goals/Non-Goals, System Architecture, Component Design, API Design (full schemas), Data Models, Infrastructure, Security, Error Handling, Performance/SLAs, Observability, Testing Strategy, Deployment Strategy

Critique focuses on: Complete API contracts, data model coverage, security threat mitigation, error handling, specific performance targets, no ambiguity for engineers

Core Features

Interview Mode

Before the debate begins, opt into an in-depth interview session to capture requirements upfront.

Covers: Problem context, users/stakeholders, functional requirements, technical constraints, UI/UX, tradeoffs, risks, success criteria

The interview uses probing follow-up questions and challenges assumptions. After completion, the host synthesizes answers into a complete spec before starting the adversarial debate.

The Host's Active Participation

Each round, the host agent:

  1. Reviews opponent critiques for validity
  2. Provides independent critique (what did opponents miss?)
  3. States agreement/disagreement with specific points
  4. Synthesizes all feedback into revisions

Display format:

--- Round N ---
Opponent Models:
- [GPT-4o]: critiqued: missing rate limit config
- [Gemini]: agreed

Host Critique:
Security section lacks input validation strategy. Adding OWASP top 10 coverage.

Synthesis:
- Accepted from GPT-4o: rate limit configuration
- Added by host: input validation, OWASP coverage
- Rejected: none

Early Agreement Verification

If a model agrees within the first 2 rounds, the host is skeptical. The model is pressed to:

  • Confirm it read the entire document
  • List specific sections reviewed
  • Explain why it agrees
  • Identify any remaining concerns

This prevents false convergence from models that rubber-stamp without thorough review.

User Review Period

After all models agree, you enter a review period with three options:

  1. Accept as-is: Document is complete
  2. Request changes: the host updates the spec, you iterate without a full debate cycle
  3. Run another cycle: Send the updated spec through another adversarial debate

Additional Review Cycles

Run multiple cycles with different strategies:

  • First cycle with fast models, second with stronger reasoning models
  • First cycle for structure/completeness, second for security focus
  • Fresh perspective after user-requested changes

PRD to Tech Spec Flow

When a PRD reaches consensus, you're offered the option to continue directly into a Technical Specification based on the PRD. This creates a complete documentation pair in a single session.

Advanced Features

Critique Focus Modes

Direct models to prioritize specific concerns:

--focus security      # Auth, input validation, encryption, vulnerabilities
--focus scalability   # Horizontal scaling, sharding, caching, capacity
--focus performance   # Latency targets, throughput, query optimization
--focus ux            # User journeys, error states, accessibility
--focus reliability   # Failure modes, circuit breakers, disaster recovery
--focus cost          # Infrastructure costs, resource efficiency

Model Personas

Have models critique from specific professional perspectives:

--persona security-engineer      # Thinks like an attacker
--persona oncall-engineer        # Cares about debugging at 3am
--persona junior-developer       # Flags ambiguity and tribal knowledge
--persona qa-engineer            # Missing test scenarios
--persona site-reliability       # Deployment, monitoring, incidents
--persona product-manager        # User value, success metrics
--persona data-engineer          # Data models, ETL implications
--persona mobile-developer       # API design for mobile
--persona accessibility-specialist  # WCAG, screen readers
--persona legal-compliance       # GDPR, CCPA, regulatory

Custom personas also work: --persona "fintech compliance officer"

Context Injection

Include existing documents for models to consider:

--context ./existing-api.md --context ./schema.sql

Use cases:

  • Existing API documentation the new spec must integrate with
  • Database schemas the spec must work with
  • Design documents or prior specs for consistency
  • Compliance requirements documents

Session Persistence and Resume

Long debates can crash or need to pause. Sessions save state automatically:

# Start a named session (round 1, cap 5 by default)
echo "spec" | python3 "$DEBATE_PY" critique --models gpt-4o --session my-feature-spec

# Next round: same session, revised spec — the round number advances automatically
echo "revised spec" | python3 "$DEBATE_PY" critique --models gpt-4o --session my-feature-spec

# Resume after a crash (uses the stored spec)
python3 "$DEBATE_PY" critique --resume my-feature-spec

# Past the cap the script refuses (exit 3) and lists unresolved objections;
# grant more rounds by raising the cap on the same session
echo "spec" | python3 "$DEBATE_PY" critique --models gpt-4o --session my-feature-spec --max-rounds 8

# List all sessions
python3 "$DEBATE_PY" sessions

Sessions save:

  • Current spec state
  • Round number and round cap
  • All configuration (models, focus, persona, etc.)
  • History of previous rounds, with a summary of each model's objection

Sessions are what make the round cap enforceable: the host is instructed to pass --session on every call, so a host that keeps looping "until convergence" is stopped by the engine, not by prose.

Sessions are stored in ~/.config/adversarial-spec/sessions/.

Auto-Checkpointing

When using sessions, each round's spec is saved to .adversarial-spec-checkpoints/:

.adversarial-spec-checkpoints/
├── my-feature-spec-round-1.md
├── my-feature-spec-round-2.md
└── my-feature-spec-round-3.md

Use these to rollback if a revision makes things worse.

Preserve Intent Mode

Convergence can sand off novel ideas when models interpret "unusual" as "wrong". The --preserve-intent flag makes removal expensive:

--preserve-intent

When enabled, models must:

  1. Quote exactly what they want to remove or substantially change
  2. Justify the harm - not just "unnecessary" but what concrete problem it causes
  3. Distinguish error from preference - only remove things that are factually wrong, contradictory, or risky
  4. Ask before removing unusual but functional choices: "Was this intentional?"

This shifts the default from "sand off anything unusual" to "add protective detail while preserving distinctive choices."

Use when:

  • Your spec contains intentional unconventional choices
  • You want models to challenge your ideas, not homogenize them
  • Previous rounds removed things you wanted to keep

Saved Profiles

Save frequently used configurations:

# Create a profile
python3 "$DEBATE_PY" save-profile strict-security \
  --models gpt-4o,gemini/gemini-2.0-flash \
  --focus security \
  --doc-type tech

# Use a profile
python3 "$DEBATE_PY" critique --profile strict-security < spec.md

# List profiles
python3 "$DEBATE_PY" profiles

Profiles are stored in ~/.config/adversarial-spec/profiles/.

Diff Between Rounds

See exactly what changed between spec versions:

python3 "$DEBATE_PY" diff --previous round1.md --current round2.md

Export to Task List

Extract actionable tasks from a finalized spec:

cat spec-output.md | python3 "$DEBATE_PY" export-tasks --models gpt-4o --doc-type prd

Output includes title, type, priority, description, and acceptance criteria.

Use --json for structured output suitable for importing into issue trackers.

Output

Final document is:

  • Complete, following full structure for document type
  • Vetted by all models until unanimous agreement
  • Ready for stakeholders without further editing

Output locations:

  • Printed to terminal
  • Written to spec-output.md (PRD) or tech-spec-output.md (tech spec)

Debate summary includes rounds completed, cycles run, models involved, the host's contributions, and key refinements made.

CLI Reference

# Core commands
python3 "$DEBATE_PY" critique --models MODEL_LIST --doc-type TYPE [OPTIONS] < spec.md
python3 "$DEBATE_PY" critique --resume SESSION_ID
python3 "$DEBATE_PY" diff --previous OLD.md --current NEW.md
python3 "$DEBATE_PY" export-tasks --models MODEL --doc-type TYPE [--json] < spec.md

# Info commands
python3 "$DEBATE_PY" providers      # List providers and API key status
python3 "$DEBATE_PY" focus-areas    # List focus areas
python3 "$DEBATE_PY" personas       # List personas
python3 "$DEBATE_PY" profiles       # List saved profiles
python3 "$DEBATE_PY" sessions       # List saved sessions

# Profile management
python3 "$DEBATE_PY" save-profile NAME --models ... [--focus ...] [--persona ...]

# Example: combined flags — mixed CLI + API opponents, max codex effort,
# security focus, extra context, resumable session
python3 "$DEBATE_PY" critique \
  --models codex/,claude-cli/,xai/grok-3 \
  --doc-type tech \
  --codex-reasoning xhigh \
  --focus security \
  --context ./api.md \
  --session my-debate < spec.md

Options:

  • --models, -m - Comma-separated model list (auto-detects from available API keys if not specified)
  • --doc-type, -d - prd or tech (default: tech)
  • --round, -r - Override the round number (default: continues from the session, else 1)
  • --max-rounds - Hard round cap for the session (default: 5); raise it on the same session to grant more rounds
  • --converge - Final-round mode: agree unless factually wrong, contradictory, or unimplementable (automatic on the last allowed round)
  • --codex-reasoning - Reasoning effort for Codex models, passed through verbatim (e.g. xhigh — recommended for the deepest critiques). Omitted: your codex config decides
  • --codex-search - Enable web search for Codex CLI models
  • --focus, -f - Focus area (security, scalability, performance, ux, reliability, cost)
  • --persona - Professional persona
  • --context, -c - Context file (repeatable)
  • --profile - Load saved profile
  • --preserve-intent - Require justification for removals
  • --session, -s - Session ID for persistence, checkpointing, and round counting (reuse to continue)
  • --resume - Resume a previous session
  • --press, -p - Anti-laziness check
  • --json, -j - JSON output
  • --timeout - Timeout in seconds for model API/CLI calls (default: 600)

Exit codes: 0 ran, 1 usage/input error, 2 missing credentials or session, 3 round cap reached without convergence (no models called). The cap applies with or without a session: a sessionless --round N above --max-rounds (default 5) also exits 3.

File Structure

adversarial-spec/
├── .claude-plugin/
│   ├── plugin.json           # Plugin metadata
│   └── marketplace.json      # Marketplace listing
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── pyproject.toml
└── skills/
    └── adversarial-spec/
        ├── SKILL.md          # Skill definition and process
        └── scripts/
            ├── debate.py     # CLI entry point and debate orchestration
            ├── models.py     # Model dispatch and CLI/API adapters
            ├── providers.py  # Provider registry and key detection
            ├── prompts.py    # Critique prompt library
            ├── session.py    # Session persistence and checkpointing
            └── tests/        # Test suite

License

MIT

About

Iteratively refine product specifications by debating between multiple LLMs until all models reach consensus.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages