Skip to main content

Breakcheck

Dependabot tells you a new version exists. Breakcheck tells you whether the calls your code actually makes behave differently.

CI PyPI Python License

Breakcheck is a deterministic behavioral comparison tool for Python. It answers two bounded questions:

  • Did a dependency upgrade change the behavior of supported calls already present in this repository?
  • Did a code revision change the behavior of fixture-bound functions that were expected to remain stable?

Breakcheck compares observations. It does not guess intent, declare code correct, replace a test suite, or decide whether a change should ship. Calls it cannot lawfully exercise are reported as NOT_EXERCISED, never counted as safe.

Breakcheck report preview

Install

python -m pip install breakcheck

Breakcheck supports Python 3.10 through 3.13 on Linux and macOS and has no runtime package dependencies.

See a real result in one command

breakcheck demo --output-root .breakcheck/demo

The offline demo builds two small local package wheels, runs the installed CLI, detects a real behavioral difference, writes a report and evidence bundle, and verifies both. It does not contact a package index.

For the longer packaging 21.3 to 22.0 demonstration used by the release tests, run:

sh examples/run_demo.sh

Where Breakcheck fits

Neighbor Question it answers Breakcheck's question
Unit and integration tests Does known expected behavior still pass? Did these two observations differ?
Snapshot testing Does output match an approved snapshot? Did this specific dependency or revision change behavior?
Static analysis Does this code match a rule or risk pattern? Does supported code behave differently when replayed?
Traffic replay What did recorded production requests do? What can be compared without production traffic?
AI-generated tests What should this code do, as inferred by a model? What changed, without guessing intent?

Breakcheck complements these tools. Its useful property is independence: a coding tool may propose a change or fixture, while deterministic replay and comparison decide the observed result.

Dependency upgrades

Breakcheck holds your repository constant and varies one dependency:

same repository calls x current wheel vs proposed wheel -> behavioral difference

Quick start

Start at the root of the repository you want to analyze. Install the current dependency version in the interpreter running Breakcheck, then prepare an explicit local wheelhouse:

python -m pip install 'attrs==23.2.0'
mkdir -p wheelhouse .breakcheck/results
python -m pip download --only-binary=:all: --dest wheelhouse 'attrs==23.2.0'
python -m pip download --only-binary=:all: --dest wheelhouse 'attrs==24.2.0'

Download exact versions in separate commands. Asking pip download to resolve two versions of the same distribution in one command can produce a dependency-resolution error.

Run the comparison:

breakcheck attrs@24.2.0 \
  --wheelhouse wheelhouse \
  --output .breakcheck/results/report.json \
  --evidence .breakcheck/results/evidence.json \
  --coverage-report .breakcheck/results/coverage.json \
  --json \
  --ci

Breakcheck creates fresh replay environments, installs only from the supplied wheelhouse, runs admitted calls twice in each environment, and compares normalized observations. Environments created under the default temporary runtime are removed when the command finishes, including after a failure. Supplying --runtime-root preserves that explicit runtime for operator-managed retention and cleanup.

Treat every wheel as executable input. Obtain wheels from a source you trust and preserve expected hashes in your own supply-chain records.

Supported call surface

Breakcheck is strongest on pure, value-in/value-out library calls: parsing, serialization, validation, encoding, schema coercion, and deterministic numeric or string transformations.

The default static path supports:

  • direct imports and statically attributable package calls;
  • Python literal arguments;
  • bounded constant expressions and f-strings;
  • safe, single-assignment module constants;
  • nested calls to the target package or a small fixed allowlist of pure standard-library modules;
  • normalizable values, exceptions, mappings with string keys, sequences, sets, finite numbers, strings, and bytes.

It does not pretend to exercise network clients, arbitrary filesystem work, stateful object graphs, dynamic dispatch, unbounded computation, or rich return objects without an explicit projection. Those cases remain visible in the coverage report.

Coverage diagnostics

Every discovered candidate reaches exactly one terminal bucket:

  • EXERCISED
  • G1_NOT_DISCOVERABLE
  • G2_NONLITERAL
  • G3_UNNORMALIZABLE
  • G4_IMPURE

