Skip to content

Configure .lgtmaybe.yml

Place a .lgtmaybe.yml file at the root of your repository to control how lgtmaybe reviews pull requests. CLI flags override file values; the file provides defaults for all runs.

Upgrading from 1.14

Remove prompt_cache from existing .lgtmaybe.yml files and remove --prompt-cache or --no-prompt-cache from scripts. Prompt caching is now automatic where the provider supports it.

Contents

Full example

provider: openai
model: gpt-5.5
min_severity: low
include_paths:
  - "src/**"
  - "lib/**"
exclude_paths:
  - "**/__pycache__/**"
  - "**/*.min.js"
max_files: 30
max_input_tokens: 80000
categories:
  - security
  - correctness
  - tests

Field reference

See Reference: Config for the full schema with types and defaults.

provider

Which LLM backend to use. One of openai, openrouter, anthropic, zai, bedrock, vertex, azure, ollama, openai-compatible.

provider: anthropic

model

The model identifier for the chosen provider. Format varies by provider:

Provider Example model IDs
openai gpt-5.5
anthropic claude-sonnet-4-6, claude-haiku-4-5
openrouter anthropic/claude-sonnet-4-6
zai glm-4.6, glm-4.7, glm-4.5-air (GLM / Zhipu AI; newer glm-5.x pass through too)
bedrock us.anthropic.claude-sonnet-4-6, us.anthropic.claude-haiku-4-5 (prefer the cross-region inference profile; a non-Bedrock id like openai.gpt-5.5 is invalid — see Review with Bedrock)
vertex gemini-3-pro, gemini-3.5-flash
azure your deployment name, e.g. my-gpt-4o-deployment (not the upstream model id — see Review with Azure)
ollama qwen3.6:27b, gemma4:e4b
openai-compatible the served model name, e.g. deepseek-chat or meta-llama/Llama-3.1-8B-Instruct (requires api_base — see Use a custom OpenAI-compatible endpoint)

fallback_model

fallback_model names one second model for a review call that the primary model cannot finish. It is off by default and adds no calls to a healthy review.

Both model names go through the configured provider, api_base, and credentials. This means an OpenRouter review can fall back from a Google model to a Qwen model because OpenRouter serves both. An openai review cannot fall back to an Anthropic account. Cross-provider failover needs a separate review run.

provider: openrouter
model: google/gemini-3.7-flash
fallback_model: qwen/qwen3.8-max

The fallback runs at different points depending on the failure:

Primary model result What lgtmaybe does
Transient provider failure, such as a connection error, capacity rate limit, or 5xx Exhausts the primary model's bounded retry policy, then tries the fallback model.
Permanent provider failure, such as an unknown model, rejected request, or exhausted quota Skips repeated primary attempts and tries the fallback model. A shared credential, quota, or endpoint failure will usually reject both.
Output reaches the max_tokens ceiling First retries a smaller payload or lowers reasoning_effort on the primary, based on the reported token counts. It tries the fallback only if that remedy also fails.

The fallback does not judge the primary answer. A clean review, a low-quality finding, or zero findings will not trigger it. Output parsing has its own bounded repair path.

The fallback gets a fresh request and retry budget, so it adds to the primary model's spent time and tokens. The profile keeps both models' calls and the review summary names every lens answered by the fallback. Use --profile for the human-readable models: line or --profile-json for the model attached to each call. Its usage contributes to max_review_tokens after the call returns. See Profile Reference.

If the fallback fails, lgtmaybe marks the lens incomplete and stops that recovery path. A review with no usable results fails instead of posting a clean result. lgtmaybe does not walk a list of models or fall back to the primary again. A lens that repeatedly needs the second model is a signal to make that model the primary.

Set the same value for one run with --fallback-model. The CLI flag and the GitHub Action's fallback_model input override .lgtmaybe.yml.

lgtmaybe review --fallback-model qwen/qwen3.8-max

See Choose a Review Model for current model results.

min_severity

The minimum severity level to report. Findings below this threshold are suppressed. Ordered low to high: info, low, medium, high, critical.

min_severity: medium   # suppresses info and low findings

Default: low (suppresses only info findings).

include_paths / exclude_paths

