Skip to content

Ask AI

Ask anything about Destesi — setup, products, APIs.

Powered by Claude. Answers may be wrong — always verify against the docs.

Reference

The complete configuration, command, and environment surface for Destesi Code Review. Every key and value below is drawn from the product’s config parser — unknown versions and out-of-range enum values fail loud rather than being silently ignored.

A single .destesi.yml at your repo root configures everything the Destesi GitHub App does on the repo. Code review reads it from your default branch.

version: 1 # required; only 1 is recognised
defaults: # cross-cutting, optional
ignore:
paths: [...]
branches: [...]
review: # the code-review product block
enabled: true # required to opt in
triggers: { ... }
scope: { ... }
instructions: [...]
output: { ... }
chat: { ... }
model: { ... }
# preview: { ... } # the Preview product's block — configured separately
Key Type Default Notes
version int Required. Must be 1; any other value is rejected with an invalid version error.
defaults.ignore.paths []string [] Glob patterns. Matching files are excluded from review. Additive with review.scope.paths.exclude.
Key Type Default Notes
enabled bool false Must be true to opt the repo in. Without it the block is a no-op.

The remaining keys are grouped by concern, below.

When the reviewer acts on a PR. If you set none of the three, the defaults below apply.

Key Default Effect when true
on_open true Initial review fires on opened, reopened, and ready_for_review.
on_push false Re-review on every push. Off by default so chatty PRs don’t burn budget.
on_command true Respond to @destesi commands regardless of on_push.
triggers:
on_open: true
on_push: false
on_command: true

Which PRs, and which files within them, are eligible. Triggers say when; scope says which.

scope:
drafts: false # skip draft PRs (default)
base_branches: ["main"] # empty = all base branches
paths:
include: [] # empty = all files (still subject to ignore lists)
exclude: [] # additive to defaults.ignore.paths

Path priority: ignore beats include. A file matching defaults.ignore.paths or scope.paths.exclude is reported as ignored; a file not in a non-empty include list is reported as out_of_scope. Both surface in the summary’s “skipped files” footer.

Per-path natural-language guidance fed to the model. A list of { paths, guidance } entries; multiple matches stack in declaration order, so a file can pick up several instruction blocks.

instructions:
- paths: ["**/*.go"]
guidance: |
Wrap returned errors with %w. Flag goroutines without a context.
- paths: ["api/internal/**"]
guidance: |
Be strict about auth boundaries on control-plane routes.
Field Type Notes
paths []string Glob patterns. The instruction applies when the changed file matches any glob in the list.
guidance string Free-form guidance prepended to the per-file prompt for matching files.

What the reviewer writes back to GitHub.

Key Type Default Allowed values Notes
summary bool true Top-level walkthrough comment, edited in place on re-runs.
inline bool true Per-hunk comments, batched into one PR review.
check_run bool true The “Destesi Review” entry in the Checks tab.
nits string collapsed surface · collapsed · hide How nit-severity comments render. hide drops them entirely from the inline review.
fail_check_on string severe never · severe · any When the Check goes red. See below.
approve_on string never never · clean clean submits a GitHub “Approve” event when the review finds zero inline comments.

How fail_check_on maps to the Check conclusion:

Policy Highest finding severity Check conclusion
never anything success
severe (default) none success
severe nit or warn neutral
severe severe failure
any none success
any any finding failure

A neutral Check does not block merges; only failure does. The reviewer never submits a formal “Request changes” — gating is the Check’s job.

chat:
allowed_associations: [OWNER, MEMBER, COLLABORATOR]
commands: [review, summary, ignore, help]
Key Type Default Allowed values
allowed_associations []string [OWNER, MEMBER, COLLABORATOR] OWNER · MEMBER · COLLABORATOR · CONTRIBUTOR · FIRST_TIME_CONTRIBUTOR · NONE (GitHub’s author_association values)
commands []string [review, summary, ignore, help] Any subset of the four commands.

LLM selection and cost budgets. The reviewer runs two kinds of call: one summary call per PR, and one inline call per changed file (the fan-out). Both default to the same model — see below for why. Omit the whole model block to use platform defaults.

model:
summary:
provider: bedrock # anthropic | bedrock | platform
name: global.anthropic.claude-sonnet-4-5-20250929-v1:0
inline:
provider: bedrock
name: global.anthropic.claude-sonnet-4-5-20250929-v1:0
max_files: 25 # changed files sent to the inline pass
max_file_bytes: 16384 # head content per file, per prompt
max_hunk_bytes: 131072 # diff hunks per file, per prompt
max_diff_bytes: 2097152 # whole-PR diff accepted from GitHub
max_input_tokens: 36864 # per-file prompt budget
concurrency: 4 # max parallel inline calls per review
Key Type Default Notes
model.summary.provider string bedrock anthropic · bedrock · platform (or empty). platform lets the environment decide.
model.summary.name string global.anthropic.claude-sonnet-4-5-20250929-v1:0 Provider-specific model identifier. Only filled when both provider and name are omitted.
model.inline.provider string bedrock Same allowed values as summary.provider.
model.inline.name string global.anthropic.claude-sonnet-4-5-20250929-v1:0 Provider-specific model identifier. Only filled when both provider and name are omitted.
model.max_files int 25 Changed files sent to the inline pass. Ceiling 50.
model.max_file_bytes int 16384 (16 KiB) One file’s head content inside one inline prompt.
model.max_hunk_bytes int 131072 (128 KiB) One file’s diff hunks inside one inline prompt.
model.max_diff_bytes int 2097152 (2 MiB) Whole-PR unified diff accepted from GitHub before parsing.
model.max_input_tokens int derived Per-file prompt budget in tokens, enforced as an extra clamp over max_file_bytes + max_hunk_bytes. With the default byte budgets that works out to ~36,864 tokens.
model.concurrency int 4 Max parallel inline calls (and file-content fetches) per review. Ceiling 8.

