Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1,340 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Reviewgate

Reviewgate intercepts your Claude Code or Codex agent's turn-end, runs an independent LLM review over the actual change, and requires an explicit outcome for every blocking finding. It is the checker half of the agent loop, packaged so the writer can't grade its own homework. A clean PASS releases the turn; WARN-only policy, infrastructure deferral and bounded human escalation remain visibly distinct from PASS.

  • πŸ› Catches real bugs before the agent says "done" β€” a heterogeneous panel (Codex Β· Gemini Β· Claude Β· OpenCode Β· OpenRouter Β· Ollama) reviews the actual diff, in-loop, every turn.
  • 🚦 Never turns failure into green β€” a crash, timeout or quota outage is never reported as PASS. Reviewgate blocks, explicitly defers for a bounded window, or escalates to a human according to the configured policy.
  • πŸ“‹ Leaves an audit trail β€” every finding and every fix/reject decision is written to files (.reviewgate/pending.md) a human (or CI) can inspect later β€” no chat-stream parsing, no flaky stdout scraping.

Reviewers run the official provider CLIs, so users on Claude Pro/Max, ChatGPT Plus/Pro and Gemini Advanced pay $0 per review within their subscription quotas (OAuth-first). OpenRouter reviewers use an API key and can target any hosted model by name.

Warning

Alpha. Reviewgate runs provider CLIs on your working-tree diff. Reviewer filesystem write isolation plus secret-path masking ships (macOS Seatbelt, Linux bubblewrap) but is opt-in (sandbox.mode, default off). It is a denylist model, not a read allowlist: other host files may remain readable, and network egress is not isolated. Prefer your own code / trusted repos. See Security.

Reviewgate Alpha.11 replay: a real gate blocks a CRITICAL SQL-injection finding, consumes an accepted/fixed decision, then passes the parameterized fix.

Demo disclosure: this is a deterministic replay of two provider responses recorded during a real reviewgate@0.1.0-alpha.11 OpenRouter run. The production init, control-plane, trigger, Stop-hook, decision, re-review and audit-verifier paths execute live. The script checksum-verifies the cassette and aborts on prompt drift. Run it and inspect the provenance.

60-second quickstart

npm i -g reviewgate     # your platform's prebuilt binary
cd your-repo
reviewgate init         # configure policy + hosts, install hooks, record LKG, run doctor

No reviewer CLI available? Use the tested OpenRouter-only setup. For claims, raw caveats and historical catches, see Evidence.

Claude Code is armed as soon as init completes. For Codex, init installs the hook definitions, but Codex intentionally keeps new project commands disabled until you approve their exact hash once through /hooks. After activation, either host runs Reviewgate when it tries to finish a changed turn.

init recommends both hosts and can also target one explicitly:

reviewgate init --host both          # Claude Code + Codex
reviewgate init --host codex         # Codex only
reviewgate init --quick --host both  # scripted recommended preset
reviewgate init --user               # Claude Code hooks for EVERY repo (see below)
reviewgate init --user --remove      # take them back out

Host selection installs or refreshes the selected host definitions; it never silently removes an already-installed other host or any foreign hook.

Host hooks and shims are installed per checkout. Do not copy the generated Codex hook between clones or worktrees; run reviewgate init in each checkout so its fallback root and hash trust match that checkout.

User-scoped hooks (init --user, Claude Code only)

reviewgate init --user installs the Claude Code hooks into ~/.claude/settings.json and shims into ~/.reviewgate/bin/, so the gate exists in repos where nobody ran init. It writes nothing into any repository β€” it does not create .reviewgate/ and it does not arm anything.