Glob patterns to restrict which files in the diff are reviewed. include_paths acts as an allowlist; exclude_paths acts as a denylist applied after the allowlist, so an exclude always wins. Both default to empty (all files included). Patterns match against the full repo-relative path, and a **/-prefixed pattern also matches at the repo root (so **/*.lock covers a root-level lockfile). The built-in skip filter for generated, vendored, and binary files runs first either way — an include_paths entry can't resurrect a lockfile.

include_paths:
  - "src/**"
exclude_paths:
  - "src/generated/**"
  - "**/*.lock"

max_files

Maximum number of changed files to include in the review. Files beyond this limit are skipped. Reduces token usage on large PRs.

max_files: 30

Default: 50.

max_input_tokens

Token budget per model call. When the compressed diff exceeds this limit, lgtmaybe splits it across multiple batched calls (and, with recursive on, walks an over-budget single file hunk-by-hunk) — nothing is truncated or dropped.

max_input_tokens: 80000

Default: 100000.

preset

How many model calls the review spends. fast (the default) covers all nine categories in four calls, one per concern:

Call Covers
security security
correctness correctness, and stated intent when the PR states one
code health performance, complexity, ponytail, deprecation
artefacts tests, documentation

The same four run on every provider — worker count changes only how they are scheduled, not how many there are. full runs one focused call per lens for release branches and deep audits.

preset: full

Default: fast. CLI: --preset fast|full (--full is shorthand). An explicit categories list (below) overrides the preset grouping.

categories

Which review lenses to run. An explicit list disables the preset grouping: the reviewer asks for each listed category in its own concurrent model call and merges the findings, so a focused prompt concentrates on one concern at a time. One or more of security, correctness, deprecation, tests, documentation, performance, complexity, intent, ponytail. Narrowing the list trades thoroughness for fewer model calls (and lower token usage).

The ponytail lens is the "lazy senior dev" check — the best code is the code you never wrote — flagging code that needn't exist at all (YAGNI, reach for the standard library, do it in fewer lines). See What gets reviewed.

The intent lens checks the diff against the PR's stated intent — title, description, and commit names on GitHub; your git log commit names on the CLI (in both branch and --working mode). When nothing states an intent (e.g. no commits beyond the base branch yet), it is skipped automatically, so it never costs an extra call (under the fast preset it shares correctness's call). It is also the only lens that sends the PR title/description/commit names to the provider — drop it from categories if you don't want that text sent at all.

categories:
  - security
  - correctness

Default: all nine categories.

context_lines

Ceiling on the number of unchanged lines added around each changed hunk. The lines are read from the head revision of the file, so the model reviews a change in the context of its surrounding code. The pad is asymmetric: the full budget goes before the hunk (the enclosing signature and setup explain a change best) and a quarter of it — at least one line — goes after. The actual number used is the smaller of this ceiling and what the token budget allows, so it shrinks automatically on large PRs. Set it to 0 to disable context expansion and review the bare diff (no extra file content is fetched).

context_lines: 10   # at most 10 lines before each hunk (2 after); 0 disables

Default: 20.

function_context

Extend each hunk's leading pad up to the enclosing function or class signature when it sits above the fixed context_lines window — the signature and setup explain a change better than an arbitrary cut. Boundaries are found structurally with ast-grep (already bundled for symbol resolution; parsing only, never executing) for Python, JS/TS/TSX, Go, Rust, Java, and Ruby, with a bounded reach so a distant definition can't drown the diff. Unsupported languages and any ast-grep failure keep the plain fixed-line pad.

function_context: false   # fixed-line padding only

Default: true.

timeout

Per-request timeout in seconds for each model call. Left unset, lgtmaybe picks a provider-aware default: 1800 s for ollama, openai-compatible, and openrouter (local models are slow, and openrouter can route to slow reasoning models) and 600 s for direct cloud providers. Set it explicitly to raise it for a large local model.

timeout: 3600   # an hour per call, e.g. for a big model on CPU

Default: auto (ollama/openai-compatible/openrouter 1800 s, cloud 600 s). See Run locally with ollama.

structured_output

Constrain the model to emit the findings JSON schema using the provider's native JSON mode (litellm response_format). This keeps models — especially local ones — from returning prose or reasoning instead of findings. Leave it on unless the combination in front of you fails in one of two ways:

  1. A gateway rejects response_format outright (some openai-compatible gateways return a 400). The review fails loudly; turn this off.
  2. A gateway accepts JSON-schema mode but returns replies the parser reads as empty. This fails silently: every lens logs findings: 0 parsed / 0 returned, the run exits clean, and a capable model looks like one that finds nothing. Seen with anthropic/claude-fable-5.1 behind OpenRouter on lgtmaybe 2.3.0 and 2.7.0 — the same diff with the same flags returned 11 parsed findings once this was off. If a model is unexpectedly silent, re-run one diff with --no-structured-output before concluding the model finds nothing.

In both cases the lenient parser still strips fences and pulls JSON out of any surrounding prose. CLI: --no-structured-output.

structured_output: false   # if your gateway rejects JSON-schema mode, or a model returns zero findings under it

Default: true. See Use a custom OpenAI-compatible endpoint.

reflect

Run the self-reflection pass that audits the merged findings and drops the ones the model marks low-confidence, before anything is posted. This trims false positives, so leave it on for most models. Turn it off for a weaker or local model that over-prunes and drops valid findings during the audit. CLI: --no-reflect.

reflect: false   # keep every finding; skip the false-positive audit

Default: true. To audit a weak reviewer's findings with a stronger model instead of disabling the pass, set reflect_model to that model id (it uses the same provider and credentials as model).

min_confidence

During reflection the auditor also scores each kept finding's confidence from 0 (certainly a false positive) to 10 (certain it is real), reached by actively trying to disprove the finding against the diff and the file text. Findings scored below min_confidence are dropped before posting; the surviving score is shown in the CLI output, the JSON export, and — as a percentage — on the posted GitHub comment itself: **[HIGH · security · 80%] Title**, alongside the lens that raised it. A finding the auditor keeps but doesn't score always survives the threshold — a missing score never drops a real finding, and the comment simply omits that half of the badge (as it does for every finding when reflect: false). CLI: --min-confidence.

min_confidence: 5   # drop findings the auditor scores 0-4

Default: 0 (no numeric filtering — reflection prunes only via its keep/drop verdicts, as before the score existed).

mid_review_retrieval

Let a review lens defer once for bounded, read-only codebase context.

By default every lens is told the diff is only a slice of the codebase: if a finding depends on code it cannot see — a guard, a base class, the helper it calls — it must hedge the wording, lower the severity, or say nothing. That protects you from confident cross-file nonsense, and it also means a real bug whose evidence lives one file away is never reported.

With this on, the lens gets a third option: alongside its findings it names the files (or symbols) it must read. lgtmaybe fetches them read-only — the same API/worktree read the reflection auditor's deferral uses, never a checkout of PR code — redacts them, and re-runs that one lens with them in front of it. Both calls' findings are kept and deduped, so a deferral can only add findings.

mid_review_retrieval: true   # trade tokens for cross-file recall

Default: false. It is opt-in because of the price: up to one extra model call per (batch, lens), carrying the fetched text as well. Every other bound is fixed — one hop (the re-run cannot defer again), at most five files inside a quarter of max_input_tokens, and a deferral arriving past max_review_seconds or max_review_tokens is skipped and reported in the summary. Read Reduce Review Cost before turning it on, and pair it with max_review_tokens if the worst case matters. CLI: --mid-review-retrieval; Action input: mid_review_retrieval.

incremental

Commit-scoped incremental review, for the GitHub posting path. On a re-run lgtmaybe reads a hidden watermark (the head SHA its last completed review covered) from its own summary comment and reviews only the diff of the commits pushed since, instead of the whole PR — faster, cheaper, and no re-noise on code that was already reviewed. New findings post as inline comments; findings on files outside the increment stay open, and are only auto-resolved by a run that actually re-reviewed their file.

It always degrades to a full review when there is no watermark yet (first review), after a force-push/rebase (the increment would be meaningless), or if the compare fails. A failed review never moves the watermark, so no commit is ever silently skipped. Comment /review full on the PR to force a full re-review on demand.

incremental: false   # every run reviews the whole PR

Default: auto — incremental on a synchronize push (new commits on an already-reviewed PR), full review everywhere else (open/reopen, slash commands, and the local CLI, which never uses it).

static_analysis

Static-analysis fusion: run fast, deterministic tools over the changed files. Each tool reaches the review in one of two modes:

  • hint — findings become hints for the model to confirm, contextualise, or discard. This raises recall on exactly the mechanical bugs LLMs miss without posting raw linter noise; only findings the model itself confirms are reported. The default for ruff, bandit, mypy and semgrep.
  • finding — findings are posted directly, with no model call at all. The default for gitleaks, zizmor, ast-grep and osv-scanner. Deterministic, free, and identical run to run.

