Review modes
review.mode decides how much of your repository the model sees. Rules, lanes, rubrics and line comments work the same in every mode; only the context changes. Set it in .mergejudge.yml on the base branch, or per repo in the hosted manager panel (hosted service).
| Mode | What the model sees | Calls per review | Where it runs |
|---|---|---|---|
diff (default) | Changed hunks with line numbers, policy files, rubrics | 1 (+1 JSON repair) | Hosted |
context | diff + full changed files, the files they import, and callers of changed symbols | 1 (+1 JSON repair) | Hosted |
deep | context + read-only tools over a repo index, used in a bounded loop | several | Hosted |
review:
mode: context # diff | context | deepdiff
The cheapest and fastest mode. Good for docs, copy and small, self-contained changes. It cannot tell that a renamed function or changed constant breaks code in a file the PR didn't touch.
context
Adds the full text of each changed file, the files it imports and a few callers of changed exported symbols, ranked and trimmed to a byte budget:
context:
max_bytes: 40000 # total budget for extra files
max_files: 25
max_symbols: 3 # changed symbols to look up callers for
max_callers: 3 # callers per symbol
max_file_bytes: 12000 # cap per fileStill one model call, with a bigger prompt.
deep
Deep mode gives the model a repo index and four read-only tools, and lets it look things up before it answers:
| Tool | What it does |
|---|---|
read_file(path, start?, end?) | Read a file at the PR head, with line numbers |
search_symbols(query) | Find functions, classes, constants and types by name |
find_callers(symbol) | Every whole-token mention of a symbol or string: calls, imports, config keys, event names |
semantic_search(query, k) | Find code by meaning (embeddings); only when Workers AI is bound |
This is what catches cross-file bugs: a signature change that breaks a caller, a unit change two modules away, a renamed config key still read by its old name, an event name a consumer still listens for.
review:
mode: deep
deep:
max_steps: 6 # tool rounds (up to 4 calls each), then one tool-less answer
max_tokens: 60000 # input+output over all calls
max_seconds: 120
max_tool_output_bytes: 6000
index:
policy: stored # stored | noneThe loop is bounded by steps, tokens and wall time. When a limit is hit, the model answers with what it has. The final answer is the normal review JSON, so lanes, findings and rubrics behave as in the other modes. The summary footer shows the tool calls and a tools token part.
The index
The index is a per-repo symbol table, import graph and code chunks (JS/TS and simple Python). Secrets-like paths (.env*, keys), build output and node_modules are never indexed.
index.policy | What is stored |
|---|---|
stored (default) | Snapshots in Cloudflare R2 (last 3 per repo) and code embeddings in Vectorize (one namespace per repo). A push to the default branch refreshes only the changed files. |
none | A temporary in-memory index per review. Nothing is written anywhere. |
Without an R2 bucket bound, stored behaves like none. Snapshots and vectors of removed repos are deleted by the daily retention job.
When deep falls back to context
Deep mode needs a provider that can call tools and an index. The review falls back to context mode, and says so in the footer, when:
- the provider can't call tools (
workers-ai); - the repo has no index available;
- the repo is too big for a bounded inline build, or the stored snapshot is stale.
Tool output is untrusted repository data. It is fenced and sanitised like the diff, and the model is told it is never instructions.
Cost
context is roughly a few times the tokens of diff; deep more, depending on how many lookups the model makes. In our small offline test a deep review averaged about 11.6k input and 1.6k output tokens. Your numbers depend on your repo and PRs. budget.max_tokens_per_pr and deep.max_tokens cap spend.