--coverage-report writes the machine-readable candidate inventory, reason codes, reason details, and argument provenance. A run that exercises nothing exits nonzero unless a human explicitly passes --allow-empty; that choice is recorded in the artifact.

Generate fixture suggestions

When source arguments cannot be resolved statically, generate a reviewable skeleton without creating replay environments:

breakcheck attrs@24.2.0 \
  --suggest-fixtures breakcheck.fixtures.toml

The generated file identifies each unresolved call by repository-relative file, line, column, API, and nearby source. A human or coding tool fills in concrete expressions and changes fixture_authored_by from unknown to human or agent.

Example:

schema_version = 1

[[binding]]
fixture_authored_by = "agent"
file = "src/app/serialize.py"
line = 42
column = 8
api = "attrs.asdict"
args = ["Point(1, 2)"]
kwargs = {}
setup = """
import attrs
@attrs.define
class Point:
    x: int
    y: int
"""

Review the fixture diff, then run:

breakcheck attrs@24.2.0 \
  --wheelhouse wheelhouse \
  --fixtures breakcheck.fixtures.toml \
  --fixture-policy allow \
  --output .breakcheck/results/report.json \
  --evidence .breakcheck/results/evidence.json \
  --coverage-report .breakcheck/results/coverage.json \
  --ci

Fixture expressions and setup code execute inside the same best-effort isolation as dependency calls. They are trusted executable input, not data-only configuration.

Project rich return values

A fixture may reduce a rich result to a stable, normalizable value:

[[binding]]
fixture_authored_by = "human"
file = "src/app/load.py"
line = 12
column = 8
api = "pandas.read_csv"
args = ["io.StringIO('a,b\\n1,2\\n')"]
kwargs = {}
setup = "import io"
projection = "(list(outcome.columns), outcome.shape, outcome.to_dict('list'))"

Projected results use explicit verdicts: IDENTICAL_UNDER_PROJECTION and CHANGED_UNDER_PROJECTION. They never imply that the complete rich object was identical.

Code revisions

Breakcheck can also hold the environment constant and vary repository code:

same fixture and environment x base revision vs head revision -> behavioral difference

This mode is for claims such as "this refactor preserves behavior." It cannot establish absolute correctness, judge intentional changes as bad, or verify new functions that have no baseline.

Revision commands create detached Git worktrees under an absent runtime path. They never check out over the user's working tree, touch the index, or stash changes. Worktrees are removed on success and on handled failure.

Bind functions to inputs

Revision fixtures use the same file format, but point at function definitions:

schema_version = 1

[[binding]]
fixture_authored_by = "human"
file = "src/app/pricing.py"
line = 10
column = 0
api = "app.pricing.compute_total"
args = ["100", "0.15"]
kwargs = {}

For strict separation, commit fixtures against the base revision before making the code change. Breakcheck records fixture authorship, fixture hash, source revision, and whether the fixture predates the change.

Capture a baseline

breakcheck freeze \
  --revision HEAD \
  --fixtures breakcheck.fixtures.toml \
  --output .breakcheck/baseline.json

freeze records repeated normalized observations, source-tree identity, fixture identity, Python version, and platform. Dirty working trees are refused because silently omitting uncommitted changes would produce a misleading baseline.

Compare two revisions

Compare explicit revisions:

breakcheck diff \
  --base main \
  --head feature/refactor \
  --fixtures breakcheck.fixtures.toml \
  --strict-separation \
  --output .breakcheck/revision-report.json \
  --evidence .breakcheck/revision-evidence.json

Or compare against a verified baseline artifact:

breakcheck diff \
  --baseline .breakcheck/baseline.json \
  --head HEAD \
  --fixtures breakcheck.fixtures.toml \
  --strict-separation \
  --output .breakcheck/revision-report.json \
  --evidence .breakcheck/revision-evidence.json

By default, Breakcheck selects top-level and class-level functions whose normalized signatures, bodies, or relevant non-callable module or class context changed. It also surfaces additions, removals, ambiguous definitions, and signature drift so structural changes cannot disappear from the comparison. Use a repeatable --target module.path:symbol to select targets explicitly. Missing baselines, import asymmetry, and unexercised targets remain distinct fail-closed results.

To disclose fixture retuning, pass an earlier verified revision report:

