Docs

Capabilities

Release Checks & Blast Radius ​

What a Bumpkit release check proves, every checker and finding kind, verdicts, feedback, PR annotations and blast radius through the code graph.

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

Terminal
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:

Terminal
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 and 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 (for example Go outside internal and cmd directories, TypeScript and JavaScript exports, 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).
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.
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.
blast_radius blast_radius low / medium The change reaches a product area through the call graph. Added by the CLI; see 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, 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 to require_approval.

File names in the list follow your retention mode: 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:

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

Terminal
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: 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.

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.

Terminal
bumpkit predict --base v1.4.0 --json

It is also available as POST /v1/predict. When a regression does ship, see Rollback & Time Machine.

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 (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
Terminal
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.

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, 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:

    Terminal
    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, 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.

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

It reads every supported language. 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.

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

↑↓ to move, Enter to open