Configuration
Put a .anvil.yaml file at the root of the repository. Anvil also reads .anvil.yml and .gitea/anvil.yaml, in that order, and uses the first one it finds.
For reviews and questions on a pull request, the file is read from the PR's base branch, not from the PR itself. A PR that edits .anvil.yaml (to turn reviews off, widen path filters or drop a check) is still reviewed under the rules already on the base branch, and its change applies to PRs opened after it merges. The walkthrough's "Review details" section says which file and branch were used. Every key is optional. If the file has an error (invalid YAML, or a value of the wrong type), the whole file is ignored, the defaults apply, and the walkthrough comment shows the error. @anvil configuration shows the settings in effect for a PR.
Example #
This is .anvil.example.yaml from the Anvil repository:
# Copy to .anvil.yaml at the root of a repository. Every key is optional.
profile: chill # chill: bugs and real risks only · assertive: also maintainability, tests, docs, nits
language: en
instructions: |
We use zod for all input validation. Server code lives in server/, never import it from app/.
request_changes_on_critical: false
path_filters: # globs; "!" excludes. Lock files and build output are always excluded.
- "!**/*.generated.ts"
path_instructions:
- path: "migrations/**"
instructions: "Every migration must be reversible and safe to run twice."
- path: "**/*.test.ts"
instructions: "Don't flag style in tests; check that assertions test behaviour."
auto_review:
enabled: true
incremental: true # after a push, review only the new commits
drafts: false
base_branches: [] # e.g. ["main", "release/*"]; empty = all
ignore_title_keywords: ["wip", "do not review"]
ignore_authors: ["renovate"]
walkthrough:
sequence_diagram: auto # auto: only for a flow across components · true · false
estimate_effort: true
checks: # judged on every review, reported in the walkthrough and the commit status
- name: Description explains why
instructions: "The PR description says why the change is needed, not only what changed."
mode: warning # off | warning | error (error fails the `Anvil` status)
ci:
analyze_failures: true # explain failed CI jobs on the PR
reviews:
fail_commit_status: false # true: a review that errors fails the `Anvil` status
guideline_files: true # load AGENTS.md, CLAUDE.md, .cursorrules, copilot instructions
nitpicks: folded # inline | folded | off (chill always drops nits)
routing: auto # auto: cheap model for docs/bot PRs, mid-tier for small diffs, strongest for big or risky ones · off
bots: light # dependabot, renovate, *[bot]: light | review | skip
risky_paths: ["src/payments/**"] # always reviewed on the strongest model (on top of migrations, auth, CI, Dockerfiles…)
trivial_paths: ["translations/**"] # count as trivial, like docs
# models: { light: claude-haiku-5-5, standard: claude-sonnet-5-5, deep: claude-opus-5-5 }
triage: auto # big deep reviews: a cheap model rates each file first, only risky files get the full review
prep: # work done before the model is called (no repository code is ever run)
context_pack: true # callers, callees, types, tests and config of each changed function, in the prompt
context_tokens: 8000
static_analysis: true # secret scanner, dependency check, risky-sink hints
osv: true # look new dependency versions up on osv.dev (sends package names and versions)
memo: true # don't re-review code that comes back unchanged after a rebase or force-push
merge_risk_instructions: "Anything touching migrations/ is at least high."
security_risk_instructions: "Changes under server/auth/ are at least moderate."
chat:
auto_reply: true # answer replies in Anvil's review threads without @anvil
max_budget_usd: 2 # per agent run; can't exceed the server's MAX_BUDGET_USD
Top-level keys #
enabled #
boolean, default true. Set to false to turn off reviews and CI diagnosis for the repository. Commands that start a review still run.
enabled: false
profile #
chill or assertive, default chill. chill reports bugs, security problems and real risks, and drops nits. assertive also comments on maintainability, naming, tests, docs and style.
profile: assertive
language #
string, default en. The language Anvil writes in, as a language code or name.
language: de
instructions #
string, default empty. House rules added to every review.
instructions: |
We use zod for all input validation.
Server code lives in server/; never import it from app/.
request_changes_on_critical #
boolean, default false. When true, a review with at least one critical finding is posted as Request changes instead of a comment.
request_changes_on_critical: true
path_filters #
list of globs, default []. Which changed files are reviewed. Patterns without ! are includes: when there's at least one, only matching files are reviewed. Patterns starting with ! exclude. Dotfiles match.
Lock files, minified files, source maps, dist/, build/, .next/, node_modules/, vendor/ and *.snap are always excluded.
path_filters:
- "src/**"
- "!src/**/*.generated.ts"
path_instructions #
list of { path, instructions }, default []. Extra instructions for files that match a glob. They're added to a review when at least one changed file matches.
path_instructions:
- path: "migrations/**"
instructions: "Every migration must be reversible and safe to run twice."
merge_risk_instructions #
string, default empty. Extra guidance for the merge risk rating, added to every review. Use it to say what counts as risky in this repository.
merge_risk_instructions: "Anything touching migrations/ or billing/ is at least high."
security_risk_instructions #
string, default empty. Extra guidance for the security risk rating, added to every review.
security_risk_instructions: "Changes under server/auth/ or to CORS settings are at least moderate."
See Merge and security risk for the five levels.
max_budget_usd #
positive number, optional. Lowers the cost cap for each agent run on this repository, in US dollars. It can't go above the server's ceiling ($3 on this server). Questions are always capped at $1 or this value, whichever is lower.
max_budget_usd: 1.5
model #
string, optional. One Anthropic model for everything on this repository: every review tier, questions and CI diagnosis. It turns model routing into a single model; to change one tier only, use reviews.models instead.
model: claude-sonnet-5-5
auto_review #
When reviews start on their own. None of these apply to @anvil review.
| Key | Type | Default | What it does |
|---|---|---|---|
enabled |
boolean | true |
Review new and updated PRs automatically |
incremental |
boolean | true |
After a push, review only the new commits |
drafts |
boolean | false |
Also review draft PRs (and titles starting with WIP or Draft:) |
base_branches |
list of globs | [] |
Only review PRs into these branches. Empty means all |
ignore_title_keywords |
list of strings | ["wip", "do not review"] |
Skip PRs whose title contains one of these (case-insensitive) |
ignore_authors |
list of strings | [] |
Skip PRs opened by these users |
auto_review:
base_branches: ["main", "release/*"]
ignore_authors: ["renovate"]
walkthrough #
| Key | Type | Default | What it does |
|---|---|---|---|
sequence_diagram |
auto, true or false |
auto |
Include a Mermaid sequence diagram. auto keeps one only when the review finds a request or data flow across three or more components (most PRs don't get one, which saves output tokens); true asks for one whenever an interaction between components changes; false never |
estimate_effort |
boolean | true |
Include the review effort estimate (1 to 5) |
walkthrough:
sequence_diagram: false
checks #
list, default []. Pre-merge checks written in plain language and judged on every review. See Pre-merge checks.
| Key | Type | Default | What it does |
|---|---|---|---|
name |
string | required | Shown in the walkthrough and the commit status |
instructions |
string | required | What must be true for the check to pass |
mode |
off, warning or error |
warning |
error fails the Anvil commit status when the check fails |
checks:
- name: Migrations are reversible
instructions: "Every new file in migrations/ has a working down migration."
mode: error
ci #
| Key | Type | Default | What it does |
|---|---|---|---|
analyze_failures |
boolean | true |
Explain failed CI jobs on the PR in a comment |
ci:
analyze_failures: false
reviews #
| Key | Type | Default | What it does |
|---|---|---|---|
fail_commit_status |
boolean | false |
When a review errors (the agent fails twice, the checkout fails), set the Anvil status to failure instead of a warning, so a required anvil status blocks merging until a review succeeds |
guideline_files |
boolean | true |
Load AGENTS.md, CLAUDE.md and .cursorrules (at the root and in every changed directory) and .github/copilot-instructions.md from the base branch into the review. See Guideline files |
nitpicks |
inline, folded or off |
folded |
Where nit findings go: inline comments like other findings, one collapsed Nitpicks section in the walkthrough, or nowhere. The chill profile always drops them |
routing |
auto or off |
auto |
Pick the model and effort per pull request (see Model routing). off reviews everything on the strongest model at full effort |
models |
{light, standard, deep} |
server defaults | The model for each routing tier. Unset tiers use the server's REVIEW_MODEL_LIGHT (claude-haiku-5-5), REVIEW_MODEL_STANDARD (claude-sonnet-5-5) and REVIEW_MODEL (claude-opus-5-5) |
risky_paths |
list of globs | [] |
Extra paths that always get the deep tier, on top of the built-in list |
trivial_paths |
list of globs | [] |
Extra paths that count as trivial, like docs: a PR touching only these gets a light review |
bots |
light, review or skip |
light |
Pull requests from dependency bots (dependabot, renovate, any *[bot] account): a light review, a normal one, or none |
triage |
auto, on or off |
auto |
On the deep tier, for diffs of 400+ changed lines or 12+ files (auto) or always (on): a cheap model rates each file's risk first, and only files rated medium or high, plus every risky path, are reviewed in full. See Preparation |
prep.context_pack |
boolean | true |
Put the code around each change into the prompt before the model starts: the enclosing function, its callers, what it calls, the types it uses, its tests and the config it reads |
prep.context_tokens |
number (500–40000) | 8000 |
Token budget for the context pack on the deep tier (half on the standard tier, none on the light tier) |
prep.static_analysis |
boolean | true |
Run the bundled checks on the changed lines: secret scanner, dependency check, risky-sink hints (and Semgrep when the server has it) |
prep.osv |
boolean | true |
Look new and changed dependency versions up on osv.dev. Sends only package names and versions. Off: dependencies are listed for the model, not checked |
prep.memo |
boolean | true |
Remember what was found in each reviewed function; code that comes back unchanged (after a rebase or force-push) isn't reviewed again, and its findings carry over |
reviews:
fail_commit_status: true
nitpicks: inline
Model routing #
Every review is routed to one of three tiers before it starts. The tier, and why, is in the walkthrough footer, on the review page and in the run list.
| Tier | When | Model (default) | Effort | Turns |
|---|---|---|---|---|
| light | Only docs, images, licences or whitespace changed (plus trivial_paths); or a bot PR under 800 changed lines |
claude-haiku-5-5 |
low | 8 |
| standard | Up to 200 changed lines in up to 10 files, nothing risky | claude-sonnet-5-5 |
medium | 40 |
| deep | Anything bigger: medium effort up to 800 lines and 30 files, full effort above that | claude-opus-5-5 |
medium or REVIEW_EFFORT (high) |
MAX_TURNS |
| deep | Any risky path, at any size, whoever the author: migrations and .sql, auth, sessions, tokens, secrets, permissions, crypto, payments and billing, Dockerfiles and compose files, CI workflows, .env*, plus risky_paths |
claude-opus-5-5 |
REVIEW_EFFORT (high) |
MAX_TURNS |
| deep | @anvil full review |
claude-opus-5-5 |
REVIEW_EFFORT (high) |
MAX_TURNS |
Only files in scope count (lock files and build output are always filtered out). For an incremental review, the tier comes from the new commits only. The assertive profile keeps small diffs on the deep tier at medium effort. When a light or standard review fails (runs out of turns or budget, or returns unusable output), it is rerun once on the deep tier instead of failing; both runs show in the run list.
reviews:
routing: auto
bots: skip # don't review dependabot/renovate at all
risky_paths: ["src/ledger/**"]
models:
standard: claude-opus-5-5 # small diffs on Opus too, at medium effort
Preparation #
Before the model is called, Anvil does the work that needs no model, with a parser and git grep over the checkout. It never runs code from the repository.
- Context pack. For every changed hunk: the enclosing function or class (parsed with tree-sitter for TypeScript, JavaScript, Python, Go, Rust, Java, Ruby, PHP, C# and C++; other files get a line-based outline), the places that call it, the functions and types the changed lines use, the tests that reference it and where the environment variables it reads are set. Ranked by relevance and cut to
prep.context_tokens. The reviewer reads this instead of opening files one by one, so deep reviews get fewer turns (REVIEW_PACKED_TURNS, 24 by default); a review that needs more is rerun once with the full allowance. - Collapsing. Files renamed without changes, blocks moved unchanged between files and the same identifier edit repeated across files (a rename) are shown as one line each. Anything with an extra change stays in full.
- Static analysis. A secret scanner (provider key formats: AWS, GitHub, GitLab, Slack, Stripe, Google, npm, private keys) and a dependency check (new or changed versions in
package.json,requirements*.txt,pyproject.toml,go.mod,Cargo.toml,Gemfile, checked against OSV). A provider key outside test data and a dependency version with a published advisory are posted as findings directly. Genericpassword = "…"matches and risky sinks (SQL built from strings,eval, shell commands, raw HTML, unsafe deserialisation, TLS checks turned off) go to the model as hints to confirm or drop. - Triage (see
triageabove). - Template walkthrough. The changed-files table, the layers and the change summaries are built from the diff and the parser; the model writes the summary, risks and findings, plus a sentence for the changes that most need one.
- Memo. Each reviewed function's content hash is stored with its findings. After a rebase, a file whose changed functions were all reviewed before (same path, same code, same review settings) is left out and its findings are re-anchored to the new lines.
@anvil full reviewignores the memo.
The walkthrough's "Review details" section says what was prepared for each review.
chat #
| Key | Type | Default | What it does |
|---|---|---|---|
auto_reply |
boolean | true |
Answer replies in review threads Anvil started without needing @anvil. Replies that start with someone else's @mention are left alone |
chat:
auto_reply: false
agents #
What Anvil's code-changing agents may do. Read from .anvil.yaml on the pull request's base branch, so a pull request can't change these for itself. See Let Anvil change code.
| Key | Type | Default | What it does |
|---|---|---|---|
enabled |
boolean | true |
Allow @anvil fix, fix ci, docstrings, tests, simplify, resolve conflicts and recipes |
push |
new_branch, pr_branch or off |
new_branch |
Where a change goes: a new anvil/fix-<pr>-<n> branch with a pull request into the PR's branch, a commit on the PR's branch, or only a patch in the comment |
command |
string | none | Check command run after the change (pnpm test); the agent can run it too. Needs AGENTS_COMMANDS=local on the server |
command_timeout_s |
number, 10 to 1800 | 600 |
Kill the check command after this many seconds |
max_files |
number, 1 to 200 | 25 |
Refuse a change that touches more files |
max_budget_usd |
number | max_budget_usd, else the server's cap |
Spending cap per task; can't exceed the server's MAX_BUDGET_USD |
max_turns |
number, 5 to 200 | 60 |
Turn limit per task |
autofix_ci |
boolean | false |
Run fix ci by itself when CI fails on a pull request |
autofix_max_attempts |
number, 1 to 5 | 1 |
Automatic attempts per pull request |
recipes |
list | [] |
Prompts run with @anvil run <name>: name, prompt, optional command |
agents:
push: pr_branch
command: pnpm test
autofix_ci: true
recipes:
- name: changelog
prompt: Add an entry for this pull request to CHANGELOG.md under "Unreleased".
Unknown keys are kept and ignored, so a file written for a newer version of Anvil still works.