What happens where:

  • A repo with its own Reviewgate hooks: the user-scoped shim stands down. Both scopes fire for the same event (Claude Code merges hook entries rather than replacing them), so this avoids two gates contending for the same lock and burning double reviewer quota.
  • An armed repo without repo-local hooks (for example a linked worktree that inherits the main checkout's approval): the gate runs normally.
  • An unarmed repo: nothing happens and nothing is written β€” loudly when the repo ships a reviewgate.config.ts nobody approved there, silently otherwise.

If the binary cannot be resolved, the user-scoped Stop hook allows the turn and warns on stderr, unlike the repo-local hook which fails closed. A globally installed hook that blocked every turn everywhere because of a missing binary would be unusable; the trade-off is deliberate, and reviewgate doctor reports the broken install. Uninstall with reviewgate init --user --remove, which removes only Reviewgate's own entries and leaves foreign hooks and other settings untouched.

Codex project hooks are hash-trusted by Codex itself. After installation, start or restart Codex inside the trusted project, open /hooks, inspect the three Reviewgate responsibilities (SessionStart reset, PostToolUse trigger and Stop gate), then trust their exact current definitions. This is normally a one-time action and repeats only when those definitions change. If the project already defines inline hooks in .codex/config.toml, init preserves them and warns that Codex will merge both sources; it never rewrites TOML hooks.

Important

Installed does not yet mean active in Codex. reviewgate doctor can verify the generated file, shims, timeouts and binary, but Codex does not expose its per-hash trust decision to Reviewgate. That is why Doctor shows a manual warning even when installation is healthy. Reviewgate will not use Codex's dangerous trust-bypass option: allowing the installer or coding agent to approve its own shell commands would defeat the checkpoint. See the dedicated Codex host and hook-trust guide.

Want the why? How this fits "write loops, not code", the failure modes it survives, the security model β†’ Why Β· Failure modes Β· Security.

Full feature list (0.1.0-alpha)

Multi-reviewer panel (Codex Β· Gemini Β· Claude Β· OpenCode Β· OpenRouter Β· Ollama) Β· parallel execution Β· adversarial critic Β· adaptive triage Β· tree-sitter symbol graph Β· research context Β· review cache Β· quota auto-failover Β· per-repo learning brain + curator Β· false-positive ledger Β· stats & weekly reports Β· complete interactive init wizard Β· opt-in reviewer filesystem isolation (macOS Seatbelt / Linux bubblewrap). Remaining caveat: network egress is not isolated. See Scope & limitations.


How it works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Claude Code or Codex (host) ──────────────────────────┐
β”‚  Edit / Write / apply_patch / Bash                                          β”‚
β”‚        β”‚                                                                    β”‚
β”‚        β–Ό  PostToolUse hook                                                  β”‚
β”‚  .reviewgate/bin/trigger  ──►  marks .reviewgate/dirty.flag                 β”‚
β”‚                                                                             β”‚
β”‚  …agent finishes its turn…                                                  β”‚
β”‚        β”‚                                                                    β”‚
β”‚        β–Ό  Stop hook                                                         β”‚
β”‚  .reviewgate/bin/gate  ──►  reviewgate gate --hook stop                     β”‚
β”‚        β”‚                                                                    β”‚
β”‚        β”œβ”€ no changes since last pass ───────────────────────► allow stop   β”‚
β”‚        β”‚                                                                    β”‚
β”‚        β–Ό  run configured panel on diff since the captured review base       β”‚
β”‚  aggregate findings β†’ verdict                                               β”‚
β”‚        β”‚                                                                    β”‚
β”‚        β”œβ”€ PASS / policy-allowed SOFT-PASS ──────────────────► allow stop   β”‚
β”‚        β”œβ”€ FAIL / blocking SOFT-PASS ──► pending.md/json, BLOCK turn         β”‚
β”‚        β”‚           Agent reads pending.md, fixes or rejects each finding,   β”‚
β”‚        β”‚           appends decisions/<iter>.jsonl, stops again β†’ re-review  β”‚
β”‚        └─ max iterations / stuck / cost cap ──► ESCALATION.md, allow stop   β”‚
β”‚                                                                             β”‚
β”‚  reviewgate.config.ts changed?                                               β”‚
β”‚        └─ separate policy fingerprint β†’ review under last-known-good policy β”‚
β”‚           β†’ weakening/non-monotonic change requires human TTY approval      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

* Reviewer filesystem isolation ships via OS sandboxing β€” macOS Seatbelt (sandbox-exec) and Linux bubblewrap (bwrap) β€” enabled with sandbox.mode: "strict" (fails closed if the OS sandbox is unavailable) or "permissive" (runs unisolated with a warning). The default is "off". Network egress is not isolated on either platform. See Security.

πŸ“ For the full control flow, module map and pipeline stages, see docs/architecture.md.


Why: Reviewgate is the verification loop

The current meta in agentic coding is "write loops, not code" (Boris Cherny, Simon Willison, Addy Osmani). The unit of work moved from the keystroke β†’ to the prompt β†’ to the loop: you stop writing lines and start designing the system that prompts the agent and lets it iterate until a goal is met.

Every agentic loop has two halves β€” a generator that produces code and a checker that verifies it and decides when to stop. The loop-engineering crowd is near-unanimous on the part that actually makes a loop trustworthy: split the one who writes from the one who checks, and give the loop a testable termination condition so it can't grade its own homework or run forever.

Reviewgate is that checker, packaged as reusable infrastructure. It is not a code-generating orchestrator (that's the host β€” Claude Code's Workflow, parallel agents, cron). It is the verification loop you drop into such a loop so it can't merge unreviewed work "while you sleep":

Loop-engineering principle Reviewgate
Writer β‰  checker (no self-grading) A heterogeneous reviewer panel (Codex Β· Gemini Β· Claude Β· OpenCode Β· OpenRouter Β· Ollama) β€” independent models inspect the diff
"Are you done?" check each turn A Stop hook blocks on unresolved blocking findings; explicit SOFT-PASS/defer/escalation outcomes are never mislabeled PASS
Testable termination condition decisions/<iter>.jsonl must address every finding id in pending.json before the gate allows the stop
Adversarial verification A demote-only critic + severity-weighted veto + cross-reviewer consensus
Explicit failure exits (no infinite loop) LoopDriver caps iterations and emits ESCALATION.md on max-iter / stuck-signatures / cost-cap / high-reject-rate
Feedback as an observable signal Findings are written to files the agent reads with its normal Read tool β€” no chat-stream scraping

In a full "write loops, not code" setup, Reviewgate is the /goal-style verifier that runs at the end of each turn.

And there is inspectable evidenceβ€”not just a product claim. The current Alpha.12 benchmark v2 publishes raw artifacts for a preregistered 30-case corpus run three times with Codex + Claude Code and an OpenRouter/DeepSeek critic. Both reviewers reached 90/90 coverage under a preregistered 2-attempt retry protocol; the critic reached 86/86 eligible coverage. In that run, the critic improved aggregate precision from 0.3091 to 0.3505 and reduced clean-case false positives from 0.8958 to 0.7292 at unchanged recall (0.8095). Across the three repeats the clean-FP rate ranged 0.625–0.875 (0.729 Β± 0.106), so read the headline as a small-sample central tendency, not a fixed figure. The corpus is still hand-authored and the clean-FP rate is still high; this is alpha evidence, not a leaderboard. Raw artifacts and caveats: docs/evidence.md.


Failure modes it survives

A code-review gate has exactly one job: don't let a real bug ship. The easy part is reviewing a diff β€” any wrapper around an LLM does that in an afternoon. The hard part is never silently failing open: a gate that quietly says "green" when it didn't actually check is worse than no gate at all, because you trust it.

Almost everything below was learned the hard way, in production, dogfooding Reviewgate on its own changes. Each one is a way a naΓ―ve gate fails open β€” and what this one does instead. The fix is the product.

Design rule: when in doubt, fail closed β€” block, over-review, or escalate to a human. Never fail open: pass, hide, or demote. Every guard below is a consequence of that rule.

"No findings" must never mean "PASS." NaΓ―ve: every reviewer is quota-exhausted or times out β†’ the panel returns nothing β†’ "0 findings" β†’ PASS. Reality: your code was never reviewed; the gate just waved it through. Reviewgate: zero successful reviews is an ERROR that blocks, distinct from a real clean pass.

A demote is not harmless. NaΓ―ve: an "uncertain" CRITICAL is quietly downgraded to WARN so the turn can proceed. Reality: under the default policy a WARN-only result soft-passes β€” so a real, possibly-correct CRITICAL vanishes with no decision required. Reviewgate: a finding demoted from CRITICAL is flagged and still requires an explicit decision before the turn can end β€” it can never silently soft-pass.

Reviewers hallucinate β€” including in code they never saw. NaΓ―ve: trust the panel. Reality: a lone reviewer emits a 0.97-confidence CRITICAL citing file:line in a file with fewer lines than that β€” a fabrication that, at panel size 1, hard-FAILs the gate with full authority. Reviewgate: a deterministic, no-LLM fact-check demotes a finding whose cited line provably doesn't exist; diff-scoping makes findings on unchanged code advisory; a demote-only critic and cross-reviewer consensus down-weight the rest.

A blocked turn must not become an infinite loop. NaΓ―ve: "block until every finding is resolved." Reality: the agent writes its decision file β†’ that write re-arms the dirty flag β†’ the gate re-blocks β†’ forever. Reviewgate: the loop is bounded β€” it caps iterations and emits an ESCALATION.md (releasing the turn to a human) on max-iterations, stuck-signatures, cost-cap or a high reject-rate.

Multi-agent shared checkouts break "review the repo's HEAD." NaΓ―ve: review whatever is in the working tree. Reality: in a shared checkout, session A's gate blocks on session B's parallel work β€” code A never wrote. And the obvious fix ("attribute committed work to a session") is itself a fail-open: a file authored via a shell command and then committed is invisible to every attribution signal, so an agent could "disown" its own CRITICAL. Reviewgate: per-session baseline-delta ownership scopes uncommitted work soundly; committed foreign work is never silently demoted β€” it routes to an honest, human-surfaced escalation. (That unsound auto-attribution was caught by an adversarial pre-implementation review before a line of it shipped.)

The bug a green test suite can't see. NaΓ―ve: the schema looks right and the stub tests pass β€” ship it. Reality: one property missing from a strict JSON-schema's required list makes every real provider review return HTTP 400; the stub-based tests never hit the real endpoint, so they stay green. Reviewgate: a structural test replicates the provider's strict-mode rules so the trap can't be reintroduced β€” and provider changes are verified against a real CLI/API call, not just stubs.

How these get found

None of this comes from foresight β€” it comes from process:

  • It dogfoods itself. Reviewgate runs its own gate on every change to Reviewgate; most of the incidents above were surfaced by the tool reviewing its own diff.
  • Adversarial verification, before and after. Plans are reviewed by an independent model panel before implementation (a pre-implementation gate that has killed real fail-opens on paper) and again after β€” reviewers prompted to refute, not rubber-stamp.
  • Real calls, not just mocks. Provider behaviour is verified end-to-end against the actual CLIs/APIs, because stubs have hidden whole classes of bug.

If a guard ever looks paranoid, assume it's load-bearing β€” it's almost certainly a scar from one of the failures above.


Requirements

  • Bun β‰₯ 1.0 (Node 20+ works for the compiled binary)
  • At least one reviewer CLI, installed and logged in:
    • Codex CLI β‰₯ 0.130 (codex login) β€” recommended default
    • Gemini CLI (OAuth)
    • Claude Code (OAuth)
    • OpenCode (OAuth/provider credentials)
    • …or an OpenRouter API key for any hosted model
    • …or an Ollama API key (Ollama Cloud, or point baseUrl at a local ollama serve)
  • macOS or Linux (Windows: use WSL2)
  • git

Using Codex as the authoring host (not merely as a reviewer) requires a Codex release with project lifecycle hooks; the implementation is verified against Codex CLI 0.144.1. Codex must trust the generated project hook hash through /hooks before those hooks execute. See Codex host setup for the exact installed β†’ trusted β†’ active states.

You don't need all of them β€” one is enough to start. Check exactly which reviewers are ready on your machine at any time:

reviewgate doctor

Install

Option A β€” one-liner install (fastest)

Detects your platform, downloads the matching prebuilt binary from Releases, verifies its SHA-256, and symlinks it onto your PATH (into ~/.local/bin). No sudo, no build step, no Bun required:

curl -sSL https://raw.githubusercontent.com/Codevena/reviewgate/master/install.sh | sh
reviewgate --version

Pin a version with REVIEWGATE_VERSION=v0.1.0-alpha.15, or change where it lands with REVIEWGATE_INSTALL_DIR / REVIEWGATE_BIN_DIR. macOS + Linux (arm64/x64).

…or download the tarball manually

Grab the asset for your os-arch from the Releases page and keep the extracted folder intact β€” the binary loads its sibling grammars/ (tree-sitter .wasm) at runtime. Each release ships a SHA256SUMS.txt to verify against:

tar xzf reviewgate-v0.1.0-alpha.15-darwin-arm64.tar.gz
ln -sf "$PWD/reviewgate-v0.1.0-alpha.15-darwin-arm64/reviewgate" /usr/local/bin/reviewgate
reviewgate --version

Option B β€” build from source (contributors / latest master)

git clone https://github.com/Codevena/reviewgate.git
cd reviewgate
bun install
bun run build          # produces ./dist/reviewgate (+ sibling grammars/)

Then, in the repo you want reviewed:

reviewgate init        # complete guided first-run setup:
                       # policy + Claude/Codex hosts + hooks + LKG + doctor,
                       # copies .reviewgate/bin/{trigger,gate,reset},
                       # writes config + approved policy fingerprint

init is idempotent and merges into existing .claude/settings.json and/or .codex/hooks.json without clobbering foreign hooks or other settings. Use reviewgate init --hooks-only --host both to repair/re-bake hooks without changing an existing configuration.

reviewgate setup remains an alias for the same guided project wizard; its --global mode remains config-only.

reviewgate setup       # compatibility alias for the init wizard

Option C β€” npm (npm i -g reviewgate)

npm i -g reviewgate     # installs only your platform's prebuilt binary
reviewgate init         # configure + arm Claude Code/Codex + health-check
reviewgate doctor       # repeat the health-check whenever needed

A global install is recommended for the persistent Stop gate, because reviewgate init bakes the binary's absolute path into the hook. npx reviewgate init works for a quick try, but the binary lives in an ephemeral npx cache that may be garbage-collected β€” init warns when it detects this. Supported: macOS and Linux (glibc) on arm64/x64; on other platforms use Option A or B.


First run β€” guided walkthrough

Zero to your first blocked review in ~5 minutes.

  1. Run reviewgate init. In one guided flow it asks which coding-agent hosts to protect, chooses quick/custom policy setup, configures reviewers/models, critic and memory features, then asks the first-run safety/completion choices: sandbox mode, SOFT-PASS policy, clean-pass acknowledgement, desktop notifications and the warn-only pre-push reminder. It then installs the native hooks, records the validated initial LKG and runs doctor.

  2. Get one reviewer working. You need exactly one to start:

    • Lowest friction (no CLI): set OPENROUTER_API_KEY and add an openrouter reviewer to phases.review.reviewers (paid per call).
    • $0 within your subscription: install + log in to one OAuth CLI β€” codex login, Claude Code, or the Gemini CLI β€” they're already in the starter config.
    • Ollama Cloud (also no CLI, $0 within your Ollama subscription quota): set OLLAMA_API_KEY and add an ollama reviewer β€” see Choosing the Ollama model below.

    Then confirm what's actually ready: reviewgate doctor tells you exactly which reviewers it can reach and what to fix.

    During custom OpenRouter setup, Reviewgate discloses and offers one bounded paid capability check per distinct paid request tuple (purpose, model, auth, route and probe bounds), enabled by default. Reviewer and fallback choices use the same strict structured review() request as production; critic/curator choices use their real free-form completion shape. The check is capped at 15 seconds and 256 output tokens and sends a constant repository-free prompt. Success confirms that exact request completed and parsed at setup time; it is not a permanent guarantee about a third-party route.

  3. If Codex is selected, activate the installed project hooks. Start or restart Codex in the trusted repository, run /hooks, inspect the exact .codex/hooks.json commands and trust their current hash. Codex skips new or changed hooks until this happens. Reviewgate cannot perform or verify that user-owned action and never bypasses it. Full explanation: Codex host setup and hook trust.

  4. See it work (60-second smoke test) β€” before you trust it in a loop. Make a deliberately broken change and run the gate by hand:

    echo 'export const refund = (amt, by) => amt / by;  // div-by-zero, no guard' >> smoke.ts
    git add smoke.ts
    reviewgate gate            # reviews the current Reviewgate change scope
    cat .reviewgate/pending.md # the findings the panel raised
    git rm -f smoke.ts
  5. Use Claude Code or Codex as normal. After it edits files and tries to finish a turn, the Stop hook runs the review. If a reviewer raises a blocking finding, the agent is told to read .reviewgate/pending.md and address each one. The current turn stays blocked while those findings remain unresolved; bounded non-convergence instead produces an explicit human escalation rather than an endless loop.

  6. You review the final diff and commit manually. Reviewgate never commits or edits code itself; it only reports.

Useful commands outside the loop:

reviewgate doctor                    # which reviewers are ready + what to fix
reviewgate init                      # complete interactive first-run/reconfigure flow
reviewgate init --hooks-only --host both # repair hooks, preserve config
reviewgate setup                     # compatibility alias (`--global` is config-only)
reviewgate config status             # approved/pending policy fingerprints
reviewgate config approve            # TTY-only human approval after an LKG pass
reviewgate gate                      # review the current change scope on demand
reviewgate reset                     # re-arm the gate (clear this session's review state)
reviewgate audit verify --file <jsonl>   # verify an audit-log hash chain

Configuration β€” reviewgate.config.ts

reviewgate.config.ts is a data-only default-export object. Reviewgate parses objects, arrays, strings, finite numbers, booleans, null and comments; it does not execute the file. Imports, function calls, spreads, template expressions and environment lookups are rejected. A present invalid config blocks instead of falling back to a weaker default policy.

The effective config has a separate control-plane fingerprint. A changed candidate is first evaluated while code review continues under the last-known-good policy. Provable monotonic strengthenings are adopted only after that pass. Any weakening or non-monotonic change needs reviewgate config approve from an interactive TTY; there is deliberately no --yes bypass. Details are written to .reviewgate/POLICY_CHANGE.md, never injected into the normal reviewer diff.

Minimal single-reviewer setup (Codex only, OAuth, $0):

export default {
  providers: {
    codex: { enabled: true, auth: "oauth", model: "gpt-5.5", timeoutMs: 300_000 },
  },
  loop: {
    maxIterations: 3,        // escalate to the human after N failed review rounds
    costCapUsd: 1.5,         // only enforced in apikey/openrouter mode (OAuth = $0)
    softPassPolicy: "allow", // allow | block | ask-once for WARN-only verdicts
  },
  sandbox: {
    mode: "off",
  },
};

Multi-reviewer panel with an OpenRouter critic:

export default {
  providers: {
    codex: { enabled: true, auth: "oauth", model: "gpt-5.5", timeoutMs: 300_000 },
    gemini: { enabled: true, auth: "oauth", model: "gemini-3.5-flash", timeoutMs: 300_000 },
    "claude-code": { enabled: true, auth: "oauth", model: "claude-sonnet-4-6", timeoutMs: 300_000 },
    openrouter: {
      enabled: true,
      auth: "openrouter",
      model: "deepseek/deepseek-v4-pro",   // ← any OpenRouter model slug (see below)
      apiKeyEnv: "OPENROUTER_API_KEY",
      costPerMTokensUsd: 0.075,            // optional; fed into loop costCapUsd tracking
      timeoutMs: 120_000,
    },
  },
  phases: {
    review: {
      reviewers: [
        { provider: "codex",       persona: "security" },
        { provider: "gemini",      persona: "security" },
        { provider: "claude-code", persona: "adversarial" },
        { provider: "openrouter",  persona: "security" },
      ],
    },
    critic: { provider: "openrouter", persona: "critic" },
  },
  loop: {
    maxIterations: 3,
    costCapUsd: 2.0,
    softPassPolicy: "allow",
  },
};

Codex with an Ollama Cloud fallback (also $0 within your Ollama subscription quota β€” no CLI, just an API key):

export default {
  providers: {
    codex: { enabled: true, auth: "oauth", model: "gpt-5.5", timeoutMs: 300_000 },
    ollama: {
      enabled: true,
      auth: "apikey",
      apiKeyEnv: "OLLAMA_API_KEY",
      model: "glm-5.2:cloud",              // ← any model ollama.com serves
      baseUrl: "https://ollama.com/v1",    // self-hosted: "http://localhost:11434/v1"
      timeoutMs: 300_000,
    },
  },
  phases: {
    review: {
      reviewers: [{ provider: "codex", persona: "security", fallback: ["ollama"] }],
    },
  },
};

Anything you omit falls back to the defaults. The config is zod-validated.

Deterministic checker tier (phases.checks)

Before the LLM panel runs, Reviewgate can execute a set of deterministic commands (typecheck, build, test suite, linter) that are fast, free, and perfectly reproducible. If any command exits non-zero the gate fails closed immediately β€” without spending a single token on reviewer inference β€” and surfaces the exact output as a CRITICAL finding. Commands run in order, fail-fast (the first failure stops the rest).

// reviewgate.config.ts β€” run tsc + tests before the LLM panel; a failure blocks
// the turn (with the output) and skips the panel. Order cheap β†’ expensive.
phases: {
  checks: {
    commands: [
      { name: "typecheck", run: "bun run typecheck", timeoutMs: 120_000 },
      { name: "test",      run: "bun test",          timeoutMs: 300_000 },
    ],
  },
}

Key properties:

  • Commands run unsandboxed β€” they are your own trusted config (same trust level as reviewgate.config.ts itself), not untrusted reviewer subprocesses.
  • A check failure is not rejectable by the agent; it must be fixed (or the check removed from the config) before the panel runs. This prevents the agent from shipping code that doesn't compile or breaks tests.
  • Each command has an optional timeoutMs (default 300 s). A timeout is also treated as a failure (fail-closed, never a silent skip).
  • Combine with sandbox.mode to isolate only the LLM reviewers β€” the checks tier always runs unsandboxed regardless of the sandbox setting.

Project knowledge β€” "Lore" (phases.lore)

Reviewers keep re-deriving the same project facts β€” invariants, past decisions, gotchas β€” and sometimes get them wrong (a hallucinated "bug" that's actually a deliberate design choice). Lore lets you write those facts down once, as committed Markdown, and have the gate inject the relevant ones into each review as trusted context. One maintainer-authored note can end a whole class of repeated false positives.

Turn it on in the custom reviewgate init flow (it asks and explains β€” off by default), or by hand:

phases: {
  lore: { enabled: true },
}

Then write one file per fact under .reviewgate/lore/<slug>.md:

---
schema: reviewgate.lore.v1
id: payment-webhook-invariants        # must equal the file name
status: draft                          # draft | canon β€” only canon is injected
anchors:                               # which files this fact is about (globs ok)
  - "src/lib/stripe-webhook-handlers.ts"
  - "src/app/api/webhooks/**"
verified_at: 2026-07-10
verified_tree: "…"                     # hash of the anchored files at verify time
---
Every subscription write is a compare-and-set on (status, lastStripeEventAt).
Why: Stripe delivers webhooks out of order, so a naive "last write wins" corrupts state.
Write the WHY β€” never restate what the code already says.

How it behaves (all fail-safe β€” a broken lore file never blocks a review):

  • Draft β†’ canon, with approval. New notes start as status: draft (never injected). A maintainer promotes one to status: canon β€” and that alone is not enough: the promotion only takes effect once a line lands in the committed .reviewgate/lore/approvals.jsonl, and only an approved canon note is ever injected. This keeps a compromised or careless commit from silently feeding the reviewer instructions. Two ways to record that approval: reviewgate lore approve <id> does it directly β€” it prints the note's full body, asks you to type back a challenge bound to the entry's exact bytes, and writes the line. It is TTY-only with no --yes, so no agent can run it, and it refuses a draft (approval is permanent for that id, so a draft must never be pre-authorized). Or leave it to the gate: on the next run it raises a one-time, verdict-neutral "did a human approve this promotion?" finding, and the agent's fixed decision writes the same line once you say yes.
  • Relevant-only injection. A note is injected only when its anchors overlap the files in the current diff β€” so reviews stay focused and cheap.
  • Freshness, enforced. When the anchored files change, the note goes stale (a content hash no longer matches). The gate then raises a verdict-neutral, once-per-day reminder to update or re-confirm it β€” so your knowledge base can't quietly rot. Rejecting a reminder (with a reason) snoozes it.
  • Never changes a verdict. Both lore findings are advisory INFO β€” a PASS stays a PASS. They just cost one turn via the decision requirement.
  • reviewgate lore status lists every note with its status and freshness; reviewgate lore verify <slug> (or --all) recomputes verified_tree/ verified_at for the named entries and writes them back, so you never have to hand-compute the hash; reviewgate lore approve <id> records the canon approval above (interactive, human-only); reviewgate doctor flags broken, un-anchored, or too-broad notes.

Completion signal

A passing review used to be silent (the Stop hook just exits 0). Now the gate always writes a one-line summary to stderr on completion β€” e.g. 🟒 Reviewgate Β· GATE OPEN β€” PASS (iteration 1) or πŸ”΄ Reviewgate Β· GATE CLOSED β€” … β€” so "green" is distinguishable from "the gate didn't run". Set notify.desktop: true to also fire a macOS/Linux desktop notification when a review finishes:

export default {
  // ...providers, phases...
  notify: { desktop: true },   // osascript (macOS) / notify-send (Linux)
};

Note: by hook architecture, an AI agent can only be interrupted on a blocking (FAIL) verdict β€” on PASS its turn simply ends. The stderr line and desktop notification are the human-facing signal; an agent confirms a pass by reading .reviewgate/state.json / pending.md.

If you want the agent to be told about a pass too, set loop: { acknowledgePass: true }. Then a passing review blocks ONCE with a βœ… Reviewgate PASS … message so the agent can confirm the result to you, and ends cleanly on the next stop (one extra turn per pass; default off).

Choosing the OpenRouter model

The OpenRouter reviewer can target any model OpenRouter hosts β€” just set the model field to its slug. Examples that work today:

deepseek/deepseek-v4-pro        deepseek/deepseek-v4-flash:free
google/gemini-2.0-flash-001     openai/gpt-4o-mini
anthropic/claude-sonnet-4.5     meta-llama/llama-3.3-70b-instruct

Browse and copy exact slugs from https://openrouter.ai/models. An invalid slug returns a 404 (ModelNotFoundError) and the reviewer reports status: error (fail-closed β€” never a silent pass). Set your key once in the shell:

export OPENROUTER_API_KEY=sk-or-...   # e.g. in ~/.zshrc

Reviewgate sends a strict JSON schema via OpenRouter's response_format; models that ignore it are still recovered by the tolerant parser.

Choosing the Ollama model

The ollama reviewer is an OpenAI-compat HTTP adapter (no CLI, no subprocess β€” same shape as openrouter), pointed at Ollama Cloud by default:

export OLLAMA_API_KEY=...   # ollama.com β†’ Account β†’ API Keys, e.g. in ~/.zshrc
ollama: {
  enabled: true,
  auth: "apikey",
  apiKeyEnv: "OLLAMA_API_KEY",
  model: "glm-5.2:cloud",             // any model ollama.com serves; verified with glm-5.2:cloud
  baseUrl: "https://ollama.com/v1",
  timeoutMs: 300_000,
},

Self-hosted instead of the cloud: point baseUrl at a local ollama serve daemon:

ollama: { enabled: true, auth: "apikey", apiKeyEnv: "OLLAMA_API_KEY", model: "glm-5.2:cloud", baseUrl: "http://localhost:11434/v1", timeoutMs: 300_000 },

Run ollama serve (and ollama signin first if you want to pull Ollama's :cloud models through your local daemon). Availability is key-based, not URL-based β€” reviewgate doctor and reviewer selection check for a non-empty OLLAMA_API_KEY env var regardless of baseUrl, so even a pure-localhost setup is treated as unavailable with no key at all. Set OLLAMA_API_KEY to any non-empty placeholder in that case: a local daemon ignores a bogus Bearer token, but the loopback request itself doesn't require a real key.

Note: unlike OpenRouter, Ollama's /v1 endpoint accepts a response_format JSON schema in the request but does not enforce it server-side β€” schema conformance is prompt-driven, the same way it is for the claude and gemini adapters. The tolerant parser (plus a reasoning-block stripper for <think> output) recovers the JSON regardless; verified working end-to-end with glm-5.2:cloud.


Verdicts

Verdict Meaning Effect
PASS No findings, or INFO only allow stop
SOFT-PASS Only WARN findings, singleton/minority, no CRITICAL allow stop (default policy)
FAIL A CRITICAL (security/correctness), or majority WARN block until addressed
ESCALATE Max iterations, stuck findings, or cost cap hit writes ESCALATION.md, allow stop
ERROR Reviewer could not run (crash/timeout/sandbox) block (fail closed), eventually escalates

Reviewgate fails closed: a reviewer that crashes or times out is never treated as a pass.


Adaptive pipeline

Four stages run before the reviewer panel, making the gate faster and more precise without changing any external protocols:

Triage

Before spawning any reviewer, Reviewgate classifies the diff:

  • Doc-only diffs (changes confined to Markdown, comments, or other non-executable files) are skipped at $0 β€” they get an automatic PASS verdict without touching the reviewer panel.
  • Sensitive-path diffs (auth, crypto, payment, admin) receive an expanded review budget (more iterations, higher cost cap).

Research context

For every non-trivial diff Reviewgate builds a research.md context file and injects it into each reviewer's prompt. The context includes:

  • A summary of which files changed and why.
  • Symbol graph callers/callees (see below).
  • Any relevant entries from the per-repo learning brain (when enabled).

Every reviewer reads this context, so findings reference stable symbol names rather than raw line numbers.

Tree-sitter symbol graph

Reviewgate uses web-tree-sitter + grammar WASM files to extract the call graph around the changed symbols. Supported languages: TypeScript, TSX, JavaScript, JSX, Python.

The symbol graph needs ripgrep (rg) to find callers efficiently. If rg is absent, the symbol graph degrades gracefully (callers list is empty; reviews still run). If no grammar WASM can be found the symbol graph is disabled entirely but reviews are unaffected.

Grammar WASM files are bundled into dist/grammars/ by bun run build so the compiled binary works without node_modules. Run reviewgate doctor to confirm both rg and the grammars are available.

Review cache

When the diff is byte-for-byte identical to a previous run (same content hash), Reviewgate returns the cached verdict without spawning any reviewer. This makes repeated stop-hooks instantaneous after a trivially clean re-run.


Security

  • Author session β‰  reviewer process. The host Claude Code or Codex session never reviews inline; reviewers are fresh isolated subprocesses. When Claude is the authoring host, any Claude reviewer is additionally downgraded to a smaller tier. A Codex host does not incorrectly trigger that Claude-only rule.
  • Diff sanitisation. Diffs are run through a 6-layer pipeline (Unicode NFKC normalise β†’ injection-marker neutralise β†’ fenced wrap β†’ high-entropy secret redaction β†’ persona reaffirmation) before reaching the reviewer, to blunt prompt-injection planted in code.
  • Tamper-evident audit log. Every run appends a sha256 hash-chained JSONL event log; reviewgate audit verify detects any modification.
  • Sandbox (denylist filesystem model; opt-in). With sandbox.mode: "strict" or "permissive", macOS Seatbelt denies writes except the exact findings/run-temp/ own-credential targets; Linux bubblewrap exposes / read-only and binds only those targets writable. Known secret paths are denied/masked, but this is not a read allowlist: other host files may remain readable. "strict" fails closed if the OS sandbox is unavailable; default is "off". Network egress is not isolated, and Linux cannot enforce glob denies such as *.pem or .env*.
  • Provider risk is not uniform. Codex is invoked read-only and Claude's reviewer tools are restricted. Gemini/agy and OpenCode are coding-agent CLIs invoked with their non-interactive permission-bypass flag and may explore/run tools; use sandbox.mode: "strict". OpenRouter/Ollama are HTTP adapters: they do not run local tools, but the prompt/diff is sent over the network.
  • Config control plane. Config is data-parsed, invalid candidates block, and policy changes are reviewed under a last-known-good snapshot before adoption.

See SECURITY.md for the full threat model and how to report a vulnerability.


What gets written to .reviewgate/

Path Committed? Purpose
bin/{trigger,gate,reset} yes tiny hook shims that call the binary
personas/security.md yes the reviewer's persona prompt
pending.md / pending.json no current iteration's findings (human + machine)
decisions/<iter>.jsonl no Coding agent's accept/reject ledger
state.json no loop FSM state
control-plane.json no approved policy snapshot + pending candidate
POLICY_CHANGE.md no human-readable policy checkpoint
audit/… no hash-chained event log
ESCALATION.md no written when a run escalates to the human

For AI agents

If you are an AI coding agent operating in a Reviewgate-enabled repo, read docs/AGENTS.md β€” it specifies exactly how to respond when Reviewgate blocks your turn (read pending.md, fix or reject each finding, write decisions/<iter>.jsonl).


Scope & limitations

Reviewing

  • Multi-reviewer panel β€” Codex + Gemini + Claude + OpenRouter (any model by slug), run in parallel, with an adversarial critic phase and confirmed_by cross-reviewer consensus tracking.
  • Severity-weighted veto verdict Β· pending.md/pending.json + decisions protocol Β· single loop with escalation (max-iterations / stuck-signatures / cost-cap).
  • Quota auto-failover β€” if a reviewer hits its usage cap, the gate fails over to a configured fallback provider, remembers when the limit resets, and resumes the primary automatically (no config edit).

Speed & precision

  • Adaptive triage β€” doc-only diffs skip review at $0; sensitive paths (auth, crypto, payment) get an expanded budget.
  • research.md context injected into every reviewer prompt β€” a tree-sitter symbol graph (TS/JS/TSX/Python; ripgrep used for callers if present, degrades gracefully) and, when enabled, Context7 library docs.
  • Review cache β€” an identical diff returns a cached verdict with no reviewer spawn.

Learning

  • Per-repo learning brain & Curator (see Brain & Curator) β€” committed memory; reviewgate brain list|show|revoke.
  • False-positive ledger β€” FPs you reject are demoted on future runs; reviewgate fp list|show|pin|unpin|audit.

Tooling

  • Commands: init Β· gate Β· doctor Β· config status|approve Β· audit verify Β· stats Β· report Β· review-plan <file> Β· setup Β· brain Β· lore Β· fp Β· learn status Β· bench.
  • Cost model: subscription/OAuth paths for Codex, Gemini, Claude and OpenCode; OpenRouter/Ollama use the configured HTTP endpoint/key and are tracked against costCapUsd where pricing is configured.
  • Hash-chained audit log Β· cassette record/replay for deterministic provider testing.

Caveats: reviewer filesystem isolation ships (macOS Seatbelt / Linux bubblewrap) but is opt-in (sandbox.mode, default off); network egress is not isolated on either platform, and Linux does not enforce glob secret-denies. See Security.


Brain & Curator

The brain is a committed per-repo memory (reviewgate.brain.json, brain.md, sources.jsonl, archive.md under .reviewgate/brain/). Every reviewer reads the brain entries most relevant to the current diff and may propose new facts. The Curator is a non-blocking background validator that applies 7 acceptance rules (uniqueness, source authority, cross-provider quorum, embedding dedup against existing entries, etc.) before anything enters the brain; proposals that fail are discarded or archived, never silently committed.

Committed vs. gitignored:

Path Committed?
.reviewgate/brain/brain.json yes
.reviewgate/brain/brain.md yes
.reviewgate/brain/sources.jsonl yes
.reviewgate/brain/archive.md yes
.reviewgate/brain/proposals/ no (gitignored)
.reviewgate/brain/snapshots/ no (gitignored)

Enable in reviewgate.config.ts:

export default {
  // ...providers, phases...
  phases: {
    brain: {
      enabled: true,
      embeddings: {
        model: "baai/bge-base-en-v1.5",   // default; any sentence-transformers slug works
      },
      // optional: which provider runs Curator validation
      curator: { provider: "codex" },
      // optional: domains the brain may fetch sources from
      egressAllowlist: ["github.com", "docs.example.com"],
    },
  },
};

CLI:

reviewgate brain list          # list all committed brain entries
reviewgate brain show --id <id>     # show a single entry with metadata
reviewgate brain revoke --id <id>   # archive + revoke an entry immediately

Development

bun test            # unit + integration (fake Codex stub)
bun run typecheck   # tsc --noEmit
bun run lint        # biome
bun run build       # compile single binary

REVIEWGATE_E2E=1 bun test tests/e2e   # real Codex end-to-end (uses your quota)

The design spec lives in docs/superpowers/specs/, the implementation plans in docs/superpowers/plans/, and spike findings in docs/superpowers/spikes/.

See CONTRIBUTING.md before opening a PR.


Contributing

Bug reports, feedback, and small focused PRs are welcome β€” see CONTRIBUTING.md. For security issues, follow SECURITY.md (do not file them publicly).

License

MIT Β© Markus Wiesecke

About

Fail-closed independent review loop for Claude Code and Codex. Native hooks, six reviewer paths (Codex, Gemini, Claude, OpenCode, OpenRouter, Ollama), LKG policy control plane and local audit trail.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages