GitHub Integration
roborev can poll GitHub for open pull requests, run code reviews on each one, and post the results as PR comments.
How It Works
The CI poller runs inside the roborev daemon. On each interval it:
- Lists open PRs for each configured repo via
gh pr list - Skips PRs that have already been reviewed at their current HEAD SHA
- Fetches the PR head commit (including fork-based PRs)
- Computes the merge-base range (
base..head) and enqueues review jobs - When all reviews complete, posts the result as a PR comment
For multi-agent or multi-type configurations, results from all jobs are synthesized by an LLM into a single combined PR comment. When only one job is configured, the output is posted directly (no synthesis overhead).
What to Expect
Before enabling the CI poller, understand the following:
- The poller reviews ALL open PRs on first start. It polls immediately on startup, not after the first interval. If you have 20 open PRs, all 20 will be enqueued for review right away. Subsequent polls only review PRs with new commits (tracked by HEAD SHA in the local database).
- All open PRs are reviewed. There is no filtering by draft status, labels, or author. Draft PRs, bot PRs, and stale PRs all get reviewed.
- CI settings are global by default, with per-repo overrides. The
agents,review_types, andmodelin the global[ci]section apply to every repo unless overridden. Individual repos can override agents, review types, and reasoning level via the[ci]section in their.roborev.toml(see Per-Repo Overrides). - Reviews run with
max_workersconcurrency (default: 4). Jobs are enqueued immediately but executed up to 4 at a time. With a multi-agent matrix, a single PR can generate multiple jobs (e.g. 2 types x 2 agents = 4 jobs). - The daemon does not survive reboots. Use
roborev daemon startto run in the background, but you’ll need a launchd agent (macOS) or systemd service (Linux) if you want it to start on boot.
Choose Your Authentication Method
roborev needs GitHub credentials to list PRs and post comments. There are two options:
| GitHub App (Recommended) | Personal (gh CLI) | |
|---|---|---|
| Comments appear as | your-app-name[bot] | Your personal GitHub account |
| Setup effort | Create an app, generate keys, install on repos | Minimal: just gh auth login |
| Best for | Teams, shared repos, production use | Quick testing, personal projects |
| Permissions | Scoped to specific repos and permissions | Whatever your account has access to |
Prerequisites
Before enabling the CI poller, you need:
-
ghCLI installed (roborev shells out toghfor PR listing and comment posting):Terminal window # Install: https://cli.github.com/gh --version # verify it's installedIf you’re using GitHub App auth,
ghdoes not need to be separately authenticated. The app token is injected automatically. If you’re using personal auth, you also need to log in:Terminal window gh auth logingh auth status # verify it workedIf your organization uses SSO with personal auth, make sure your token is authorized for SSO access. The daemon runs long-lived. If your
ghtoken expires while the daemon is running, the poller will log errors and skip repos until you re-authenticate. -
A local checkout of each repo you want to poll, registered with roborev:
Terminal window cd /path/to/myreporoborev init # starts daemon automaticallyroborev init --no-daemon # if using systemd/launchd to manage the daemonThe poller matches GitHub repos to local checkouts by git remote URL. If
roborev inithasn’t been run for a repo, the poller will logno local repo found matching "owner/repo"and skip it.The checkout must use
originas its remote name (the default). The poller runsgit fetch originandgit fetch origin pull/<number>/headto retrieve PR commits, including those from contributor forks. -
At least one AI agent installed. The poller auto-detects installed agents in this order:
codex,claude-code,gemini,copilot,opencode,cursor,droid. You can check what’s available with:Terminal window roborev review --agent test --help # lists available agentsOr set a specific agent in the
[ci]config (see below).
Setup with GitHub App (Recommended)
PR comments will appear as your-app-name[bot] with scoped permissions.
1. Create the GitHub App
Go to GitHub Settings > Developer settings > GitHub Apps > New GitHub App.
| Field | Value |
|---|---|
| App name | Any name you like, e.g. roborev-ci (this becomes the [bot] username) |
| Homepage URL | Your repo URL or any URL |
| Webhook | Uncheck “Active” (not needed — roborev polls) |
Under Repository permissions, set:
- Pull requests: Read & write
- Contents: Read-only
Leave everything else as “No access”. Click Create GitHub App.
If app permissions are currently empty
If you already created the app with empty permissions:
- Open app settings and go to Permissions & events.
- Set Pull requests to Read and write and Contents to Read-only.
- Save the app settings.
- For each existing installation, open installation settings and accept the updated permissions.
Until each installation accepts the new permissions, roborev may fail to list PR data or post PR comments.
2. Note the App ID
After creation, the App ID is shown near the top of the app settings page. You’ll need this for github_app_id.
3. Generate a Private Key
On the app settings page, scroll to Private keys and click Generate a private key. Your browser downloads a .pem file. Store it securely:
# The downloaded file will be named something like your-app-name.2026-02-08.private-key.pemmv ~/Downloads/your-app-name.*.private-key.pem ~/.roborev/roborev.pemchmod 600 ~/.roborev/roborev.pem4. Install the App on Your Repos
From the app settings page, click Install App in the left sidebar. Choose the account or organization that owns your repos, and select which repositories to grant access to.
After installing, note the installation ID from the URL:
https://github.com/settings/installations/12345678 ^^^^^^^^ this is your installation IDIf you have repos across multiple organizations or user accounts, install the app on each one. Each installation gets its own installation ID. You’ll need these for the multi-installation config below.
5. Add CI config
Add to ~/.roborev/config.toml:
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/myrepo"]agents = ["codex"]review_types = ["security"]
# GitHub App authenticationgithub_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"github_app_installation_id = 12345678The github_app_private_key field accepts:
- A file path:
~/.roborev/roborev.pem(tilde is expanded) - An environment variable:
${ROBOREV_APP_KEY}(expands to a path or inline PEM content) - Inline PEM content (starting with
-----BEGIN)
App auth requires github_app_id, github_app_private_key, and at least one installation ID (either github_app_installation_id or entries in github_app_installations). If none are configured, the poller falls back to your personal gh auth.
Multiple Installations
If your repos span multiple GitHub organizations or user accounts, each one has its own app installation with a separate installation ID. Use the [ci.github_app_installations] table to map each owner to its installation ID:
[ci]enabled = truerepos = ["wesm/my-project", "roborev-dev/core"]
github_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"
[ci.github_app_installations]wesm = 111111roborev-dev = 222222The poller extracts the owner from each repo (the part before /) and looks up the matching installation ID. Owner matching is case-insensitive, so wesm matches repos listed as Wesm/repo or WESM/repo.
You can also mix the map with the singular github_app_installation_id as a fallback for owners not in the map:
[ci]github_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"github_app_installation_id = 111111 # fallback for unlisted owners
[ci.github_app_installations]roborev-dev = 222222 # this org uses a different installationEach installation gets its own cached access token, so there is no performance penalty for multiple installations.
6. Start the daemon and verify
roborev daemon start # background moderoborev daemon run # or foreground mode to watch logsLook for the log line:
CI poller: GitHub App authentication enabled (app_id=123456)PR comments will now appear as your-app-name[bot].
Setup with Personal Auth
If you don’t want to create a GitHub App, you can use your personal gh CLI login instead. PR comments will appear as your GitHub account.
1. Add CI config
Add to ~/.roborev/config.toml (make sure you’ve already run roborev init in your local checkout per Prerequisites):
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/myrepo"]agents = ["codex"] # which agents to use (or omit for auto-detect)review_types = ["security"] # "security", "design", or "default"
"default"is the standard code review (bugs, testing gaps, code quality) — the same review you get withroborev reviewwithout--type. The aliases"review"and"general"are also accepted for backward compatibility.
No github_app_* fields needed. The daemon posts comments using whatever account gh auth is logged in as.
2. Start the daemon
roborev daemon start # background moderoborev daemon run # or foreground mode to watch logsVerifying It Works
On startup you should see:
CI poller started (interval: 5m0s, repos: [myorg/myrepo])The poller checks for open PRs immediately, then on each interval. When a review completes, you’ll see:
CI poller: posted review comment on myorg/myrepo#42 (job 123, verdict=P)Use roborev status to check the daemon and queue state at any time.
Keeping the Daemon Running
roborev daemon start runs the daemon in the background, but it won’t survive a reboot. For persistent operation, set up a system service.
macOS (launchd):
# Create a plist - adjust the path to the roborev binarycat > ~/Library/LaunchAgents/com.roborev.daemon.plist << 'EOF'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>com.roborev.daemon</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/roborev</string> <string>daemon</string> <string>run</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/tmp/roborev-daemon.log</string> <key>StandardErrorPath</key> <string>/tmp/roborev-daemon.log</string></dict></plist>EOF
launchctl load ~/Library/LaunchAgents/com.roborev.daemon.plistLinux (systemd):
mkdir -p ~/.config/systemd/usercat > ~/.config/systemd/user/roborev.service << 'EOF'[Unit]Description=roborev daemonAfter=network.target
[Service]ExecStart=/usr/local/bin/roborev daemon runRestart=on-failure
[Install]WantedBy=default.targetEOF
systemctl --user enable --now roborevMulti-Review Types and Agents
You can configure multiple review types and agents to run in parallel for each PR. The CI poller creates a matrix of jobs (review_types x agents) and posts a single synthesized comment when all jobs complete.
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/myrepo"]
# Run both security and standard code reviewsreview_types = ["security", "default"]
# Use multiple agentsagents = ["codex", "gemini"]
# This creates 4 jobs per PR (2 types x 2 agents)When the matrix is 1x1 (single review type, single agent), synthesis is skipped and the output is posted directly.
Synthesis
When multiple jobs complete, their outputs are combined by an LLM synthesis step into a single well-formatted PR comment. The synthesis agent:
- Deduplicates findings reported by multiple agents
- Organizes findings by severity
- Preserves file and line references
- Produces a one-line summary verdict
You can customize the synthesis behavior:
[ci]synthesis_agent = "claude-code" # Agent to use for synthesissynthesis_model = "claude-sonnet-4-5-20250929" # Model override for synthesisIf synthesis fails, roborev falls back to posting all review outputs in collapsible <details> blocks.
Per-Repo Overrides
Individual repos can override the global CI settings by adding a [ci] section to their .roborev.toml file. This lets you run different agents, review types, or reasoning levels for different repos.
# .roborev.toml (in repo root)
agent = "codex" # agent for post-commit reviews (unrelated to CI)
[ci]agents = ["gemini"] # override agents for CI reviews of this reporeview_types = ["security", "default"] # override review typesreasoning = "standard" # override reasoning level (thorough, standard, fast)Per-repo overrides take priority over the global [ci] config. Any field not set in the repo’s [ci] section falls back to the global config.
| Option | Type | Default | Description |
|---|---|---|---|
agents | array | global agents | Agents for CI reviews of this repo |
review_types | array | global review_types | Review types for CI reviews of this repo |
reasoning | string | "thorough" | Reasoning level: thorough, standard, or fast |
CI Options Reference
Core Options
| Option | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable the CI poller |
poll_interval | string | "5m" | How often to check for PRs (minimum 30s, invalid values default to 5m) |
repos | array | [] | GitHub repos to poll in "owner/repo" format |
review_types | array | ["security"] | Review types to run for each PR: security, design, or default. "review" and "general" are accepted as aliases for "default". |
agents | array | auto-detect | Agents to run for each PR (e.g., ["codex", "gemini"]) |
model | string | Model override for CI reviews | |
synthesis_agent | string | Agent for combining multi-job results | |
synthesis_model | string | Model override for synthesis |
When agents is empty, the poller auto-detects the first available agent from: codex, claude-code, gemini, copilot, opencode, cursor, droid.
GitHub App Options
| Option | Type | Description |
|---|---|---|
github_app_id | integer | App ID from the app settings page |
github_app_private_key | string | Path to PEM file, ${ENV_VAR}, or inline PEM |
github_app_installation_id | integer | Installation ID (fallback for owners not in the installations map) |
github_app_installations | table | Map of owner name to installation ID for multi-org setups (see Multiple Installations) |
App auth requires github_app_id, github_app_private_key, and at least one installation ID (either github_app_installation_id or entries in github_app_installations). If none are configured, the poller falls back to default gh authentication. For repos whose owner has no matching installation ID, the poller also falls back to default gh auth for that repo.
Troubleshooting
The daemon logs to stdout (or to the log file if using a system service). Common issues:
“no local repo found matching…”
You need to run roborev init in a local checkout of the repo. The poller matches GitHub owner/repo to local repos by git remote URL.
“gh pr list: …”
The gh CLI is not installed, not authenticated, or doesn’t have access to the repo. Run gh auth status and gh pr list --repo owner/repo to debug. If your org uses SSO, re-authorize your token with gh auth refresh.
“merge-base … : …”
The PR’s base or head commit isn’t available locally. This usually means git fetch failed. Check that the local repo has the remote configured correctly.
“GitHub App token failed, falling back to default gh auth”
The GitHub App authentication failed. Check that your PEM file path is correct, the app is installed on the repo, and the installation ID matches. The daemon falls back to your gh CLI auth. If you’re also logged in via gh auth login, PR operations will still work but comments will appear as your personal account. If you’re not logged in, gh commands will fail.
“no installation ID for owner …, using default gh auth”
The poller found no installation ID for this repo’s owner. If you’re using [ci.github_app_installations], add an entry for the owner. If you’re using the singular github_app_installation_id, make sure it’s set. Owner names are matched case-insensitively, so wesm and Wesm are equivalent.
No log output at all for CI
Check that [ci] enabled = true is in ~/.roborev/config.toml and that the daemon was restarted after adding it. The [ci] section requires a daemon restart to take effect.
Reviews enqueue but never complete
Check roborev status to see if jobs are queued/running. The agent may be failing — check the daemon logs for error messages from the agent.
Unexpected review burst on first start This is normal. The poller reviews all open PRs on first startup. After the initial run, only PRs with new commits (different HEAD SHA) trigger new reviews. The tracking is persistent across daemon restarts.
Full Examples
GitHub App — Single Review
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/backend", "myorg/frontend"]review_types = ["security"]agents = ["claude-code"]model = "claude-sonnet-4-5-20250929"
github_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"github_app_installation_id = 12345678GitHub App — Multi-Agent Matrix
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/backend"]
# 2x2 matrix = 4 jobs per PRreview_types = ["security", "default"]agents = ["codex", "gemini"]
# Synthesis settingssynthesis_agent = "claude-code"synthesis_model = "claude-sonnet-4-5-20250929"
github_app_id = 123456github_app_private_key = "${ROBOREV_APP_KEY}"github_app_installation_id = 12345678GitHub App — Multiple Installations
[ci]enabled = truepoll_interval = "5m"repos = ["wesm/my-project", "roborev-dev/core", "roborev-dev/docs"]review_types = ["security"]agents = ["codex"]
github_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"
# Each org/user has its own app installation[ci.github_app_installations]wesm = 111111roborev-dev = 222222Per-Repo Overrides
Global config (~/.roborev/config.toml) sets defaults for all repos:
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/backend", "myorg/frontend"]review_types = ["security"]agents = ["codex"]
github_app_id = 123456github_app_private_key = "~/.roborev/roborev.pem"github_app_installation_id = 12345678The backend repo wants deeper reviews with multiple agents. Add a .roborev.toml in the backend repo root:
[ci]review_types = ["security", "default"]agents = ["codex", "gemini"]reasoning = "thorough"The frontend repo is lower-risk and only needs a fast security scan:
[ci]review_types = ["security"]agents = ["codex"]reasoning = "fast"Result: backend PRs get a 2x2 matrix (4 jobs) with thorough reasoning, while frontend PRs get a single fast security review. Repos without a .roborev.toml [ci] section use the global defaults.
Personal Auth — Single Review
[ci]enabled = truepoll_interval = "5m"repos = ["myorg/backend", "myorg/frontend"]review_types = ["security"]agents = ["claude-code"]model = "claude-sonnet-4-5-20250929"See Also
- Configuration - Global and per-repo settings
- Event Streaming - Stream review events for custom integrations