breakcheck diff \
  --base main \
  --head HEAD \
  --fixtures breakcheck.fixtures.toml \
  --previous-report .breakcheck/previous-revision-report.json \
  --output .breakcheck/revision-report.json

When a changed fixture turns a prior CHANGED result into IDENTICAL for the same base and target, the new report emits FIXTURE_REVISED_AFTER_FAILURE. The event is visible and integrity-bound but does not automatically block the run because the original fixture may have been wrong. Breakcheck never infers this history from a mutable local cache; the prior report must be supplied explicitly.

Adjudicate a preservation claim

A claim file lists exactly what the change asserts it preserved:

schema_version = 1
claim = "behavior_preserved"
base_revision = "0123456789abcdef0123456789abcdef01234567"

[[target]]
symbol = "app.pricing:compute_total"

Run:

breakcheck attest \
  --head HEAD \
  --claim breakcheck.claim.toml \
  --fixtures breakcheck.fixtures.toml \
  --output .breakcheck/claim-report.json \
  --evidence .breakcheck/claim-evidence.json

The strict defaults require fixtures to predate the head revision and refuse to treat an unverifiable claim as success.

Claim dispositions are:

  • CLAIM_VERIFIED: exercised and identical;
  • CLAIM_REFUTED: exercised and changed;
  • CLAIM_UNVERIFIABLE: not lawfully exercised;
  • CLAIM_OUT_OF_SCOPE: the change touched symbols omitted from the claim.

Using Breakcheck with AI-assisted development

Breakcheck provides a CLI and deterministic JSON contract; it does not require a model-specific server or protocol.

A productive division of labor is:

  1. A coding agent proposes a dependency or code change.
  2. Breakcheck identifies admitted and unresolved targets.
  3. The agent may propose fixture values for unresolved targets.
  4. A human reviews the fixture diff.
  5. Breakcheck performs repeated replay, normalization, comparison, provenance recording, and refusal.
  6. The agent reports CHANGED, NOT_EXERCISED, CLAIM_REFUTED, CLAIM_UNVERIFIABLE, and CLAIM_OUT_OF_SCOPE results verbatim for human review.

The agent must not edit reports, evidence, baselines, verdict logic, or a fixture after seeing an unfavorable result. It must not lower coverage or separation policy to obtain a passing run. Repository-local instructions are provided in SKILL.md.

Before sending any generated artifact to an external AI service, inspect and sanitize the report and evidence because they may contain repository code, arguments, outputs, and source locations.

This separation matters when software, tests, and review may all involve probabilistic tools: fixture proposals can be reviewed, while the replay and comparison outcome is deterministic and independently verifiable.

Result semantics and exit codes

Dependency and revision comparison

Exit Meaning
0 The configured coverage requirement passed and no exercised behavior changed.
2 The request, input, or evidence was refused.
3 At least one exercised behavior changed.
4 Exercised coverage was below the configured minimum.

Revision comparison always uses these semantic exit codes. Dependency comparison uses changed and coverage exit codes when --ci is present; without --ci, findings remain visible in the output but a changed result is informational. A dependency run that exercises nothing still exits nonzero unless --allow-empty was explicitly recorded.

Claim attestation

Exit Meaning
0 All claims admitted by the selected policy passed. Under the default strict policy, every claim was verified.
1 At least one claim was refuted.
2 At least one claim was unverifiable under strict policy.
3 Out-of-scope changed symbols were detected.

An exit of 0 is evidence about admitted, exercised targets only. It is not a universal compatibility, correctness, or security claim. Using --no-strict makes claim attestation advisory: unverifiable claims remain explicit in the report but do not by themselves make the command fail. Automated agents must not weaken the strict defaults.

What Breakcheck does not prove

Breakcheck reports observed differences across the inputs it lawfully exercises. It does not prove that either side is correct, that unexercised paths are safe, that a dependency is secure, or that every platform behaves identically. A CHANGED result may describe an intentional improvement. Breakcheck does not decide whether an upgrade should ship; maintainers make that decision alongside ordinary unit, integration, security, and platform testing.

Verify persisted evidence

breakcheck \
  --verify .breakcheck/results/report.json \
  --evidence .breakcheck/results/evidence.json

