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.

The bottleneck moved

Nobody is reviewing forty PRs a day.

Assistants made writing code cheap. Reviewing it costs exactly what it always did. So the backlog gets dealt with one of three ways, and you already know which one your team picked. All three are the same bet — that the code is fine — placed by default rather than on purpose.

  1. “We'll get to it this week.”

    Let the queue build

    PRs pile up waiting for someone with the context to spare. Branches drift, conflicts stack, and the review that eventually happens is a scroll to the bottom of a diff whose author has moved on twice since.

    What it costsEverything ships late, and the change nobody could find time to review still gets the least attention of all when it finally goes in.

  2. “LGTM 👍”

    Approve without really reading

    The queue stays clear because approvals get shallower. Forty PRs a day is roughly one every twelve minutes, so the diff gets skimmed for anything obviously alarming and waved through.

    What it costsYou keep the ceremony of review and lose the protection. Every approval on the repo now means the same thing — nothing — including the ones somebody really did read.

  3. “Turn it off for now, we're blocked.”

    Switch required reviews off

    Branch protection comes off “temporarily” so the team can move. It is the honest version of the rubber stamp: at least nobody is pretending the code was looked at.

    What it costsWhatever lands, lands. And the day something goes wrong there's no record of who agreed to it, or on what basis — the audit trail left with the requirement.

  4. “Is this one safe to merge?”

    Decide with evidence, not with vibes.

    Point CodeCapitan at a pull request and it answers that question — with reasons, file and line — telling you whether the 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 also doesn't replace anything you already run. Your scanners, your review bots and your CI keep reporting exactly as they do now; CodeCapitan reads what they found, and whether the PR did anything about it.

    What you getThe bet becomes a decision you can show your working for: re-runnable, argue-able, and still defensible a week later to somebody who wasn't there.

The scan, step by step

Five steps to the summit.

A scan is a pipeline, and it behaves like one: fixed order, every step reports, results streaming back as they land. Five steps run 14 agent groups, 73 lenses and 291 sub-checks between them — and the half that catches the worst of it costs nothing.

11 22 33 44 55
  1. Step 1

    Deterministic scans

    Secrets, tampered CODEOWNERS, trojan source, size gates, a red pipeline — plus a language pack for whatever the PR happens to touch.

    40+ rules · every PR · $0.00

  2. Step 2

    Agent checks

    Fourteen groups, each multiplexed into one model call behind a cheap gate that decides whether the group is worth running at all.

    14 groups · 73 lenses · 291 sub-checks

  3. Step 3

    Adjudication

    A council argues every finding — one advocate to suppress it, one to keep it, a judge to rule. False positives are a defect here.

    suppress-advocate · keep-advocate · judge

  4. Step 4

    Risk assessment

    Confidentiality, integrity, availability and privacy are each rated, then re-rated against each other into one whole-PR risk band.

    4 dimensions · emergent band

  5. Step 5

    Verdict and actions

    Every result is scored into a single light, CODEOWNERS resolves the reviewers, and the report states exactly what it intends to do.

    score · resolve reviewers · exit 0 / 1 / 2

The one short-circuit. If a rule at the top severity fails — a failing required check, a disabled CI gate — 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.

Scope

It doesn't replace your tools. It reads them.

Those five steps exist to answer one question — is this pull request safe to merge without a human? Everything else already in your pipeline is evidence for that answer. Keep all of it: the more signal reaching the PR, the better the call.

A SAST or DAST replacement
Keep them. Semgrep, Snyk, GitHub Advanced Security, Trivy and GitGuardian sweep an entire codebase for vulnerabilities, in depth and on their own schedule. CodeCapitan reads one pull request and prices the risk of merging it. It will surface security problems on the way — that's a by-product of the risk call, not a substitute for a scanner.
A replacement for CodeRabbit or Sourcery
Keep them too. A review bot's job is to comment on the code. CodeCapitan's job is to decide. It reads what those bots said on the PR and — more usefully — whether the PR actually did anything about it. A thread marked resolved on a line that never changed is a signal no bot reports on itself.
A replacement for your pipeline
Keep the lot. Tests, lint, type-check, coverage gates, your own scanners bolted onto CI. A failing required check makes a PR dead on arrival; a failing advisory one is a reason to escalate rather than decline. Switch any of it off and CodeCapitan has less to go on, not more.

reviews.unaddressed_feedback · always on

The finding somebody resolved without fixing.

Tool output is only worth what somebody does with it. CodeCapitan reads every review thread on the PR, works out who opened it and whether the flagged line actually changed in the diff, and treats a security tool's high-severity thread closed with no code change as a red.

That tier is deterministic and fail-closed: no model is consulted, so a persuasive comment body can't talk its way out of it. The agent layer is only ever allowed to narrow the set below, never to widen it or soften a severity.

  • security tools → red tierGitHub Advanced Security · Snyk · Semgrep · SonarQube Cloud · SonarCloud · Trivy · GitGuardian
  • review bots → weighted amberCodeRabbit · Sourcery — and their boilerplate is stripped before anything is read, so the badge rows and prompt blocks don't count as feedback
  • closed_without_changeThread resolved or gone outdated while the line it flagged was never touched by the diff
  • open_on_shippedThread still open on a file the PR is shipping
  • identity from the login, never the textThe actor is GitHub's author.login. Comment bodies are untrusted data, delimited as such, and never instructions
  • CI from both APIsCheck Runs and the legacy commit statuses, so Buildkite, Jenkins and Codecov count the same as a GitHub Action

The catalogue

What it actually looks for.

What CodeCapitan reads in one diff to work out how much risk merging it carries. 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

Wrappers around a single caller, any-type escape hatches, renames left half-done, and tests that exercise behaviour without asserting anything.

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.

Docs & comments

agent.docs.sensitive_disclosure · agent.docs.content_quality · agent.comment_quality

Real customer names, live credentials or personal data in prose, instructions that contradict themselves, and comments that restate the code or narrate the edit instead of the intent.

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.

The verdict

How all of that becomes one answer.

Every rule in the catalogue declares its severity before it ever runs, and a scan lands in exactly one of four tiers. Green means safe to approve unattended, amber means a human should look, red and critical mean decline.

  1. 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 paid agent pass is skipped entirely. The deterministic tier still runs and reports in full.

  2. 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.

  3. 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.

  4. 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.

Precedence runs down the list: the highest severity with a failing rule is the verdict, and nothing below it can argue that away.

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. These seven properties are enforced by the engine itself and guarded by tests — not by anyone remembering to be careful.

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.

Every rule judges the same snapshot

One fetch pulls the PR, its diff, checks, review threads and CODEOWNERS before a single rule runs. Nothing re-reads GitHub mid-scan, so two rules can never disagree about what the PR contained.

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 webhook listener. The webhook ingress is parked; scans are driven by you.
  • Not a replacement for the tools it reads. It's the layer above them.