Revera reviews GitHub pull requests with the models you choose and posts only the findings that survive an independent second check.
It runs as a GitHub Action or a local CLI. The models do the reasoning; plain Rust code computes the diff, tracks findings across pushes and talks to GitHub.
- Bring your own model. Any OpenAI-compatible chat endpoint, plus native Anthropic, Google Gemini and OpenAI Responses protocols. One route is enough to start.
- Nothing is published unvalidated. An investigator proposes candidate findings; a fresh-context validator re-derives each one from the code before it can appear on the PR.
- Repository-aware. Built-in lexical search over the exact PR head, and optional semantic search and reference lookup through Vera.
- Truthful status. A run is
complete,partialorfailed; budget breaches and provider errors are reported, never presented as a clean review. Findings are tracked across pushes (resolved, reopened) without duplicate comments.
-
Create
revera.yamlin your repository root with one model route:models: investigator: protocol: openai-chat base_url: https://api.example.com/v1 api_key_env: REVIEW_API_KEY model: your-model-id
That is a full configuration: baseline strategy, fresh validation using the same route, Vera off.
revera.example.yamlshows the optional validator and Vera blocks. -
Add the API key as a repository secret (
REVIEW_API_KEYhere) and a workflow:name: revera on: pull_request permissions: contents: read pull-requests: write jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - uses: VeraTools/revera@v0 env: REVIEW_API_KEY: ${{ secrets.REVIEW_API_KEY }}
-
Open a pull request. Accepted findings are posted as inline comments plus one managed summary comment; later pushes update them instead of re-posting.
A posted finding names the defect, how to trigger it, its impact and the lines it rests on. This one, shortened, came from a release test where a single line of ownership checking was flipped:
[high] owns() returns true for comments with missing author, allowing finding suppression via forged revera-id markers
Changing the
(None, _) => truearm makesposted_revera_idstreat any inline review comment whose author is missing as Revera's own. […] GitHub returnsuser: nullfor comments by deleted users.Trigger: a PR has an inline comment from a deleted account whose body contains a revera-id marker matching a finding Revera is about to post. Impact: the finding is silently suppressed. Evidence: src/github/publish.rs:87, src/github/publish.rs:184, src/github/api.rs:102, tests/outcome_tests.rs:530
The summary comment states whether the review is complete or partial
and lists open, resolved and reopened findings.
@v0 follows the latest 0.x release; pin @vX.Y.Z for an exact version.
Check a configuration before the first run with
revera doctor --config revera.yaml; it validates only what the effective
configuration uses and prints a next: hint for every failure.
diff (base…head) → investigator → candidates → fresh validator → anchor / dedupe / publish
│ tools │ tools
lexical search (+ Vera) lexical search (+ Vera)
- Revera diffs base and head (the checked-out tree must be exactly the PR head) and, if Vera is enabled, refreshes the repository index.
- The investigator explores the change with read-only tools and submits structured candidate findings.
- Each candidate is handed to a validator that starts from an empty context and must re-establish the claim from the code. This is why one model is enough to start: the validator reuses the investigator's route but never its conversation.
- Rust anchors accepted findings to diff lines, reconciles them with the previous review (resolved / reopened / still open) and publishes.
Models never post to GitHub directly, run shell commands, or write files. Details: docs/how-it-works.md.
| key | default | notes |
|---|---|---|
models.investigator |
required | protocol, base_url, api_key_env, model, optional reasoning |
models.validator |
investigator route | set to validate with a different (e.g. stronger) model |
review.strategy |
baseline |
delegated / panel are advanced multi-lane strategies |
review.publish |
dry-run (the Action passes comment) |
|
review.min_severity |
low |
|
vera |
off | add a vera: block with an embedding endpoint to enable |
budget.* |
25 tool calls / 300 s per agent, 120 requests / 900 s per run | hard limits; breaches make the run partial |
${VAR} in string values expands from the environment. Full reference:
docs/configuration.md.
| protocol | endpoint | examples |
|---|---|---|
openai-chat |
{base_url}/chat/completions |
OpenAI, Z.ai, DeepSeek, most OpenAI-compatible servers |
openai-responses |
{base_url}/responses |
OpenAI Responses API and compatible hosts |
anthropic |
{base_url}/v1/messages |
Anthropic Claude |
gemini |
{base_url}/v1beta/models/{model}:generateContent |
Google Gemini |
Routes can mix protocols and providers. Reasoning/thinking is on (medium)
by default for every route and degrades automatically on providers that
reject the field.
baseline (one investigator, one validator) is the default and the
recommended setting. delegated (lead plans questions, workers answer,
lead synthesises) and panel (independent scouts, findings unioned) are
available through review.strategy or the Action's strategy input. In
our evaluation they cost 1.5–2× more and did not improve recall; panels
produced more false positives. See docs/strategies.md.
Measured on synthetic corpora with hand-written truth files, 1–2 runs per cell (small samples; treat as provisional):
- Baseline with validation produced zero false positives across 13 small corpora (four clean-control PRs included) with the same model on both routes.
- On a ~50k LOC repository (ripgrep) with cross-crate defects, Vera retrieval found 5/6 defects vs 4/6 lexical-only at ~17 % more time.
- Across eight investigator models (Meta Muse Spark, Z.ai GLM, OpenAI GPT, Google Gemini, DeepSeek, xAI Grok and others), recall did not separate single-model configurations on small repositories; false positives and cost did.
Method, tables and caveats: docs/evaluation.md.
- Action: Linux x86_64 runners (
ubuntu-22.04,ubuntu-24.04/latest). The binary is a staticx86_64-unknown-linux-muslbuild. - Fork PRs are skipped by default (secrets are unavailable to them) and
reported as
partial, not clean. - Inline comments require the finding to sit on a diff line; other accepted findings appear in the summary under "Findings outside the diff".
- Vera indexing of a large repository is slow on first run; the Action caches the index and updates it incrementally.
| revera exit | Action status |
meaning |
|---|---|---|
| 0 | complete |
every stage finished; findings: 0 means "reviewed, nothing found" |
| 2 | partial |
something was not checked (budget, provider, retrieval, malformed model output); zero findings is not a clean verdict |
| other | failed |
config/setup error or crash; no trustworthy report |
fail-on: failed | partial | never (default failed) decides which of
these fail the step.
revera review --repo . --base origin/main --config revera.yaml # local dry-run
revera review --event "$GITHUB_EVENT_PATH" --publish comment # inside Actions
revera doctor [--config revera.yaml] [--profile deep] [--strategy panel]
revera cache-key [--config revera.yaml] # Vera index identity, or "disabled"Local runs keep their state in .revera/ and, with Vera enabled, the index
in .vera/ (Vera 2 also creates .vera.build/, .vera.old/ and, after an
interrupted API index, .vera.resume/); add .revera/ and .vera*/ to
.gitignore. With Vera enabled, local runs need Vera 2.0 or later.
Prebuilt binaries are attached to
releases; or
cargo install --git https://github.com/VeraTools/revera.
- How it works: architecture, boundaries, state and re-review
- Configuration: every key with defaults
- Strategies: baseline, delegated, panel
- Evaluation: what was measured and how to reproduce it
- Contributing · Security · Changelog
MIT.