Successful verification prints VERIFIED. Schema-2 verification checks the artifact envelope, payload hash, finding and witness identities, observation hashes, repeat hashes, replay-source hashes, projection hashes, provenance, ordering, counts, and report/evidence binding. Schema-1 reports from Breakcheck 1.x remain readable.

Hashes provide integrity and self-consistency, not identity or secrecy. They do not make your computer a server, open a network port, or reveal the original content from the digest alone.

Machine-readable integration

Discover supported platforms, Python versions, schemas, and features without prompts:

breakcheck --capabilities --json

All commands are noninteractive. Reports use a versioned schema, canonical JSON, stable identities, explicit provenance, and documented exit codes so CI and coding tools do not need to parse human prose.

The repository includes a compact workflow example at examples/github-actions.yml. Copy it into your project as .github/workflows/breakcheck.yml, then adapt the package versions and command to your dependency update process.

Security and privacy

Breakcheck executes trusted package and repository code with a scrubbed environment, no shell, bounded resources, a fresh working directory, and a Python socket audit guard. These are containment measures, not a security boundary. Native extensions, subprocesses, import-time code, or code with the caller's operating-system permissions can still affect the host.

Reports and evidence may contain:

  • repository-relative source locations and replay source;
  • literal or fixture arguments and setup code;
  • normalized return values and exception messages;
  • captured output;
  • source, fixture, environment, and observation hashes.

Inspect artifacts before sharing them with another person or an external service. Do not commit .breakcheck/ output from a private repository. See SECURITY.md for the complete trust model.

Release evidence

The repository includes bounded release measurements under release_evidence/:

  • a 20-package dependency battery;
  • a 16,002-call-site scale exercise;
  • a three-repository fixture-loop viability run.

The fixture run records 49 generated bindings across three unrelated public repositories. In that bounded pre-release evaluation, one automated fixture-authoring pass produced 49 valid, executable, deterministic bindings and increased aggregate exercised calls from 1 to 50 without manual fixture edits. The included record preserves aggregate counts and fixture digests, but not enough source coordinates to serve as an independently reproducible benchmark. It is a release measurement, not a claim that the fixtures exhaust each repository's behavior.

Development

git clone https://github.com/lovettsendit/breakcheck.git
cd breakcheck
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e '.[test]'
python -m pytest -q
breakcheck --help

To build release artifacts:

python -m pip install 'build>=1.2,<2'
python -m build
python -m pip install dist/breakcheck-2.0.0-py3-none-any.whl
breakcheck --capabilities --json

See CONTRIBUTING.md before proposing changes. Breakcheck is licensed under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

breakcheck-2.0.0.tar.gz (950.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

breakcheck-2.0.0-py3-none-any.whl (99.3 kB view details)

Uploaded Python 3

File details

Details for the file breakcheck-2.0.0.tar.gz.

File metadata

  • Download URL: breakcheck-2.0.0.tar.gz
  • Upload date:
  • Size: 950.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for breakcheck-2.0.0.tar.gz
Algorithm Hash digest
SHA256 4704846710edfefaf8d6a8bf902a1b57871343d4c514a4ca345dd69c8fc3fb57
MD5 5c4ed7ced1ef646f5c1ef51101fca455
BLAKE2b-256 1905c0e6cdc2d49de70026a1157cc9f1c2d8956bacd0912384376d3b74552d35

See more details on using hashes here.

Provenance

The following attestation bundles were made for breakcheck-2.0.0.tar.gz:

Publisher: release.yml on lovettsendit/breakcheck

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file breakcheck-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: breakcheck-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 99.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for breakcheck-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5753f97e0dd17a3de847677706b9a0b9485d6e9295645bdd284a7e32303c2eaf
MD5 1e1fd1e9c5c8cac26201cc65a46ab8c2
BLAKE2b-256 20aef1a88b6cac45918c1428a2cfa9370320fefd2529a9708c8c905e814187d6

See more details on using hashes here.

Provenance

The following attestation bundles were made for breakcheck-2.0.0-py3-none-any.whl:

Publisher: release.yml on lovettsendit/breakcheck

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

2.0.1

2 files

This release

2.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page