v1.0.0  ·  Source-available  ·  Elastic License 2.0

Your team ships
faster than it
can review.

AI-assisted development moved the bottleneck to the pull request queue. CodeCapitan tells you which PRs are safe to approve unattended, and which ones genuinely need a human — as an auditable, rule-by-rule verdict you can still defend a week later.

  • exit 0 GREEN
  • exit 1 AMBER
  • exit 2 RED

The bottleneck moved

Nobody is reviewing forty PRs a day.

Assistants made writing code cheap. Reviewing it costs exactly what it always did. Every team that has hit that wall ends up in one of three places — and all three of them are a risk decision made by default rather than on purpose.

The queue wins

PRs sit for days. Branches drift, conflicts pile up, and the review that finally happens is a scroll to the bottom of a diff nobody has the context for any more.

The rubber stamp

Approvals keep pace by getting shallower. At volume, an LGTM is indistinguishable from no review at all — right up until the thing it waved through lands in production.

Reviews off

Required reviews get switched off "temporarily" so the team can move. Velocity goes up, and the audit trail that told you why anything was approved goes away with it.

The fourth option

Decide with evidence, not with vibes.

Point CodeCapitan at a pull request and it tells you — with reasons, file and line — whether that PR is safe to approve unattended, or risky enough that a human should really look at it.

It isn't a rubber stamp and it isn't a blocker. It's a risk assessment you can read, disagree with, re-run, and hand to somebody who wasn't there.

The verdict

Safe to approve, or send it to a human?

That is the only question CodeCapitan answers. Green means safe to approve unattended, amber means a human should look, red and critical mean decline. Four severities feed that call, in precedence order — critical ≻ red ≻ amber ≻ green. Red and critical are never overridable. Amber is weighted and only tips the verdict once the failing weights cross the threshold. Green never rescues a bad PR — a borderline change can't score its way clear by collecting cheap passes.

Critical

A categorical failure that makes evaluating the rest moot — a red pipeline, or a PR that disables a CI gate.

The PR is dead on arrival: the verdict is critical and the paid agent pass is skipped entirely. The deterministic tier still runs and reports in full.

Red

A hard security, integrity or hygiene failure. Secrets in the diff, CODEOWNERS tampering, an unticked checklist, an unreviewable diff.

Any failing red rule makes the verdict red, whatever the amber score says. Decline, and label the PR for a human.

Amber

A quality concern: poor abstraction, weak error handling, loose types, cryptic naming, authentication used badly.

Weighted. When the failing weights cross amber_threshold, the verdict is amber — escalate to a human reviewer.

Green

A positive signal: tests added, docs updated, types tightened.

Failing one is never penalising, and passing one adds nothing to the verdict maths. Logged as a signal, nothing more.

The scan, pitch by pitch

Six steps up the wall.

A scan is a pipeline, and it behaves like one: fixed order, every step reports, results streaming back as they land. Cheap deterministic work runs first; the paid agent layer only runs if the PR is still alive after it.

El Capitan drawn as a climbing topo, with the six steps of a scan marked as pitches along a dotted route up the face. 1 2 3 4 5 6
  1. Pitch 1

    Plan-doc classification

    Before anything reads the diff, CodeCapitan works out whether the PR carries a plan document — nominate by path, then confirm by content.

    plan_doc_classifier · path-nominate · content-confirm

  2. Pitch 2

    Deterministic scans

    Secrets, denylisted paths, tampered CODEOWNERS, trojan source, size gates, CI state, review threads — plus language packs for PHP, JS, Python, Go, Terraform, Docker and Lambda. Pure local Python.

    40+ rules · every PR · $0.00

  3. Pitch 3

    Agent checks

    Fourteen groups, each multiplexed into a single model call behind a cheap gate that decides whether the group is worth running. Security surfaces force-run their own; review-trust is never gated.

    14 groups · 73 lenses · 291 sub-checks

  4. Pitch 4

    Adjudication

    A council argues every finding: a suppress-advocate for noise, a keep-advocate for signal, a judge to rule. False positives are treated as a defect, not a cost of doing business.

    suppress-advocate · keep-advocate · judge

  5. Pitch 5

    Risk dimensions

    What survives is read again along four axes, so a PR that trips nothing hard can still come back risky.

    confidentiality · integrity · availability · privacy

  6. Pitch 6

    Risk rationalization

    One last pass re-rates the four against each other and sets a single whole-PR risk band — the emergent read, not the sum of the parts.

    risk_rationalizer · overall band

