# Release Checks & Blast Radius

Commit messages don't tell you what a release really changes; a release check reads the code and proves it, for engineers and reviewers deciding whether to ship.

A release check reads the diff between two versions and reports what it can prove: removed APIs, destructive migrations, committed secrets, vulnerable dependencies, untested code and more. Each finding points at a file and line. The [Release Gate](/docs/release-gate) then applies your `bumpkit.yml` policy to those findings.

## Run a check

The dashboard's **Checks** page has three tabs. **Decisions** lists saved checks and gate outcomes, one row per release. **Run a check** checks a selected open pull request in a connected repository, or tries an example or pasted diff. **Stacks** shows pull requests based on another open pull request's branch.

```bash
bumpkit check --base v1.4.2
bumpkit check --head working-tree
bumpkit check --base v1.4.2 --json
```

`--base` defaults to the latest tag. `--head` defaults to `HEAD`; `working-tree` includes uncommitted work. The command prints the verdict, the suggested version, a summary and each finding, and exits `1` when the verdict is `block`.

List and reopen past checks:

```bash
bumpkit checks list --repository acme/api --limit 20
bumpkit checks view CHECK_ID
```

The same check is available as `POST /v1/checks`, `GET /v1/checks` and `GET /v1/checks/{id}`. See the [CLI Reference](/docs/cli) and [API Reference](/docs/api-reference).

## How a check works

1. Deterministic checkers read the diff. They never guess: a finding is either proven by the diff or not reported.
2. New or upgraded dependencies are looked up in the OSV vulnerability database.
3. Bumpkit's AI review summarizes the release and lists behavior changes the checkers didn't cover. These findings are marked `inference` and never decide the verdict or the gate.
4. Bumpkit suggests the next version from the higher of two signals: the commit messages and the code itself.

The code-based bump is `major` when a deterministic checker proves a break, `minor` when a new route or a new public export appears, `patch` for any other source change, and `none` when no source code changed.

If the diff is too large to read in full, the summary says the check is incomplete.

## Findings

Every finding has these fields:

| Field | Values |
|---|---|
| `checker` | The checker that reported it (see below) |
| `kind` | What it is, for example `breaking` or `secret` |
| `severity` | `high`, `medium`, `low` or `info` |
| `confidence` | `high` or `medium` |
| `evidence` | `deterministic` (proven by the diff) or `inference` (the AI review's reading) |
| `file`, `line_start`, `line_end`, `snippet` | Where it is |
| `fingerprint` | A stable ID used to suppress false positives across checks |

Secrets are masked in snippets.

## Checkers and finding kinds

Checkers skip generated files (lock files, vendored code, `dist/`, `build/`, `.next/`, `*.pb.go`, `*.min.js` and similar) and, except where noted, test files and sample code (`examples/`, `docs/`, `testdata/`, `fixtures/`, `bench/` and similar).

| Checker | Kind | Severity | What it proves |
|---|---|---|---|
| `exports` | `breaking` | high | A public function, type or constant was removed or its signature changed incompatibly, in any [supported language](/docs/supported-languages) (for example Go outside `internal` and `cmd` directories, TypeScript and JavaScript `export`s, Python `def` and `class`, Java `public` members and Rust `pub` items). Moving a symbol within its package, or adding an optional or variadic trailing parameter, is not reported. |
| `routes` | `breaking` | high | An HTTP route was removed: Go `Handle`/`HandleFunc` and router methods, Express-style routers, Python decorators, deleted Next.js `app/**/route` or `pages/api` files, and the common frameworks of every other supported language except C and C++ (Rails, Laravel, Spring, ASP.NET, Phoenix, actix-web, Vapor and more; see [Supported Languages](/docs/supported-languages)). |
| `routes` | `feature` | info | A new HTTP route was added. |
| `migrations` | `data_loss` | high | A migration drops a table or column, or deletes every row (`TRUNCATE`, `DELETE FROM` without `WHERE`). |
| `migrations` | `breaking` | high | A migration renames a column or table, or adds a `NOT NULL` column without a default. |
| `migrations` | `migration` | medium / low / info | Changes a column type or sets `NOT NULL` (medium), creates an index without `CONCURRENTLY` (low), or adds a new migration file (info). Indexes on tables created in the same migration, or in another migration in the same change, are ignored, including `UNIQUE`, `ON ONLY` and `USING` indexes split over several lines. |
| `dependencies` | `dependency` | low / medium / info | A dependency was added (low), changed major version (medium) or removed (info) in `go.mod`, `package.json`, `requirements*.txt`, `Cargo.toml`, `Gemfile`, `*.gemspec`, `composer.json`, Gradle files and version catalogs, `pom.xml` version properties, `*.sbt`, `*.csproj`, `*.props`, `packages.config`, `pubspec.yaml`, `mix.exs`, `Package.swift`, `CMakeLists.txt`, Conan or `vcpkg.json` files. |
| `vulnerabilities` | `vulnerable_dependency` | high / medium / low | A new or upgraded, exactly pinned Go, npm, PyPI or crates.io dependency has known OSV advisories. Critical advisories are reported as high. The detail lists each advisory, its CVE and the version that fixes it. |
| `vulnerabilities` | `vulnerability_check_unavailable` | medium | The vulnerability database didn't answer, so the dependencies couldn't be checked. |
| `secrets` | `secret` | high or medium | A committed credential: private keys, cloud, CI, registry and AI provider tokens (60 rules), database passwords in connection strings, and high-entropy values assigned to credential-named variables. Committing a real environment file (such as `.env`) is also high. |
| `secrets` | `secret_sample` | medium | The same, but in a test, sample, fixture or example file. There only values in a real provider key format count: high-entropy values assigned to credential-named variables and database passwords in connection strings are treated as test fixtures and not reported. |
| `security` | `security_regression` | high / medium | The change is less secure than the base. See [Security regressions](#security-regressions). |
| `sensitive` | `risk_area` | medium | The release changes `authentication`, `authorization`, `billing` or `cryptography` code, detected from path words and corroborated by the file's content. |
| `tests` | `untested` | medium | At least 40 lines of code changed and no test file did. Generated files don't count toward the lines or the files it names: sqlc output, `*_templ.go`, `*.pb.go` and files with a `Code generated ... DO NOT EDIT.` header. |
| `config` | `config` | low | The code starts reading a new environment variable that must be set before deploying, such as `os.Getenv`, `process.env`, `os.environ`, `ENV[...]`, `System.getenv` or `env::var`. |
| `llm` | `behavior` | low / info | A behavior change the model found in a file no checker covered. Always `inference`. |
| `backports` | `missing_backport` | high | A fix shipped in a recent release tag isn't on this branch. Added by `bumpkit gate`; see [Hotfixes](/docs/hotfixes). |
| `blast_radius` | `blast_radius` | low / medium | The change reaches a product area through the call graph. Added by the CLI; see [Blast radius](#blast-radius). |

## Security regressions

The `security` checker compares the head with the base and reports a `security_regression` when the change is less secure than what it replaces. It reads code only: comments, docstrings and text inside strings don't count, except CORS headers and cipher names written as strings. A call wrapped across several lines is read as one statement, so reformatting doesn't look like a change.

It doesn't report:

- Weaknesses that were already there and didn't change, or a setting moved unchanged to another file or function.
- Changes in tests, docs or sample code.
- A guard renamed, moved into middleware, or moved to a base controller, or a route removed along with its guard.
- New anonymous endpoints such as a login page or health check.

Each finding names the file and line, and for a removed protection, the line where it was at the base. A file shows at most five findings of the same kind, with a count of the rest.

High severity, which blocks the release under the default `block_on`:

- Authentication or authorization removed from a route, path, view or action that still exists, or that was renamed in the same change: middleware, a guard decorator, annotation or attribute, or an authorization check.
- Anonymous access allowed on an existing route or action, such as `permitAll()`, `[AllowAnonymous]` or `@Public()`.
- TLS certificate verification turned off, such as `InsecureSkipVerify: true`, `verify=False` passed to an HTTP client, `verify_mode = CERT_NONE`, `rejectUnauthorized: false` or `NODE_TLS_REJECT_UNAUTHORIZED=0`.
- JWTs decoded without checking their signature, such as `verify_signature: False` or `algorithms=["none"]`.
- CORS that allows any website and sends credentials, including reflecting the request's origin.

Medium severity, which asks for review:

- A removed auth guard when the same change adds a different one elsewhere, so a person can confirm the swap.
- Anonymous access or an auth-off flag added in new code.
- CSRF protection removed, turned off or exempted.
- A new route without authentication in a file where its sibling routes have it.
- Roles or permissions widened: an admin or owner role granted, or a wildcard permission or scope.
- Weak cryptography: MD5 or SHA-1 for passwords or signatures, DES, 3DES, RC4 or ECB mode, short RSA keys, a non-cryptographic random generator for tokens or secrets, or a lowered password-hashing cost.
- CORS that allows any website without credentials.

It reads Go, TypeScript, JavaScript, Python, Java, Kotlin, Ruby, PHP and C#. When the CLI sends a blast radius, each security regression also lists the product areas its changed code reaches.

## Verdicts

The check's verdict uses deterministic, unsuppressed findings only:

| Verdict | When |
|---|---|
| `block` | Any high-severity `secret`, `data_loss` or `security_regression` finding |
| `review` | Any other high or medium finding |
| `ship` | Nothing above |

The verdict is the check's own opinion. Whether a release may merge is decided by the [Release Gate](/docs/release-gate), whose outcomes are `pass`, `needs_approval` and `fail`.

## AI review coverage

Every check records what the AI review covered. You see it on the check page, in the pull request comment, in `bumpkit checks view`, in `checks_view` over MCP and as `ai_review` in the API.

| State | What was observed |
|---|---|
| Reviewed (`reviewed`) | The whole of every changed file went to the AI, and its answer listed each one as read |
| Partly reviewed (`partly_reviewed`) | Some files weren't covered. Each is listed with the reason |
| Couldn't review (`could_not_review`) | There is no usable AI answer. The reason says why: the model refused, a content filter stopped it, the answer hit its length limit, the provider didn't answer, the answer couldn't be read or didn't say what it read, the release didn't fit the AI budget, no provider is set up, or the check ran in fast mode. Bumpkit uses none of that answer |

A file is listed as not reviewed when it is binary or generated, was left out or cut off to fit the AI budget, had more than the first 150 lines of its diff left out, or wasn't in the answer's list of files read. The list is what the AI reported, not proof that it understood each file.

The state never changes the verdict or the default gate outcome. Blocking takes a proven finding. To ask for a person's approval when the review was partial or didn't run, add [`ai_review_incomplete`](/docs/bumpkit-yml#require-approval-ai-review-incomplete) to `require_approval`.

File names in the list follow your [retention mode](/docs/data-handling): with no stored file names, the check keeps the state, reason and counts only.

## Finding feedback

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `finding_feedback`.

Mark a finding real or a false positive so Bumpkit learns which checkers to trust:

```bash
bumpkit findings feedback FINDING_ID --false-positive --reason "Test key from the vendor docs"
bumpkit findings feedback FINDING_ID --real
bumpkit findings precision --days 90
```

- A false positive marked on a release decision in the dashboard's **Checks** page suppresses that finding (matched by fingerprint) in later checks of the same repository. Suppressed findings stay visible but no longer count toward the verdict or the gate.
- Feedback sent with the CLI or `POST /v1/findings/{id}/feedback` is recorded for precision but doesn't suppress.
- `bumpkit findings precision` and `GET /v1/precision?days=N` show each checker's measured precision over the last 1 to 365 days (default 90).

The reason is optional and must be under 500 characters.

## PR line annotations

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `pr_annotations`.

With the Bumpkit GitHub App, every deterministic, unsuppressed finding that has a file and line is posted on that line of the pull request as a check annotation:

| Annotation level | When |
|---|---|
| `failure` | High severity, and its kind is in [`block_on`](/docs/bumpkit-yml#block-on) |
| `warning` | Any other high or medium finding |
| `notice` | Everything else |

## Blast radius

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `blast_radius`.

Blast radius shows which areas of your product a change reaches, call by call. It runs locally in the CLI, on your machine or in CI, so your code never leaves CI.

```bash
bumpkit blast-radius
bumpkit blast-radius --base v1.4.0 --head HEAD --json
```

Bumpkit reads the code at `--head` as a graph of what calls what, maps the changed lines since `--base` to the functions, methods and types they touch, and follows their callers to the entry points they reach. Each affected area comes with the call path and the file and line of every call.

`bumpkit check` (except with `--head working-tree`) and `bumpkit gate` run the same analysis and add one `blast_radius` finding per affected area, up to 50.

### Languages

Blast radius reads all 15 [supported languages](/docs/supported-languages): Go, TypeScript and JavaScript, Python, Ruby, PHP, Java, Kotlin, Scala, C#, Dart, Elixir, Rust, C, C++ and Swift. It's limited for C and C++: in large, template-heavy headers about half of the code-graph facts are missed. Release checks are unaffected, because they read diff lines.

Tests, type declarations (`.d.ts`), minified files and directories such as `node_modules`, `vendor`, `dist`, `build`, `testdata`, `tests` and virtualenvs are skipped.

### How areas are named

Each reached file is assigned the first area that matches:

1. Areas listed in `gate.blast_radius.approve_areas`
2. Areas named in `require_approval` rules
3. Bumpkit's built-in risk areas: `authentication`, `authorization`, `billing`, `cryptography`
4. The file's owners in `CODEOWNERS`
5. Otherwise its top-level directory, or the first two segments under a container directory (`internal`, `pkg`, `src`, `lib`, `libs`, `app`, `apps`, `packages`, `services`, `cmd`, `modules`), or `(root)`

### Depth and confidence

```yaml
gate:
  blast_radius:
    max_depth: 4
    approve_areas: [billing, authentication]
```

- `max_depth` bounds how many callers deep Bumpkit follows, from 1 to 10 (default 4).
- A `blast_radius` finding is low severity, or medium when its area is in `approve_areas`. When the change reaches an area in `approve_areas`, the gate asks for an approval.
- Confidence drops to medium when a call in the path was matched by method name rather than resolved exactly. The finding detail marks that hop "matched by method name", and `bumpkit blast-radius` notes how many calls on the path were matched that way (`by_name` and `candidates` in `--json`). Hops marked "at the base" are code that this change has since changed or removed.

The policy is read from `bumpkit.yml` at the base; pass `--policy FILE` to use another. See [`blast_radius`](/docs/bumpkit-yml#blast-radius).

## Regression predictor

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `regression_predictor`.

`bumpkit predict` maps the change's blast radius, then ranks each area it reaches by the repository's own history of incidents, regressions and findings. An area shows a failure probability only where Bumpkit has measured one on your releases; otherwise it shows a likelihood and the evidence, with no percentage.

```bash
bumpkit predict --base v1.4.0 --json
```

It is also available as `POST /v1/predict`. When a regression does ship, see [Rollback & Time Machine](/docs/rollback).

## Behavior-change and hotspot checks

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `behavior_checks`.

These checks catch small changes in behavior that no type checker sees, in every [supported language](/docs/supported-languages) (Elixir skips guard checks). Every finding names the commit that introduced it (`commit_sha`, `commit_subject`).

| Checker | Kind | What it reports |
|---|---|---|
| `guards` | `behavior_change` | A removed guard clause or early return |
| `error_handling` | `behavior_change` | A removed error check, error path or error handling |
| `validation` | `behavior_change` | A removed validation call or check, a loosened check, or a dropped `.validate()`-style call |
| `comparisons` | `behavior_change` | A changed comparison boundary, such as `<` to `<=` |
| `defaults` | `behavior_change` | A changed default value |
| `ordering` | `behavior_change` | Calls that were reordered while the calls around them kept their order |
| `hotspots` | `hotspot` | The release changes a file with a history of reverts and regression fixes |

Behavior findings are low severity with medium confidence. Hotspots come from your git history and are graded high, medium or low by how often the file regressed.

These findings are informational until your policy uses them:

```yaml
gate:
  behavior:
    changes: approve
    hotspots: approve
    severity: medium
    hotspots_min: medium
```

| Key | Values | Meaning |
|---|---|---|
| `changes` | `off`, `approve`, `block` | What the gate does with `behavior_change` findings (default `off`) |
| `hotspots` | `off`, `approve`, `block` | What the gate does with `hotspot` findings (default `off`) |
| `severity` | `high`, `medium`, `low` | Raise matching findings to at least this severity |
| `hotspots_min` | `high`, `medium`, `low` | Only hotspots at or above this severity count (default `medium`) |

`gate.exports` tunes the `exports` checker:

```yaml
gate:
  exports:
    severity: medium
    ignore_paths: ["sdk/legacy/**"]
    max_findings: 100
```

`severity` sets the severity of `exports` findings (`high`, `medium`, `low` or `info`; default `high`), `ignore_paths` takes CODEOWNERS-style patterns (up to 100), and `max_findings` (0 to 1000) caps how many are listed, with one summary finding for the rest.

## Behavioral verification

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `behavioral_verification`.

`bumpkit verify` checks out the base and head into separate worktrees and runs them side by side in your CI, never on Bumpkit's servers:

- the project's test command at both versions (`verify.test`, or detected from `go.mod`, `package.json`, `pyproject.toml`, `pytest.ini` or `setup.py`)
- for Go, each changed exported function called with the same generated inputs at both versions
- for HTTP services (`verify.http.start`), recorded requests (`verify.http.requests`) and requests derived from the OpenAPI spec replayed against both versions, with responses compared after masking times and IDs
- with Playwright, the pages in `verify.browser.pages` compared by text, DOM structure and screenshot

```bash
bumpkit verify
bumpkit verify --base v1.4.0 --head HEAD --no-upload --json
```

Commands run with a time limit, CPU and memory limits (set with `ulimit` where the system supports it) and a safe set of environment variables. Unless `verify.network` is `allow` (the default is `localhost`), outside network access is cut off: on Linux runners that allow user namespaces, the commands run in their own network namespace with only localhost; elsewhere, HTTP proxies point at a dead address and Go, npm and pip downloads are switched off, which stops well-behaved tools but isn't a hard block. The report records which sandbox ran (`netns`, `proxy` or `none`). `verify.timeout` defaults to 10m and can be up to 2h. The settings are read from `bumpkit.yml` at the base, so a pull request can't change how it is verified.

```yaml
verify:
  test: go test ./...
  timeout: 10m
  network: localhost
  http:
    start: ./bin/server
    ready: /healthz
    port_env: PORT
    openapi: docs/openapi.yaml
gate:
  require_checks: [behavior]
```

The report is uploaded signed with your API key and, in GitHub Actions with `id-token: write`, attested by the job's OIDC token. `require_checks: [behavior]` makes the gate require a report for the commit: a regression or failing test fails the gate, and a changed response or return value needs approval. The gate accepts only OIDC-attested reports by default and uses the latest one, so a later report signed only with a key neither replaces nor blocks it. A key's signature only shows that someone with the key uploaded the report. With the GitHub App installed, a report doesn't count if the pull request changed or added the workflow file named by the token, changed anything under `.github/`, or changed a local action or script the default branch's workflow names in `uses: ./` or `run:`, or if its diff was too large to read in full. Merge that change first. An attestation proves which workflow ran on which commit. It doesn't prove the code under test is honest: the workflow still runs the pull request's own code and tests, and anything those call. A report from a pull request's merge commit counts only while that merge commit is the pull request's current one, its head is the commit being gated and its base is your default branch. `verify.require_ci_attestation: false` in your repository's policy opts out and trusts anyone with a key; use it where no GitHub Actions OIDC token is available, such as on GitLab, Bitbucket or Azure DevOps connections. With no App or connection, Bumpkit uses the policy your CI sends, setting included, and the decision says `policy_source: client`. See [`verify`](/docs/bumpkit-yml#verify).

## Regression evidence

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `regression_evidence`.

Prove the tests this change can break ran and passed on the exact commit you're shipping. Bumpkit picks the tests from the change's [blast radius](#blast-radius), and your CI uploads the results.

### Set it up

1. Turn on the rule in `bumpkit.yml` on your default branch:

   ```yaml
   gate:
     require_regression_evidence: true
   ```

2. Give the CI job `permissions: id-token: write`. That lets GitHub Actions prove which repository, commit and workflow run produced the results.

3. After your tests run, upload the results before the gate:

   ```bash
   go test -json ./... > results.json
   bumpkit test attest --file results.json --base main
   ```

   It takes JUnit XML or `go test -json` output. If your tests run in shards, run it once per shard: uploads from the same workflow run add up.

To see the list before you push, run `bumpkit test expected --base main`.

### What's expected

A test is expected when it refers to a changed symbol or to code that calls one, when its own test file changed, when its test file pairs with a changed source file, or when it sits next to a changed fixture. Each test says which symbol or path put it on the list. Tests are matched by name across the call graph, so a shared helper can pull in more tests than strictly need to run, and a test that only reaches the change through a helper in another file can be left out.

### What you'll see

The check page, the gate reason, `bumpkit test evidence` and the `test_evidence` MCP tool show the same list: each expected test as passed, failed or didn't run, and why it's expected. A skipped test counts as didn't run.

| State | Means |
|---|---|
| `passed` | Every expected test passed, or the change touches no code |
| `failed` | An expected test failed |
| `partial` | An expected test didn't run or was skipped |
| `missing` | No results were uploaded for this commit |
| `not_attested` | Results were uploaded, and none are attested by a CI run for this commit. A plain API key doesn't count, and a merge-commit attestation counts only for its own pull request. With the GitHub App, the workflow that ran also has to match the copy on your default branch |
| `unparseable` | An uploaded file couldn't be read |
| `undetermined` | Bumpkit can't work out the tests: a language or file type it can't map, no test reaches the changed code, or the change reaches too much code to list |
| `other_change` | The upload was computed against a different base, so it doesn't cover every changed file. Pass the pull request's base to `--base` |
| `not_enabled` | The policy asks for the rule, and the flag isn't on for this workspace |

Anything but `passed` fails the gate, or needs approval with [`regression_evidence_action: approve`](/docs/bumpkit-yml#regression-evidence-action). Docs, images and data files such as JSON and YAML need no tests. Other file types, such as `.sql` or `.sh`, count as `undetermined`.

## Test evolution

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `test_evolution`.

`bumpkit tests` lists each new function or method, and each changed one that gained branches, that no test reaches, with its exact file and lines. It also finds obsolete tests: tests that refer to symbols this release removes and, with `--results`, tests that never failed and never touched changed code in the last `--releases` releases.

```bash
go test -coverprofile=cover.out -json ./... > results.json
bumpkit tests --coverage cover.out --results results.json --patch tests.patch
```

It reads every [supported language](/docs/supported-languages). With `--coverage` (a Go cover profile), a function counts as tested only when the profile shows its lines run; otherwise a test must refer to it by name. `--patch` writes test skeletons for each gap in the language's usual test framework (for example Go tests, JUnit 5, xUnit, RSpec, PHPUnit, GoogleTest and swift-testing); `--llm` has Bumpkit's AI write the test bodies.

## Intent versus implementation

> **Beta, early access.** A workspace without access gets `403 FEATURE_EARLY_ACCESS` and a link to request it. Flag: `intent_check`.

`bumpkit criteria` extracts acceptance criteria from the pull request description, the GitHub, Jira or Linear issues it links, and any `--spec` files (markdown checklists, Acceptance criteria sections and Given/When/Then lines). With `--llm`, Bumpkit's AI extracts them too. Each criterion is mapped to the changed files, the tests touching them and the behavioral verification results, and reported as covered or not evidenced.

```bash
bumpkit criteria --spec docs/specs/export.md
bumpkit criteria --spec docs/specs/export.md --llm --json
```

Jira needs `JIRA_BASE_URL`, `JIRA_EMAIL` and `JIRA_API_TOKEN`; Linear needs `LINEAR_API_KEY`.

This never passes or fails a release on its own. Add the `intent` rule, and pass the same spec files to `bumpkit gate --spec` (comma-separated), to ask for an approval when a criterion has no evidence; each such criterion is an `intent_unevidenced` finding:

```yaml
gate:
  require_approval:
    - intent
```

See [`require_approval`](/docs/bumpkit-yml#require-approval).