Why the summary model is not the expensive one: the summary pass never sees your source or your diff. It reads the one-sentence digests the inline pass produced and writes at most 600 words of markdown, so it runs on Sonnet like the inline pass does. Review quality lives in the inline pass, which is the one that actually reads the code. Set model.summary.name if you want a heavier model back.

A single review covers at most 25 changed files by default; files beyond that cap are reported as file_limit in the summary footer and are not reviewed at all. See Some files were skipped for the full set of drop reasons.

paths lists everywhere in the file use the same glob dialect:

Pattern Matches
* Any run of characters except /
? Exactly one character except /
** Any sequence including / — zero or more path segments
**/*.go Every Go file at any depth
api/internal/** Anything under api/internal/
**/generated/** Anything under any generated/ directory

Per-path instructions are the single biggest lever on review quality. A few patterns that work, drawn from real configs:

  • Be concrete and verifiable. “Write good Go” gives the model nothing. “Wrap returned errors with %w. Reject silent error swallowing.” gives it a clear yes/no test per rule.
  • Lead with the highest-impact rule. The first rule in a guidance block is the most reliably applied. Put your “if you see this, scream” rule first.
  • Anchor to your invariants. “This package owns auth boundaries — never approve a route without its auth middleware” is a check the model can run against any diff.
  • Stack general → specific. Broad rules under broad globs (**/*.go), surgical rules under narrow globs (api/internal/auth/**). They compose at review time.
  • Mark style as nits explicitly. If you don’t say “(nit)”, the model decides severity itself and often over-warns on taste.

Patterns that go wrong:

  • Walls of text. Past roughly ten rules in one guidance block, marginal rules get dropped. Split by glob.
  • Conflicting rules across overlapping globs. If **/*.go and a narrower glob disagree, say so explicitly: “(overrides the general rule)”.
  • Asking for repo-wide reasoning. The model sees the changed file and the diff, not the whole repo. “Make sure no other caller broke” is wishful — write a test instead.

Repo collaborators drive the reviewer by mentioning @destesi at the start of a line in any PR comment.

Command Where What it does
@destesi review Top-level PR comment Re-runs the review on the latest recorded commit.
@destesi summary Top-level PR comment Reposts the latest review’s summary.
@destesi ignore [reason] Reply to one of the bot’s line comments Stops flagging that rule on that path going forward. Optional reason is stored.
@destesi help Anywhere Posts the command list. A bare @destesi does the same.

Notes on behaviour:

  • The mention must be at the start of a line (after optional whitespace) so it doesn’t trigger on quoted text from earlier comments. @destesi-bot and other trailing-identifier forms do not trigger.
  • @destesi review re-runs against the head SHA from the PR’s most recent review. If the reviewer has never seen the PR (no recorded head SHA), it asks you to push a commit or wait for the auto-review.
  • How @destesi ignore works: every inline comment the reviewer posts ends with a hidden marker, e.g. <!-- destesi:rule_fingerprint=a1b2c3d4 path=api/foo.go -->. When you reply @destesi ignore, the reviewer reads that marker off the parent comment and persists an override keyed by (installation, repo, path, rule_fingerprint). The override survives re-reviews. If you reply to something that isn’t one of its own line comments, it tells you it can’t read the rule id.

The reviewer deliberately does not answer free-form questions, submit “Request changes”, or push commits to your repo.

@destesi review re-runs the review against the pull request’s current head commit. You may order 3 re-runs per commit (a deployment may raise this as far as 20). Past that, the reviewer replies on the pull request saying it has already re-reviewed this commit the maximum number of times — it does not fail quietly, because a command that appears to do nothing just gets posted again.

The budget exists because re-reviewing an unchanged commit buys the same answer at the same cost. Push a commit and it resets: a new head SHA is new work. Automatic reviews (on_open, on_push) don’t consume the budget — only the command does.

If you run Destesi yourself, code review is a standalone controller process plus a thin slice of routes on the Destesi API. The controller polls the API for queued reviews, fetches diffs through the GitHub App, runs the model passes, and reports results back; the API owns the GitHub App credentials and mints short-lived installation tokens on demand, so the controller never holds long-lived GitHub credentials.

Variable Default Purpose
REVIEW_ENABLED unset Kill switch. Empty → review is invisible to the GitHub webhook path (no PR-driven reviews, no chat dispatch). The controller-facing routes stay mounted regardless so in-flight reviews can drain after a flip-off.
CONTROLLER_SECRET required Bearer token gating the controller-facing routes. Must match the controller. Shared with the Preview controller.
Variable Default Purpose
DESTESI_API_URL http://localhost:9090 Base URL the controller calls back to.
CONTROLLER_SECRET required Must match the API.
REVIEW_POLL_INTERVAL 5s How often the controller polls for new work.
REVIEW_WORK_LIMIT 50 Max reviews fetched per poll.

Code review uses the same Destesi GitHub App as Preview. Beyond what Preview needs, it requires:

  • Permission: Issues (read) — to receive issue_comment events on PRs (needed for chat commands). Pull Requests (write), Checks (write), and Contents (read) are already in place from Preview.
  • Webhook events: issue_comment and pull_request_review_comment, in addition to pull_request.

After updating the App’s registration, existing installations must accept the new permissions before chat events arrive.