Skip to content
Documentation menu

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
# .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

Top-level configuration keys
KeyTypeDefaultWhat it does
profilestring"balanced"How much compute a review spends. quiet = 1 find pass, balanced = 3, thorough = 5. Accepts: quiet, balanced, thorough.
severity_thresholdstring"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.
reviewobjectWhen SuperDiffs reviews, and how much it may post.
pathsobjectWhich files are reviewed, and extra guidance for specific areas.
toolsobject25 entriesDeterministic analysers to run in the sandbox, by name. Unlisted tools keep their default state.
excluded_categoriesarray 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.
knowledgeobjectLearnings and repo convention files.

severity_threshold accepts friendlier aliases as well as the canonical names, because “high / medium / low” is what people type:

Severity aliases
You writeIt means
blockercritical
highmajor
mediumminor
lownit

review

When we review, and how much we are allowed to say when we do.

review keys
KeyTypeDefaultWhat it does
review.autobooleantrueReview automatically on pull request open/ready.
review.draftsbooleanfalseAlso review draft pull requests.
review.incrementalbooleantrueReview only the new commits on push instead of the whole diff.
review.auto_pause_after_commitsinteger5Pause automatic reviews after this many consecutive pushes without a human reply. 0 disables the pause. Range: 0–1000.
review.base_branchesarray of string[]Only review pull requests targeting these base branches. Empty means every branch. Range: up to 100 entries.
review.ignore_title_keywordsarray of string"wip", "[skip review]"Skip pull requests whose title contains any of these (case-insensitive) strings. Range: up to 100 entries.
review.ignore_authorsarray of string[]Skip pull requests opened by these logins. Range: up to 500 entries.
review.file_change_limitinteger400Refuse 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_commentsinteger20Maximum 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.

paths keys
KeyTypeDefaultWhat it does
paths.filtersarray 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.instructionsarray 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

paths.instructions entry keys
KeyTypeDefaultWhat it does
paths.instructions[].pathstringgitignore-style glob the guidance applies to.
paths.instructions[].guidancestringWhat 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.

knowledge keys
KeyTypeDefaultWhat it does
knowledge.learningsbooleantrueApply learnings captured from review feedback.
knowledge.learnings_scopestring"repo"Where learnings are shared. "auto" means repo-scoped for public repos and org-scoped for private ones. Accepts: repo, org, auto.
knowledge.ingestarray 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.

Analyser defaults
DefaultTools
Enabledactionlint, buf, clippy, detekt, eslint, gitleaks, golangci-lint, hadolint, opengrep, osv-scanner, oxlint, phpstan, rubocop, ruff, shellcheck, squawk, swiftlint, trivy, zizmor
Disabledcheckov, markdownlint, sqlfluff, tflint, trufflehog, yamllint

How layers merge

Three layers, applied in this order, each overriding the one before it:

Configuration layers
LayerSet byPrecedence
Organisation defaultsDashboard, by an org adminLowest — but its caps bind
Repository .superdiffs.yamlThe base branch of the pull requestOverrides the org
Directory .superdiffs.yamlA subdirectory containing changed files; outermost firstHighest

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.
.superdiffs.yaml — allow-list example
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-edited

Filters 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.

.superdiffs.yaml — instructions
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
# yaml-language-server: $schema=https://superdiffs.com/schema.v1.json

The 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 (annotated)
# .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:

effective configuration
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.md

The 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.