The one short-circuit. If a critical rule fails, the agent pool and the risk stage are skipped and reported as N/A. Everything else always runs to completion — a test in the suite exists purely to stop anyone reintroducing an early exit.

Steps
6
Agent groups
14
Lenses
73
Sub-checks
291
Risk axes
4
Deterministic cost
$0

The catalogue

What it actually looks for.

Every lens declares its own sub-checks, and the report tells you which one tripped — not just which lens. Here's a slice of the catalogue.

security_review_trust · never gated

The PR that tries to talk its way past the reviewer.

Most review tools assume the diff is arguing in good faith. This group assumes it isn't. It's the one group that never gets gated away, because the PRs worth catching are exactly the ones that look cheap to skip.

  • reviewer_directed_injectionInstructions aimed at an AI reviewer, forged sign-offs, text that redefines the review rules
  • review_evasionEncoded payloads decoded and executed, dynamic exec of an assembled string, a minified blob passed off as hand-written source
  • egress_introducedNew HTTP, socket or DNS traffic to an external host — especially carrying secrets or user data
  • conditional_triggerDestructive or auth-related paths gated on a hardcoded date, tenant, hostname or magic value
  • description_diff_divergenceA security-relevant change the PR description doesn't mention, or a high-impact edit buried in a large diff
  • guard_tamperingA security test deleted beside the change it guarded, an assertion loosened, a snapshot rewritten to bless the new output

Injection & input

agent.injection_risk · agent.ssrf · agent.path_traversal · agent.xss_output_encoding · agent.xxe · agent.redos

SQL built by concatenation, shell and eval with interpolated input, server-side requests to an attacker-controlled host, zip-slip, unescaped template output, regexes that backtrack catastrophically.

Authorization

agent.auth_safety · agent.broken_object_authz · agent.security_posture_delta · agent.mass_assignment · agent.cors_misconfig

An authz check quietly removed, a route that went from authenticated to public, an object resolved straight from a client-supplied id with no tenant scope, credentialed CORS reflecting any origin.

Secrets & crypto

agent.secret_in_code · agent.crypto_misuse · agent.transport_security_downgrade · agent.sensitive_data_in_logs

Hardcoded keys and connection strings, MD5 and ECB, predictable RNG for tokens, certificate verification switched off, auth headers and card numbers landing in logs.

Correctness

agent.correctness_defect · agent.concurrency_risk · agent.input_validation_at_boundary

Inverted conditions, off-by-ones, swapped arguments, swallowed exceptions, a lock held across a blocking call, a handle that isn't released on every path.

Change safety

agent.rollout_safety · agent.migration_safety · agent.terraform.risky_change

Schema and code shipped in one deploy, a non-nullable column with no backfill, a dropped endpoint, a Terraform change that forces replacement or broadens an IAM trust policy.

Maintainability

agent.abstraction_appropriate · agent.naming_clarity · agent.type_rigor · agent.incomplete_change · agent.test_quality · agent.comment_quality

Wrappers around a single caller, any-type escape hatches, renames left half-done, tests that assert nothing, and comments that narrate the edit instead of the intent.

Supply chain

agent.suspicious_dependency · agent.supply_chain.license_risk · composer.* · docker.*

Dependencies from non-canonical sources, install hooks, typo-squats, loosened pins, lockfile integrity mismatches, copyleft arriving without an SPDX id.

