Show HN: Paranoia – Get a cold, adversarial review of your code Paranoia, a new open-source tool from developer subvertnormality, provides adversarial code reviews by running one frontier coding agent against another: it integrates with Claude Code to have Codex review code, or with Codex to have Claude Code review, using a local MCP server. The tool, which requires Python 3.11+, git, and a subscription to the reviewing agent's CLI, offers five tools including critique_branch, critique_plan, query, rebut, and arbitrate, and includes a convergence loop to iterate reviews until issues are resolved. Get a cold, adversarial review of your code, your plans, and your decisions from the other frontier coding agent — running locally, on its own subscription, with full read access to your repository. Install it into Claude Code and reviews are performed by Codex. Install it into Codex and reviews are performed by Claude Code. paranoia-local is the MCP server between them: it builds the prompt, runs the other agent read-only, and returns a structured critique. ┌──────────────┐ "paranoia: critique this branch" ┌───────────────┐ │ Claude Code │ ───────────────────────────────────► │ paranoia-local│ │ your work │ │ MCP, local │ └──────────────┘ └───────┬───────┘ │ codex exec read-only ┌───────▼────────┐ │ Codex / GPT-5 │ ← reads the repo, │ cold reviewer │ decides what to open └────────────────┘ Contents · Quickstart quickstart · The five tools the-five-tools · How reviews work: the convergence loop how-reviews-work-the-convergence-loop · Tool reference tool-reference · Output reference output-reference · Configuration configuration · Safety model safety-model · Development development 1. Prerequisites - Python 3.11+ and git on PATH - The reviewing agent's CLI, installed and signed in on a subscription: Codex CLI https://developers.openai.com/codex codex , ≥ 0.144 or Claude Code https://code.claude.com claude arbitrate needs both CLIs; the four review tools need only the other one 2. Install git clone https://github.com/subvertnormality/paranoia-local cd paranoia-local pip install -e . 3. Wire it into your agent. --engine names the agent that performs reviews, which is the opposite one from the caller. Into Claude Code reviews performed by Codex claude mcp add paranoia -- paranoia-local --engine codex Into Codex reviews performed by Claude Code — needs two extra keys codex mcp add paranoia -- paranoia-local --engine claude Then edit ~/.codex/config.toml . Codex defaults to a 60-second tool timeout and a 10-second startup timeout; a review runs for minutes, so both must be raised or every call fails: mcp servers.paranoia command = "paranoia-local" args = "--engine", "claude" tool timeout sec = 3600 startup timeout sec = 30 Verify with codex mcp get paranoia . 4. Ask for a review. "Use paranoia to critique this branch against main. Intent: add overdraft protection to withdraw ." Your agent calls: { "name": "critique branch", "arguments": { "repo path": "/Users/you/Work/my-project", "base ref": "main", "round": 1, "diff intent": "Add overdraft protection to withdraw ." } } You get back a five-section critique with severity-tagged findings, and a computed CONVERGENCE: trailer telling you whether the loop may stop. | Tool | Use it to | Needs | |---|---|---| critique branch | repo path critique plan repo path + plan text | plan path query question rebut session ref from that review arbitrate both vendors independently repo path , decision , options , stakes Every review returns a session ref in its footer. Pass it to rebut to reopen that exact reviewer session. A single review is rarely the end of it. You review, you fix, you review again. paranoia-local models that as a convergence loop , and gives you four controls over it plus one computed signal that tells you when to stop. round 1 ──► review ──► fix ──► round 2 ──► review ──► fix ──► round 3 ──► CONVERGED │ │ + └── already raised ────────────┴── already raised ────► CONVERGENCE: NOT-BLOCKED Each round is a fresh, cold reviewer — it has no memory of the last one. You carry state forward with the arguments below. The 1-based round number. Increment it every round. At round = 3 the reviewer reports only merge-blocking findings and withholds MINOR and OUT-OF-SCOPE , writing CONVERGED when none remain. This is the lever that makes a loop stop instead of grinding through diminishing findings. round is required on critique branch and critique plan unless you pass class closure: false . The real deployment context, threat model, and scale the work operates in: "stakes": "Internal booking API, single team, authenticated first-party callers, ~1k req/min." The reviewer treats it as the boundary of legitimate concern . Findings that assume adversaries, scale, or failure modes beyond it are dropped or tagged OUT-OF-SCOPE , never must-fix. Omit it and the reviewer assumes a modest internal tool; a review with no stakes ends with a STAKES: unstated line. Pass stakes: "unstated" to accept that reading deliberately and silence the line. Set it once per project in .paranoia.toml paranoiatoml ; override per call to tighten it for a specific surface. One-line, file:line -cited claims you have already accepted from earlier rounds. The reviewer is told not to restate them and to hunt for what they missed. Pass the claim and its citation, never the previous reviewer's prose. "already raised": "withdraw ignores pending holds — accounts.py:88", "the overdraft test asserts the fee, not the balance — test accounts.py:210" On by default. A finding is usually an instance of a class: one violated invariant, several sites. Class closure makes the class itself a tracked object that survives the round. The reviewer ends its review with a register block: === CLASS REGISTER === CLASS: every public writer must validate its input before the first mutation SEVERITY: MAJOR PATTERN: def create|update a-z +\ . \ :\n ? . validate PATHSPEC: src/ The server then, every round: re-runs each registered regex itself against the reviewed snapshot git grep , and lists every surviving match to the next reviewer; refuses to report the loop unblocked while any BLOCKER / MAJOR / FATAL class still matches; computes the verdict in Python and appends it as the CONVERGENCE: trailer. A class closes when its predicate returns zero matches and reopens the moment it matches again. MINOR and OUT-OF-SCOPE classes are tracked but advisory — they never block. Where no regex can express the invariant, the reviewer registers a PROCEDURE: instead. Those are unmechanized : nothing re-runs them, they are shown to every later reviewer, and they close only when a reviewer explicitly writes CLOSED: