Open-source agentic code review

A merge gate built from small reviewers you write and own.

A single model reviewing the whole diff loses recall on individual concerns as its checklist grows. Bastion splits review into many narrow reviewers, each checking exactly one concern. They run in parallel and aggregate into one verdict; the gate blocks whenever a gate reviewer can't return a clean pass, while advisors comment without blocking. You author them, version them in your repo, and run them on your own infrastructure.

  • Single-concern reviewers
  • Fails closed
  • Local loop + CI
  • Open source, self-hostable
.bastion.yamlyaml
# Each reviewer owns exactly one concern.
reviewers:
  - name: single-responsibility
    trigger: [src/**/*.rs]
    mode: gate
    prompt: |
      Block if any one file has taken on multiple
      unrelated concerns; otherwise approve it.

  - name: parse-dont-validate
    trigger: [src/**/*.rs]
    mode: advisor
    prompt: |
      Flag boundaries that pass raw strings where a
      parsed type would remove a class of bugs.
bastion review --base main
  • single-responsibilitypass
  • error-handlingblock
  • parse-dont-validateadvisor
aggregateblock

Recall falls as one reviewer takes on more concerns.

One model reviews the whole diff and leaves comments for a person to act on. Its recall on individual concerns drops as the checklist grows.

one generic reviewerrecall collapses
  • 1 concernworks
  • 10 concernsslips
  • 100 concernsuseless

One checklist stretched across everything; each concern gets a fraction of the model's attention.

many single-concern reviewersrecall holds
  • single-responsibilityholds
  • error-handlingholds
  • + tenant-isolationholds

You cover more ground by adding narrow reviewers, not by broadening one. Each stays at high recall.

One concern per reviewer.

A reviewer is a focused fitness function: a prompt, a trigger, a mode. A cross-cutting property like tenant isolation or migration safety is just another reviewer whose single concern is that property.

reviewerconcerntriggermode
single-responsibilityNo one file concentrates multiple unrelated concerns.src/**/*.rsgate
error-handlingNo recoverable error can panic in production code.src/**/*.rsgate
api-compatibilityPublic API changes stay backward-compatible.src/api/**gate
release-surface-robustnessInstallers verify checksums and fail closed on any mismatch.scripts/**gate
parse-dont-validateBoundary data is parsed once into a precise type.src/**/*.rsadvisor
test-coverageNew or changed behavior is covered by tests.src/**/*.rsadvisor
docs-in-syncUser-visible changes update the matching docs.docs/**/*.mdadvisor

Gates block. Advisors comment.

A gate holds the merge until it passes. Anadvisor reports findings without blocking. Same reviewer shape, different authority, your call per concern.

Declarative and static

Reviewers live in checked-in YAML, not generated by code. That keeps them reviewable and the trigger set stable, so a change to review policy is a diff you can read.

Composable and parallel

They run independently and aggregate at the end. Add a reviewer for a new concern without touching the others; a slow one never blocks a fast one.

How the gate aggregates

Reviewers run in parallel with per-reviewer timeouts; their verdicts aggregate into one decision. A gate that crashes, times out, or can't produce a valid verdict resolves to block, never a silent pass. Advisors are best-effort: when one fails, it's ignored.

reviewerwhat happenedcounts as
single-responsibilityreturned passpass
error-handlingreturned blockblock
e2e-checkout-flowtimed outblock
migration-safetycrashedblock
no-verdictmalformed outputblock
test-coverageadvisorerroredskipped
Every gate returned pass?No. Four gates resolved to block.
merge gateblock

You govern the policy.

The human moves from reviewing every diff to authoring and governing the reviewers that do. Your interface becomes the registry and the escape feed.

.github/CODEOWNERShuman review required
# Changing the review policy is itself reviewed.
.bastion.yaml   @your-org/platform

Because reviewers are declarative and static, any PR that weakens the gate, loosens a prompt, drops a trigger, or downgrades a gate to an advisor shows up as a diff a human must approve. The policy can't quietly erode.

  • A reviewer encodes a check you'd otherwise run by hand.
  • Even an earnest agent can game a check; a reviewed policy diff makes that visible.
  1. 01

    An escape slips through

    A change merges that a reviewer should have caught. Inevitable, especially early. It's also your most valuable signal.

  2. 02

    Triage which reviewer missed

    Find the concern that wasn't covered, or the reviewer whose prompt was too loose to catch it.

  3. 03

    Adjust the policy

    Tighten a prompt, add a narrow reviewer, or harden a trigger. It's a diff against .bastion.yaml, reviewed like any other.

  4. 04

    The gate gets sharper

    The next changeset meets a better policy. The gate improves as you feed escapes back into it.

The same reviewers, locally and in CI.

A reviewer is a fitness function the author can optimize against before anyone opens a PR. The same reviewers run in your local loop (fast, pre-PR) and in CI (authoritative). CI runs the same gate, so its result matches what the author already saw.

local

The author loop

An authoring agent runs bastion review against the working tree, exactly as CI would, and loops until green.

# fix what blocked, then run again
$ while ! bastion review --base main; do
    agent fixes the blocking findings
$ done
all gates pass · ready to open a PR
ci

The confirmation

On the PR, the same gate runs as the authoritative check. Because the author already drove it green, CI mostly just agrees.

# uses: jssblck/bastion@v0
 bastion / gate
  single-responsibility pass
  error-handling        pass
  release-surface       pass
merge gate: pass

Bastion doesn't own your CI. It runs anywhere code can run, and stays portable across the local and GitHub surfaces by keeping them deliberate mirror images.

Open source and self-hostable.

Read every reviewer, run them on your own infrastructure through the model provider and billing you already use, and own the gate outright. The reviewers, the verdicts, and the aggregation are all in the open.

Open source

AGPL-3.0. Every reviewer, every verdict, the whole gate. Nothing about how your code is judged is hidden behind a service.

Self-hostable

Runs anywhere code can run, local or any CI. It drives the agent backends you already use (Claude Code, Codex, Pi) and reuses your own auth and billing.

No lock-in

Your policy is one declarative file in your repo. Take it with you. It's plain YAML you can read, diff, and version, with no proprietary format in the way.

Early software, fails closed.

Bastion is early and moving fast. The runner, the gate, and the Claude Code, Codex, and Pi backends run reviewers for real. Anything not yet implemented returns block instead of pass, so an unfinished capability can't wave code through by claiming a review it never ran.

From nothing to your first gate in five minutes.

The installer detects your platform, downloads the matching release, verifies its SHA-256 checksum, and puts bastionon your PATH. It fails closed on any checksum problem.

macOS · Linux
curl -sSfL https://raw.githubusercontent.com/jssblck/bastion/main/scripts/install.sh | bash
Windows
irm https://raw.githubusercontent.com/jssblck/bastion/main/scripts/install.ps1 | iex

Install the agent skill so your coding agents know how to drive Bastion (commit what it writes), then loop a reviewer until the gate is green:

$ bastion skills install
$ bastion review --base main