The split is about the tool, not taste: a committed credential is present or it isn't, so asking a model to "confirm or discard" a regex match only adds latency and a chance of it talking itself out of a real hit. A lint or a SAST heuristic is the opposite — often technically true and beside the point — which is exactly what the model is good at filtering. Override either way with tool_mode.

Supported tools: ruff, bandit, and mypy (Python), gitleaks (secrets, any language), zizmor (GitHub Actions workflow security — template injection, unpinned uses, over-broad permissions; it runs only when the PR changes a workflow file), ast-grep (your own structural rules — the deterministic sibling of extra_lenses; set ast_grep_rules), osv-scanner (known vulnerabilities in your dependencies), and semgrep (multi-language SAST).

osv-scanner is the one check a model genuinely cannot do: whether a pinned dependency has a published advisory depends on what was disclosed this week, and a model's knowledge cutoff makes any answer it gives unreliable. It reads the dependency manifests and lockfiles your PR changes — fetched into a scan-only channel that never enters the diff, a prompt, or the reflection pass, because nobody wants a model commenting on line 84,000 of a lockfile.

Its findings are about the dependency rather than a line, so they render in the review body rather than as inline comments, and only high/critical advisories surface by default (unanchored_min_severity).

It needs a local vulnerability database — the sandbox has no network, so it can never fetch one. The GitHub Action image ships one, baked in at build time. On the CLI, seed it once with osv-scanner --download-offline-databases; without a database the scanner reports nothing, and the review says so rather than passing it off as clean.

When osv-scanner is set to post findings, the review prompt stops asking the model whether a version has a known advisory or a package is abandoned — a knowledge cutoff cannot answer either, so asking would only put a confident guess beside an accurate answer. Everything a vulnerability database cannot answer stays with the model: deprecated APIs, end-of-life runtimes, typosquats and licence conflicts. Demote osv-scanner to hint and the model takes those claims back.

The same happens for gitleaks. When it is set to post findings, the review prompt stops asking the model for hardcoded secrets — and here the ask was close to unanswerable anyway: lgtmaybe redacts every secret it matches before the diff leaves for the provider, so the model was being asked to spot what it had been prevented from seeing, while gitleaks reads the unredacted file text and answers exactly. Secrets reaching a log are a different defect that no secret scanner reports, so the model keeps that one. Demote gitleaks to hint and the model takes the claim back.

semgrep now works out of the box. It used to skip itself unless you set semgrep_rules, which almost nobody did — so the one multi-language tool never ran. It now falls back to a small, high-precision MIT rule pack shipped with lgtmaybe, and semgrep_rules overrides which rules it uses. Rules are always read from a local path: semgrep's registry configs need the network, which the sandbox forbids.

We ship our own rules rather than bundling a well-known upstream pack because the widely-used semgrep-rules / opengrep-rules collections are LGPL-2.1 plus a Commons Clause — not an open-source licence, and not something an MIT package can redistribute honestly. Point semgrep_rules at a directory to use a fuller pack you have obtained yourself.

Two rules keep direct posting honest. Tools read whole files, but only the diff is under review, so a finding on a line this PR did not change is dropped and counted in the summary — otherwise a fake credential in a test fixture would post on every PR that touches that file, forever. And direct findings are capped per review, most severe first, since no model is there to filter volume.

Posted findings carry a scan:<tool> category, so finding_rules can drop or re-grade a scanner without turning it off:

finding_rules:
  - match: {category: "scan:gitleaks", path: "tests/fixtures/**"}
    action: drop

mypy earns its place on unguarded-Optional bugs: a dict.get() narrowed to str | None and then dereferenced is a crash a review lens reads straight past, and mypy proves it from the file's own text in seconds. It runs with --ignore-missing-imports --follow-imports=skip, because the sandbox holds only the changed files and everything they import is absent by construction — so it reports what it can prove from a single file, and stays quiet about the rest (untyped code and unresolvable imports produce nothing).

The tools run against the already-fetched file texts in a throwaway directory (never a checkout, never executing PR code), in a subprocess with a scrubbed environment (no proxy or credential variables) and a hard timeout. A tool that isn't installed is skipped silently — install them with pip install lgtmaybe[static-analysis], or rely on whatever is already on PATH. Tool output is treated as untrusted text: redacted and injection-wrapped before it reaches the model. CLI: --static-analysis/--no-static-analysis.

