Skip to content
GitHub stars

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:

  1. Lists open PRs for each configured repo via gh pr list
  2. Skips PRs that have already been reviewed at their current HEAD SHA
  3. Fetches the PR head commit (including fork-based PRs)
  4. Computes the merge-base range (base..head) and enqueues review jobs
  5. 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, and model in 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_workers concurrency (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 start to 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 asyour-app-name[bot]Your personal GitHub account
Setup effortCreate an app, generate keys, install on reposMinimal: just gh auth login
Best forTeams, shared repos, production useQuick testing, personal projects
PermissionsScoped to specific repos and permissionsWhatever your account has access to

Prerequisites

Before enabling the CI poller, you need:

  1. gh CLI installed (roborev shells out to gh for PR listing and comment posting):

    Terminal window
    # Install: https://cli.github.com/
    gh --version # verify it's installed

    If you’re using GitHub App auth, gh does 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 login
    gh auth status # verify it worked

    If your organization uses SSO with personal auth, make sure your token is authorized for SSO access. The daemon runs long-lived. If your gh token expires while the daemon is running, the poller will log errors and skip repos until you re-authenticate.

  2. A local checkout of each repo you want to poll, registered with roborev:

    Terminal window
    cd /path/to/myrepo
    roborev init # starts daemon automatically
    roborev init --no-daemon # if using systemd/launchd to manage the daemon

    The poller matches GitHub repos to local checkouts by git remote URL. If roborev init hasn’t been run for a repo, the poller will log no local repo found matching "owner/repo" and skip it.

    The checkout must use origin as its remote name (the default). The poller runs git fetch origin and git fetch origin pull/<number>/head to retrieve PR commits, including those from contributor forks.

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

    Or set a specific agent in the [ci] config (see below).

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.

FieldValue
App nameAny name you like, e.g. roborev-ci (this becomes the [bot] username)
Homepage URLYour repo URL or any URL
WebhookUncheck “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:

  1. Open app settings and go to Permissions & events.
  2. Set Pull requests to Read and write and Contents to Read-only.
  3. Save the app settings.
  4. 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:

Terminal window
# The downloaded file will be named something like your-app-name.2026-02-08.private-key.pem
mv ~/Downloads/your-app-name.*.private-key.pem ~/.roborev/roborev.pem
chmod 600 ~/.roborev/roborev.pem

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

If 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 = true
poll_interval = "5m"
repos = ["myorg/myrepo"]
agents = ["codex"]
review_types = ["security"]
# GitHub App authentication
github_app_id = 123456
github_app_private_key = "~/.roborev/roborev.pem"
github_app_installation_id = 12345678

The 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 = true
repos = ["wesm/my-project", "roborev-dev/core"]
github_app_id = 123456
github_app_private_key = "~/.roborev/roborev.pem"
[ci.github_app_installations]
wesm = 111111
roborev-dev = 222222

The 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 = 123456
github_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 installation

Each installation gets its own cached access token, so there is no performance penalty for multiple installations.

6. Start the daemon and verify

Terminal window
roborev daemon start # background mode
roborev daemon run # or foreground mode to watch logs

Look 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 = true
poll_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 with roborev review without --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

Terminal window
roborev daemon start # background mode
roborev daemon run # or foreground mode to watch logs

Verifying 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):

Terminal window
# Create a plist - adjust the path to the roborev binary
cat > ~/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.plist

Linux (systemd):

Terminal window
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/roborev.service << 'EOF'
[Unit]
Description=roborev daemon
After=network.target
[Service]
ExecStart=/usr/local/bin/roborev daemon run
Restart=on-failure
[Install]
WantedBy=default.target
EOF
systemctl --user enable --now roborev

Multi-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 = true
poll_interval = "5m"
repos = ["myorg/myrepo"]
# Run both security and standard code reviews
review_types = ["security", "default"]
# Use multiple agents
agents = ["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 synthesis
synthesis_model = "claude-sonnet-4-5-20250929" # Model override for synthesis

If 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 repo
review_types = ["security", "default"] # override review types
reasoning = "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.

OptionTypeDefaultDescription
agentsarrayglobal agentsAgents for CI reviews of this repo
review_typesarrayglobal review_typesReview types for CI reviews of this repo
reasoningstring"thorough"Reasoning level: thorough, standard, or fast

CI Options Reference

Core Options

OptionTypeDefaultDescription
enabledboolfalseEnable the CI poller
poll_intervalstring"5m"How often to check for PRs (minimum 30s, invalid values default to 5m)
reposarray[]GitHub repos to poll in "owner/repo" format
review_typesarray["security"]Review types to run for each PR: security, design, or default. "review" and "general" are accepted as aliases for "default".
agentsarrayauto-detectAgents to run for each PR (e.g., ["codex", "gemini"])
modelstringModel override for CI reviews
synthesis_agentstringAgent for combining multi-job results
synthesis_modelstringModel 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

OptionTypeDescription
github_app_idintegerApp ID from the app settings page
github_app_private_keystringPath to PEM file, ${ENV_VAR}, or inline PEM
github_app_installation_idintegerInstallation ID (fallback for owners not in the installations map)
github_app_installationstableMap 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 = true
poll_interval = "5m"
repos = ["myorg/backend", "myorg/frontend"]
review_types = ["security"]
agents = ["claude-code"]
model = "claude-sonnet-4-5-20250929"
github_app_id = 123456
github_app_private_key = "~/.roborev/roborev.pem"
github_app_installation_id = 12345678

GitHub App — Multi-Agent Matrix

[ci]
enabled = true
poll_interval = "5m"
repos = ["myorg/backend"]
# 2x2 matrix = 4 jobs per PR
review_types = ["security", "default"]
agents = ["codex", "gemini"]
# Synthesis settings
synthesis_agent = "claude-code"
synthesis_model = "claude-sonnet-4-5-20250929"
github_app_id = 123456
github_app_private_key = "${ROBOREV_APP_KEY}"
github_app_installation_id = 12345678

GitHub App — Multiple Installations

[ci]
enabled = true
poll_interval = "5m"
repos = ["wesm/my-project", "roborev-dev/core", "roborev-dev/docs"]
review_types = ["security"]
agents = ["codex"]
github_app_id = 123456
github_app_private_key = "~/.roborev/roborev.pem"
# Each org/user has its own app installation
[ci.github_app_installations]
wesm = 111111
roborev-dev = 222222

Per-Repo Overrides

Global config (~/.roborev/config.toml) sets defaults for all repos:

[ci]
enabled = true
poll_interval = "5m"
repos = ["myorg/backend", "myorg/frontend"]
review_types = ["security"]
agents = ["codex"]
github_app_id = 123456
github_app_private_key = "~/.roborev/roborev.pem"
github_app_installation_id = 12345678

The backend repo wants deeper reviews with multiple agents. Add a .roborev.toml in the backend repo root:

myorg/backend/.roborev.toml
[ci]
review_types = ["security", "default"]
agents = ["codex", "gemini"]
reasoning = "thorough"

The frontend repo is lower-risk and only needs a fast security scan:

myorg/frontend/.roborev.toml
[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 = true
poll_interval = "5m"
repos = ["myorg/backend", "myorg/frontend"]
review_types = ["security"]
agents = ["claude-code"]
model = "claude-sonnet-4-5-20250929"

See Also