Configuration
Everything SuperDiffs does is controlled by one optional file. Having no file at all is a supported, and common, configuration — these are the keys for when the defaults are not what you want.
Where the file lives
Commit .superdiffs.yaml at the root of the repository. It is read from the base branch of each pull request, not from the head — otherwise a pull request could change the rules it is reviewed under, which is a hole rather than a feature.
The same filename in a subdirectory is a directory override, applying to pull requests that touch files beneath it. Organisation-level defaults are set in the dashboard and sit under both.
A starter file
The four settings that matter on day one. Copy it, commit it, adjust after a week of real reviews rather than in advance.
# .superdiffs.yaml — commit this at the repository root.
# Everything is optional. These are the four knobs that matter on day one.
profile: balanced # quiet (1 pass) | balanced (3) | thorough (5)
severity_threshold: minor # critical | major | minor | nit
review:
drafts: false # don't review while the PR is a draft
max_comments: 15 # lowest severities are dropped first
paths:
filters:
- "!**/dist/**"
- "!**/*.gen.ts"
Top-level keys
| Key | Type | Default | What it does |
|---|---|---|---|
profile | string | "balanced" | How much compute a review spends. quiet = 1 find pass, balanced = 3, thorough = 5. Accepts: quiet, balanced, thorough. |
severity_threshold | string | "minor" | Findings below this severity are not posted. Defaults to the profile minimum (quiet: major, balanced: minor, thorough: nit). blocker/high/medium/low are accepted aliases for critical/major/minor/nit. Accepts: critical, major, minor, nit, blocker, high, medium, low. |
review | object | — | When SuperDiffs reviews, and how much it may post. |
paths | object | — | Which files are reviewed, and extra guidance for specific areas. |
tools | object | 25 entries | Deterministic analysers to run in the sandbox, by name. Unlisted tools keep their default state. |
excluded_categories | array of string | "denial-of-service", "rate-limiting", "resource-exhaustion", "input-validation-without-impact", "open-redirect" | Finding categories dropped before posting. Replaces (does not extend) the default list. Range: up to 100 entries. |
knowledge | object | — | Learnings and repo convention files. |
severity_threshold accepts friendlier aliases as well as the canonical names, because “high / medium / low” is what people type:
| You write | It means |
|---|---|
blocker | critical |
high | major |
medium | minor |
low | nit |
review
When we review, and how much we are allowed to say when we do.
| Key | Type | Default | What it does |
|---|---|---|---|
review.auto | boolean | true | Review automatically on pull request open/ready. |
review.drafts | boolean | false | Also review draft pull requests. |
review.incremental | boolean | true | Review only the new commits on push instead of the whole diff. |
review.auto_pause_after_commits | integer | 5 | Pause automatic reviews after this many consecutive pushes without a human reply. 0 disables the pause. Range: 0–1000. |
review.base_branches | array of string | [] | Only review pull requests targeting these base branches. Empty means every branch. Range: up to 100 entries. |
review.ignore_title_keywords | array of string | "wip", "[skip review]" | Skip pull requests whose title contains any of these (case-insensitive) strings. Range: up to 100 entries. |
review.ignore_authors | array of string | [] | Skip pull requests opened by these logins. Range: up to 500 entries. |
review.file_change_limit | integer | 400 | Refuse to review pull requests touching more files than this, and post a summary instead. Org policy may lower it; it can never be raised above the org value. Range: 1–10000. |
review.max_comments | integer | 20 | Maximum line comments per review; the lowest-severity findings are dropped first. 0 posts the summary only. Org policy may lower it. Range: 0–200. |
max_comments: 0 is valid and useful: it posts the summary and the check run and no line comments, which is how teams trial the tool without touching the review thread.
paths
Which files are reviewed, and what a reviewer of those files must know.
| Key | Type | Default | What it does |
|---|---|---|---|
paths.filters | array of string | [] | gitignore-style globs. A "!" prefix excludes. If any non-"!" pattern is present the list becomes an allow-list. Among matching patterns the last one wins. Range: up to 500 entries. |
paths.instructions | array of objects | [] | Repo-specific review rules, applied to files matching each glob and injected into the review prompt. Range: up to 200 entries. |
Each instruction entry
| Key | Type | Default | What it does |
|---|---|---|---|
paths.instructions[].path | string | — | gitignore-style glob the guidance applies to. |
paths.instructions[].guidance | string | — | What reviewers of these files must check. |
knowledge
Learnings are the preferences we pick up from your replies — “we do not use exceptions for control flow here” — and the convention files your repository already has.
| Key | Type | Default | What it does |
|---|---|---|---|
knowledge.learnings | boolean | true | Apply learnings captured from review feedback. |
knowledge.learnings_scope | string | "repo" | Where learnings are shared. "auto" means repo-scoped for public repos and org-scoped for private ones. Accepts: repo, org, auto. |
knowledge.ingest | array of string | ".cursorrules", "CLAUDE.md", ".github/copilot-instructions.md" | Repo-relative convention files folded into review context. Paths may not be absolute or contain "..". Range: up to 50 entries. |
ingest paths must be repository-relative. An absolute path or one containing .. is rejected by the schema: these files are read inside the sandbox with your repository checked out, so a path that escapes the worktree would turn configuration into arbitrary file read.
Analysers
Deterministic tools run in the sandbox alongside the model. Their findings are the trusted tier — a linter rule that fires is a fact, not a judgement — so they skip the voting stage and go straight to ranking. Unlisted tools keep their default state; you only name the ones you are changing.
| Default | Tools |
|---|---|
| Enabled | actionlint, buf, clippy, detekt, eslint, gitleaks, golangci-lint, hadolint, opengrep, osv-scanner, oxlint, phpstan, rubocop, ruff, shellcheck, squawk, swiftlint, trivy, zizmor |
| Disabled | checkov, markdownlint, sqlfluff, tflint, trufflehog, yamllint |
How layers merge
Three layers, applied in this order, each overriding the one before it:
| Layer | Set by | Precedence |
|---|---|---|
| Organisation defaults | Dashboard, by an org admin | Lowest — but its caps bind |
| Repository .superdiffs.yaml | The base branch of the pull request | Overrides the org |
| Directory .superdiffs.yaml | A subdirectory containing changed files; outermost first | Highest |
Except for caps, which only go down
review.file_change_limit and review.max_comments are caps, and the effective value is the smallest any layer sets. A repository can tighten an organisation limit; it can never raise one. Everything else is last-writer-wins.
Lists accumulate
paths.filters and paths.instructions concatenate across layers rather than replacing. Because filter matching is last-match-wins, a directory layer can still re-include something an organisation policy excluded — which is the behaviour you want when one team genuinely does maintain the vendored tree everyone else ignores.
excluded_categories is the exception: it replaces rather than extends, so setting it gives you exactly the list you wrote.
Path filters, exactly
Gitignore-style globs, with three rules that decide every case:
- A leading
!excludes. A bare pattern includes. - As soon as one bare pattern exists anywhere in the merged list, the list becomes an allow-list: a file matching no pattern at all is not reviewed. This surprises people, so it is worth reading twice.
- Among the patterns that do match, the last one wins.
paths:
filters:
- "src/**" # a bare pattern: now only matches are reviewed
- "!src/**/*.gen.ts" # ...except generated output
- "src/api/client.gen.ts" # ...except this one, because it is hand-editedFilters are matched against the path in the new tree. Skips built into the product — lockfiles, minified output, binaries — apply regardless of filters; see limits and skips.
Path instructions
The highest-value key in the file, and the most underused. An instruction is a rule a reviewer cannot infer from the diff — a convention, an invariant, a thing that went wrong once. It is injected into the review context for files matching the glob.
paths:
instructions:
- path: "src/api/**"
guidance: >-
Every handler must be org-scoped. Flag any query that reads or writes
without an org_id predicate, including through a repository helper.
- path: "**/*.sql"
guidance: "Migrations must be reversible and must not lock a table on write."
- path: "src/billing/**"
guidance: >-
Money is integer minor units. Flag any float arithmetic on an amount,
and any comparison that assumes two currencies are the same.Editor autocompletion
The schema is published as JSON Schema. Add the modeline and your editor completes and validates the file as you type:
# yaml-language-server: $schema=https://superdiffs.com/schema.v1.jsonThe full annotated file
Every key in one file, with comments. This is a surface reference rather than a starting point — it is parsed by our own test suite on every run, so it cannot drift into something the product would reject.
# .superdiffs.yaml — every key is optional; delete what you don't need.
# yaml-language-server: $schema=https://superdiffs.com/schema.v1.json
# How hard we think about each PR: quiet (1 pass) | balanced (3) | thorough (5).
profile: balanced
# Findings below this are not posted. Defaults to the profile minimum.
# critical | major | minor | nit (blocker/high/medium/low also accepted)
severity_threshold: minor
review:
auto: true # review on PR open / ready-for-review
drafts: false # ...but not while it is a draft
incremental: true # on push, review only the new commits
auto_pause_after_commits: 5
base_branches: ["main", "release/*"]
ignore_title_keywords: ["wip", "[skip review]"]
ignore_authors: ["dependabot[bot]", "renovate[bot]"]
file_change_limit: 400 # bigger PRs get a summary and an opt-in prompt
max_comments: 20 # lowest severities are dropped first
paths:
# gitignore-style globs. "!" excludes. Add a non-"!" pattern and the list
# becomes an allow-list. Among matching patterns, the last one wins.
filters:
- "!**/dist/**"
- "!**/*.gen.ts"
- "!**/vendor/**"
instructions:
- path: "src/api/**"
guidance: >-
All handlers must be org-scoped. Flag any query that reads or writes
without an org_id predicate.
- path: "**/*.sql"
guidance: "Migrations must be reversible and must not lock a table on write."
# Deterministic analysers run in the sandbox. Unlisted tools keep their default.
tools:
eslint: true
ruff: true
gitleaks: true
opengrep: true
markdownlint: false
# Replaces the default exclusion list.
excluded_categories:
- denial-of-service
- rate-limiting
- open-redirect
knowledge:
learnings: true
learnings_scope: auto # repo for public repos, org for private ones
ingest:
- CLAUDE.md
- .cursorrules
- .github/copilot-instructions.md
Checking what is actually in force
Comment @superdiffs config on any pull request and we post the resolved configuration after all layers have merged, along with the hash recorded on every review. With no configuration anywhere, that output is:
SuperDiffs effective configuration (hash 3db61cd50308)
profile balanced — 1 find pass(es), medium effort
severity threshold minor — posts critical, major, minor; suppresses nit
review
auto review on
draft PRs skipped
incremental on — new commits only
auto-pause after 5 pushes without a reply
base branches any
ignore titles wip, [skip review]
ignore authors none
file change limit 400 files
max comments 20 per review
paths
filters none — every changed file is reviewed
instructions none
tools enabled actionlint, buf, clippy, detekt, eslint, gitleaks, golangci-lint, hadolint, opengrep, osv-scanner, oxlint, phpstan, rubocop, ruff, shellcheck, squawk, swiftlint, trivy, zizmor
tools disabled checkov, markdownlint, sqlfluff, tflint, trufflehog, yamllint
excluded categories denial-of-service, rate-limiting, resource-exhaustion, input-validation-without-impact, open-redirect
knowledge
learnings on — repo scope
ingest .cursorrules, CLAUDE.md, .github/copilot-instructions.mdThe hash is logged on every review record. If behaviour changed on a Tuesday and the hash changed on the same Tuesday, you have your answer.