Code Review

Reviewers

One built-in reviewer, bugs, reads every pull request on Claude Opus 5. Add reviewers with their own prompts, models, and filters, or replace it.

A reviewer is a prompt and a model that reads the unreviewed commits and proposes findings. Each reviewer runs in its own sandbox with the repository checked out at the reviewed commit, so it judges the change against the code around it, not the diff alone. The gatekeeper then decides which findings post, and Ellipsis posts them as one review.

The default reviewer

With reviews enabled and no pipeline file, one reviewer runs: bugs, on Claude Opus 5. Its brief is a staff engineer's, with one goal: find the defects in the change before they reach production. Not to summarize the diff, praise it, or suggest how it would have written it. It reads the diff first, then enough of the surrounding code to know whether each change is correct: a new early return is only a bug once its callers are read, so it greps for callers and reads git history where a line's intent is unclear.

It looks for:

  • Correctness errors. Inverted conditions, off-by-one bounds, a branch that returns the wrong variable, arithmetic that overflows or divides by a value that can be zero.
  • Unhandled edge cases. Empty collections, zero and negative values, an absent optional used as if present, duplicate or out-of-order input.
  • Error handling. Failures swallowed silently, retries that repeat a non-idempotent side effect, operations that leave the system half-updated with no way back.
  • Concurrency and ordering. Reads racing writes, a check whose condition can change before the action that depends on it, a cache written before the source of truth.
  • Security. Untrusted input reaching a query, shell command, or file path; a missing or wrong authorization check; a credential logged or written to disk. Judged on exploitability, not theory.
  • Regressions in behavior callers depend on. Removed or renamed fields, endpoints, and parameters; changed defaults; deleted handling whose callers are still out there.
  • Data and deploy hazards. A migration that breaks code still running, a lock held long enough to block writes on a large table, a change that only works if two things deploy simultaneously.
  • Missing tests, only when their absence is the finding.

What it never raises is equally deliberate: style, formatting, and naming (a linter owns those), speculative concerns with no concrete failing input, restatements of what the diff does, and praise. A correct, low-risk change gets a one-line confirmation rather than invented nitpicks, and still counts as reviewed.

Each finding states the defect, the input or state that produces the failure, and a fix when the reviewer is confident in one, anchored to the lines that cause the problem and rated 1 to 5 for severity.

Custom reviewers

A pipeline file adds reviewers under review:. Each takes a name, the same claude block an agent config uses (system, model, and the rest), and optionally its own sandbox, skills, budget, and pull_requests filters. Every field is in the configuration reference.

Splitshift's payout math moves money in integer cents, so a specialist watches it:

ellipsis:
  version: v1
  kind: code_review
  name: Backend review with a tip-pool specialist

pull_requests:
  repositories: [splitshift-api]

include_default_reviewers: true

review:
  - name: tip-pool
    claude:
      model: claude-opus-5
      system: |
        You review changes to tip pooling and payout math.

        Money moves through this code in integer cents. Flag arithmetic
        that converts to floats, division whose remainder is dropped
        instead of distributed, and rounding done per worker rather than
        once per pool. Check that the split always sums to the pool.

        Comment on the exact line with the exact failure. Correct math
        gets one line of confirmation, not invented concerns.
    pull_requests:
      paths: [src/payments/**]

budget:
  run: 15.00

When marcus-lee reworks the payout split, tip-pool runs beside the built-in reviewer, and its finding lands as an inline comment on the changed line of src/payments/tip_pool.py:

Splitting the pool with round(pool_cents / len(workers)) rounds each share independently, so the shares do not sum to the pool: an $847.00 pool across 6 workers pays out $847.02.

The share is computed inside the per-worker loop in allocate_tips, so the error grows with the worker count instead of cancelling.

    share_cents, remainder = divmod(pool_cents, len(workers))

Three things to know when writing one:

  • The prompt is the reviewing brain only. Ellipsis tells every reviewer which commit range to review and how its findings are delivered, so never restate the scope, and never instruct a reviewer to post to GitHub: reviewers propose findings, and Ellipsis posts the one review.
  • Reviewers run in parallel, so several cost latency only once. Each runs in its own sandbox, and reviewers without their own budget split the run budget evenly; see budget.
  • A reviewer with its own pull_requests: filters skips pull requests that are not its job, and a skipped reviewer costs nothing. tip-pool above never starts on a pull request that leaves src/payments/ untouched. How those filters match is covered in per-reviewer filters.

Replacing the default

Declaring review: replaces the built-in reviewer. That is the right shape when your own prompts are the review:

ellipsis:
  version: v1
  kind: code_review
  name: House review

pull_requests:
  repositories: [splitshift-api, splitshift-web]

review:
  - name: house-rules
    claude:
      model: claude-sonnet-5
      system: |
        Review this diff against the conventions in CONTRIBUTING.md and
        the service patterns in docs/architecture.md. Comment only where
        the diff departs from a documented convention, and cite the
        document. Skip anything a linter catches.

Only house-rules reviews these pull requests; the built-in bugs reviewer does not run.

To keep the default alongside yours, set include_default_reviewers: true, as the specialist example above does. The built-in reviewer is added in front of your list and runs first. This exists so you never have to copy the built-in prompt into your file, where it would stop tracking improvements to ours: the flag keeps you on the current built-in reviewer while your own run beside it.

Constraints, enforced when the file syncs:

  • A pipeline may declare at most 8 reviewers, counting the built-in one when include_default_reviewers is on. More is a validation error, never a silent trim.
  • Every agent name must be unique across the file, and bugs is reserved when include_default_reviewers is on.
  • review: [] disables the review stage entirely, so combining it with include_default_reviewers: true is a validation error.

Whatever the reviewer list, the gatekeeper still reads every proposed finding before anything posts.