Language packs

PHP · JavaScript · Python · Go · Terraform · Docker · Lambda · NestJS · GraphQL

Gated on what the PR actually touches: Doctrine N+1s and missing migrations, floating promises and React hook deps, goroutine leaks, bare excepts, root Docker users, unguarded GraphQL resolvers.

Load-bearing contracts

Why you can trust the light.

A review tool is only worth running if it can't be talked out of its own answer. Four properties are enforced by the engine and guarded by tests.

The verdict is never the model's text

Rules emit findings. The verdict is computed from each rule's status and its declared severity — never from anything the model wrote. An adversarial prompt can't approve itself by asking the model to type "approved".

Config comes from the default branch only

.codecapitan.yml is read from the repo's default branch, never from the PR's head or base ref. A pull request can't weaken the policy that's judging it, and nobody can smuggle a permissive config in on a side branch.

Findings are diff-scoped by enforcement

Prompts are told to stay inside the lines the PR added. The engine then post-filters every finding against the added-line set regardless. The backstop is the filter, not the prompt.

Every finding names a line

A finding carries the file, the line, the rule that raised it and that rule's weight. The report is the audit trail: hand it to someone who wasn't there and they can check the call themselves, or argue with it.

False positives are argued down, not shipped

Nothing reaches the report without surviving the council — advocate for suppressing, advocate for keeping, judge to rule. Noise is a defect, because the first tool anyone stops reading is the one that cries wolf.

The deterministic tier never short-circuits

Every rule runs even after a red. You get every reason a PR was declined in one report, the way a CI run gives you every failing job — not just the first one.

Dashboard

Watch the scan stream in.

The same engine sits behind a Django and Vite dashboard. Trigger a scan and the results stream in live: every lens and every sub-check ticks from waiting to running to a result as the pipeline works, so a long scan stays legible while it's still going and a failure points at the exact sub-check that tripped.

When a scan comes back green, Approve PR leaves a real review on GitHub, gated behind TOTP. Nothing merges on its own: CodeCapitan is read-only by design and never merges a pull request.

Nothing binds a host port by default. Browser access normally goes through a Cloudflare tunnel into the internal Caddy ingress; PORTS=1 publishes Caddy on localhost when you'd rather not run a tunnel.

agent.auth_safety security_authz
  • authz check removed or bypassedwaiting
  • route made public gating sensitive datawaiting
  • token stored outside auth boundarywaiting
  • signature / JWT / TLS verification disabledwaiting
scanning…

Quickstart

Running in three commands.

make test, make typecheck and make lint all run keyless against fixtures and stubs. You only need keys once you point it at a live PR.

Set up
# GITHUB_PAT needs repo:read. ANTHROPIC_API_KEY
# is only needed when agent rules run.
cp .env.example .env
make dev

make invoke
→ {"statusCode": 200, "body": "ok"}
Review a pull request
make review URL=https://github.com/example-org/\
example-repo/pull/4521

# deterministic only — no API key, no spend
codecapitan review <pr-url> --no-agent-rules

# machine-readable, for batch runs
codecapitan review <pr-url> --format json
Add a rule of your own
# .codecapitan.yml
custom_rules:
  - id: no-active-record-in-controllers
    severity: amber
    weight: 4
    type: prompt
    paths_include: ["app/Controllers/**"]
    prompt: "Flag Active Record calls inside
      controller methods."

Licence

Source-available under Elastic License 2.0.

Use it, copy it, modify it, self-host it — including commercially, inside your own organisation. You may not offer CodeCapitan to third parties as a hosted or managed service, and you may not alter, remove or obscure the licensing and copyright notices.

  • Not a merge bot. The CLI is read-only and never merges.
  • Not a full security scanner. It catches secrets-in-diff hygiene, not everything a dedicated scanner does.
  • Not a webhook listener. The webhook ingress is parked; scans are driven by you.