Skip to main content
This example puts a standing reviewer on every pull request in a repository. The agent reads the diff in context — the surrounding code, the repo’s own convention docs, recent related changes — posts exactly one severity-ranked review comment with a merge recommendation, and reports a GitHub check that can gate merges. One session owns each PR: pushes and review conversation route back to it instead of spawning a duplicate reviewer. Use it when you want review coverage that scales with PR volume without a human in the loop for the first pass, and a check that branch protection can require.

The problem

Human review attention is the scarcest resource in most teams. The failure mode of naive automation is noise: a fresh bot review per push, restated diffs, unranked nitpicks, and no way to tell whether the latest verdict applies to the latest head. This example is built around three constraints:
  • One current verdict per PR. A new push supersedes the in-flight review rather than stacking a second one.
  • Read-only by construction. The mount grants contents: read, and the GitHub tool surface is narrowed to reading PRs and posting one comment — no approve, no merge, no pushes, regardless of what the prompt says.
  • A check, not just a comment. The trigger declares a managed check (pr-review) that the platform creates as queued the moment the session spawns and that concludes failure on its own if the agent never reports — so a dead reviewer can’t leave a PR permanently blocked in limbo.

How it works

Three triggers drive the agent: The bind + onUnmatched: spawn combination is what makes the session own the PR: when no session is bound to the pull request yet, the router spawns one and binds it to the PR in the same creation step, so every later matching event resolves to it. See triggers and events for the routing model.

Install from the consolidated agent package

The packaged PR Review role is published in @auto/agents. The thin install is one file — the import carries the prompts, triggers, tools, runtime, and identity:
.auto/agents/pr-review.yaml
Set repoFullName and githubConnection to your repository and GitHub connection. When GitHub Sync applies this file from the bound repository, both variables also have context defaults derived from the Sync binding, and values you declare win. Override any inherited field by declaring it in this file (triggers merge by their name: key), and drop inherited entries with remove: { triggers: [...], tools: [...] }. See managed templates and imports and fragments. Merge the file to your production branch and GitHub Sync applies it.

The full configuration

The directory below is the handwritten equivalent of the template, pinned to a concrete repository so every name lines up. Copy it if you want to own the whole definition instead of tracking the template.
.auto/fragments/environments/agent-runtime.yaml
.auto/agents/pr-review.yaml
Points worth noticing before the walkthrough:
  • The mount is the PR head itself. ref: refs/pull/{{payload.github.pullRequest.number}}/head with depth: 1 checks out exactly what is under review. Mount ref is the one template surface where the payload. prefix is correct — see mounts.
  • contents: read makes the reviewer physically unable to push, while pullRequests: write and issues: write allow the review comment. GitHub credentials are minted per call by the platform and never enter the sandbox — see the GitHub tool.
  • The github tool is narrowed to two tools (pull_request_read, add_issue_comment). Naming tools replaces the curated default allowlist entirely.
  • Slack is optional wiring. Both the chat tool and the mention trigger carry optional: true: apply skips them while no slack connection exists and activates them on the next apply once one does. The GitHub review flow works either way.
  • $.github.auto.authored: false and $.github.auto.externalBot: false keep the conversation trigger from reacting to the agent’s own comments or to third-party bots.

Walkthrough

1

A pull request opens

GitHub delivers pull_request with action opened; ingress verifies the webhook signature, normalizes it to github.pull_request.opened, and records it once (duplicate deliveries never re-route). The pr-events trigger matches its where filter on the repository.
2

Spawn, bound to the PR

The bind route looks for a session bound to this github.pull_request target. There is none, so onUnmatched: spawn creates one — and binds it to the PR in the same creation step. From now on, every matching event for this PR resolves to this session.
3

The check appears immediately

Because the trigger declares checks:, the platform creates the Auto PR review check run on the head SHA in queued state as part of routing — before the sandbox even boots — and arms the beginTimeout watchdog. If the agent never calls checks.begin within 1200 seconds, the check concludes failure on its own.
4

The review runs

The sandbox boots the node24 environment with the PR head mounted at /workspace/repo. The agent calls checks.begin (check goes in_progress), reads the convention docs and the diff, optionally runs targeted tests, posts exactly one review comment with add_issue_comment, and concludes with checks.success or checks.failure carrying the reviewed SHA and gating findings. If Slack is connected, it threads a one-line verdict under the PR’s message in #pr-review.
5

A push folds into the same session

The author pushes; GitHub sends synchronize. The bind route now resolves the owning session, so instead of spawning a second reviewer, the trigger message is delivered into it — interrupting the current turn if one is running. On the platform side the delivery supersedes the old check cycle: the old head’s check run is concluded (as skipped when it never finished) and a fresh queued run is created on the new head. The agent re-begins the check and re-reviews the current head. One PR, one session, one current verdict.
6

Conversation routes back; nothing leaks after archive

Reviewer comments and review threads deliver into the same session through pr-conversation. Its onUnmatched: drop means that once the session is gone, stray conversation events are dropped rather than spawning a reviewer with no review context — while a fresh push (via pr-events, onUnmatched: spawn) starts a new owning session.
To make the review blocking, mark Auto PR review as a required status check in the repository’s branch protection — the check’s displayName is what GitHub shows.

Variations

  • Cheaper reviews. Swap model.id to claude-sonnet-5 (any id from the curated Anthropic list works with the claude-code harness). Keep Opus for repositories where a missed P0 is expensive.
  • Several repositories. Install one thin template file per repository with a distinct agent name (for example pr-review-widgets, pr-review-api) and per-file variables. Two agents cannot share a name.
  • Tune the posture, keep the machinery. Override systemPrompt in your importing file to encode your team’s real review bar — the trigger/check/binding machinery is inherited unchanged. Use { append: ... } on systemPrompt to add house rules below the template’s text instead of replacing it.
  • CI watchdog. If required GitHub Actions workflows guard the repo, configure GitHub Sync’s ciWatchdog (via the auto.sync.enable tool or sync settings) for workflows that support workflow_dispatch. This is deliberately separate from the review trigger: github.check_run.completed triggers are passive routing and may reference checks auto cannot safely re-dispatch.
  • React to CI results. Add a trigger on github.check_run.completed with routing: { kind: bind, target: github.pull_request } and a where filter like $.github.checkRun.conclusion: failure to have the reviewer weigh red CI into its verdict. Check-completion deliveries defer until the session is idle instead of interrupting mid-review. See GitHub events.