static_analysis:
  enabled: true
  tools: [ruff, bandit, mypy, gitleaks, zizmor]  # default: all supported tools
  min_severity: low            # floor on mapped tool severity (default info)
  tool_min_severity:           # per-tool overrides of the global floor
    ruff: medium               # only medium+ from ruff; bandit keeps `low`
  tool_mode:                   # per-tool overrides of hint vs finding
    gitleaks: hint             # route secrets through the model instead
  # semgrep_rules: .semgrep.yml  # override the bundled MIT pack
  # ast_grep_rules: .ast-grep/  # your own structural rules (skipped when unset)

Default: enabled: false — no subprocess ever runs and behaviour is unchanged.

triage_model

Two-stage model routing so routine PRs don't pay frontier prices while risky ones still get the strong model. When set, this cheap model runs first over the compressed per-file diffs. It skips files that plainly need no review (pure formatting, trivial renames, generated churn) and scores the rest 0–10 by risk; the strong model then does the deep per-lens review only on the survivors, riskiest first. Skipped files are listed in the review summary, and /review full reviews everything on demand.

A deterministic security floor always escalates past triage, whatever the cheap model says: security-relevant paths (auth/crypto/session code, migrations, IaC, CI workflows, dependency manifests), patches carrying security-relevant tokens, files with static-analysis hits, and large hunks. Any triage failure — an unparseable verdict, a provider error — reviews everything.

All three model slots (triage_model, model, reflect_model) resolve through the same provider and credentials, so pointing them all at one ollama model costs nothing. Trade-off: cheaper, faster reviews at the risk of the triage model under-rating a subtle change; the floor and the review-when-unsure prompt bound that risk, but for maximum recall leave triage off. CLI: --triage-model.

triage_model: claude-haiku-4-5   # cheap gatekeeper; unset = no triage

Default: unset (no triage — every file gets the full review, exactly as before).

auto_diagram

Post the change overview as a comment after the review, and refresh it on every push: a description of the change, a High Impact Areas section, a Mermaid flowchart of the components the PR touches, and — when the change alters a run-time flow — a Mermaid sequence diagram of it. One comment, updated in place, never duplicated. /diagram posts the same overview on demand.

This is the switch for the whole comment: with it off, nothing auto-posts besides the review itself.

auto_diagram: false

Default: true. See Generate a change overview.

auto_describe

Head the change overview with a structured description: a suggested title, the change type, a short summary, a per-file walkthrough table, and — when the PR states an intent — a "does it do what it says" check. Its own model call, best-effort: a failure leaves a short note in that slot and never blocks the rest of the comment.

Because it rides the overview comment, it refreshes on every push rather than only on open. /describe still posts a description as its own separate comment, whether or not this is enabled.

auto_describe: false

Default: true.

high_impact

Include the High Impact Areas section in the change overview — the changes a reviewer must not miss, in ten areas: infrastructure, security posture, availability (anything that could cause a production outage), data migrations, backups and recovery, compatibility, observability, dependencies, cost, and compliance.

Deterministic path patterns ground the model call as untrusted hints and floor its output, so a sensitive file (a *.tf, a workflow, a migration, a backup policy, a lockfile) is named even when the model says nothing about it. A failed call degrades to that floor rather than dropping the section, and an empty result says what was checked.

high_impact: false

Default: true. See Generate a change overview.

pr_labels

Attach labels derived from the finished review — no extra model calls:

  • review-effort/1review-effort/5 — a size estimate from the changed lines, so reviewers can gauge the PR at a glance;
  • possible-security-issue — a high/critical finding from the security lens was posted;
  • consider-splitting — the diff spans many unrelated top-level directories.

Labels are reconciled on each run (a stale review-effort/2 is removed when the score changes) and only lgtmaybe's own label families are ever touched. Best-effort: a labelling failure never fails the review.

pr_labels: true

Default: false.

finding_rules

Declarative post-processing applied to findings just before posting — the safe alternative to arbitrary post-processing hooks (rules can only filter or re-grade; no user code ever runs). Each rule has a match (all specified fields must match) and an action; rules apply in order.

Match fields: path (glob, **/-prefix also matches at the repo root), category (the lens that produced the finding — security, correctness, …, or a custom lens id), title_contains (case-insensitive substring), and min_severity (at or above). Actions: drop: true or set_severity.

finding_rules:
  # complexity nits in tests aren't worth a comment
  - match: {path: "tests/**", category: complexity}
    action: {drop: true}
  # documentation findings are informational for this repo
  - match: {category: documentation}
    action: {set_severity: info}

Default: no rules.

summary_template

Custom template for the review summary line, for teams matching a house style. Placeholders: {count} (findings posted), {provider}, {model}, {version} (the lgtmaybe release that produced the review). A template that fails to format falls back to the built-in line.

summary_template: "🤖 {count} finding(s) · {model} · lgtmaybe {version}"

Keep {version} if you can: the same model on the same provider reviews differently across releases, so it is the handle that makes a surprising review traceable to the code that produced it.

Default: unset (the built-in N findings · provider X · model Y · lgtmaybe Z line).

resolve_fixed

Auto-resolve a review conversation once its finding is fixed. On a re-run, when a finding lgtmaybe raised is no longer produced and GitHub marks that thread outdated (the code under it changed), lgtmaybe posts a short ✅ Looks resolved. reply and resolves the conversation. Both conditions must hold, so a thread is never collapsed just because nearby lines shifted. Set it to false to leave conversations for manual resolution.

GitHub posting only — the local CLI review has no conversations to resolve, so it ignores this. Resolving a thread uses GitHub's GraphQL API; the default GITHUB_TOKEN (pull-requests: write, already needed to post the review) is sufficient.

resolve_fixed: false   # leave fixed conversations open for manual resolution

Default: true.

extra_lenses

Define your own review lenses ("BYO skills") that run alongside the built-in categories. Each one fans out as its own focused model call and its findings merge into the same review. A lens needs an id (unique, and not one of the built-in category names) and instructions describing what to look for; a title, plus a worked example (example_diff + example_finding, supplied together) are optional but sharply improve a small model's output.

extra_lenses:
  - id: simplify
    title: Simplify or delete
    instructions: |
      Flag code that should not exist at all. The best code is the code you never
      wrote: prefer the standard library, an existing dependency, or one line over
      a new abstraction. Call out needless wrappers, premature generality, and
      "just in case" code with no caller.
    example_diff: |
      --- a/util.py
      +++ b/util.py
      @@ -4,1 +4,3 @@
       def get_name(user):
      +    name = user.name
      +    return name
    example_finding:
      path: util.py
      line: 5
      severity: low
      title: Needless local variable
      body: The temporary adds nothing; return user.name directly.
      suggestion: "    return user.name"

Lens definitions are trusted config: they go into the system prompt, so only define them in files you control (committed .lgtmaybe.yml or repo skill files), never from PR-author content. See Add a custom review lens for a full walk-through.

Default: none.

lens_paths

Load extra_lenses from separate skill files instead of inlining them — handy for sharing a lens across repos or wiring lgtmaybe into an agent harness. Each entry is a YAML file (one lens, or a list of lenses) or a directory of *.yml / *.yaml lens files. Paths are resolved relative to where lgtmaybe runs (your repo root). Lenses loaded this way are appended to any inline extra_lenses.

lens_paths:
  - .lgtmaybe/skills            # a directory of one-lens-per-file skill files
  - team-lenses/house-style.yml # or a single file

Default: none.

directory_rules

Scope extra review instructions and reference files to part of the repo — what a monorepo needs when payments/** and tests/** deserve different treatment. Each rule has paths (fnmatch globs; a **/ prefix also matches at the repo root, and an empty or omitted list applies the rule everywhere), free-text instructions, and context_files read from the checked-out workspace. A rule reaches every lens reviewing a file it matches, and no other.

directory_rules:
  - paths: ["payments/**", "billing/**"]
    instructions: |
      Money-handling code. Treat rounding, currency-conversion and retry
      changes as high severity, and flag any write path that is not idempotent.

  - paths: ["tests/**"]
    instructions: |
      Test code — do not flag duplication, long functions, or missing
      docstrings here.

  - paths: ["src/**"]
    instructions: Check changes against the architecture described below.
    context_files: [ARCHITECTURE.md]

Context files are redacted like the diff, share max_input_tokens / 8, and are capped at five files per review; a missing or over-budget path is skipped silently. Both the instructions and the context text are trusted config — on pull_request_target they come from the base branch, never the PR head. See Scope review instructions to a directory.

Default: no rules.

CLI flag overrides

Every config field can be overridden at the command line:

lgtmaybe review \
  --provider anthropic \
  --model claude-sonnet-4-6 \
  --min-severity high

Flags take precedence over .lgtmaybe.yml.