Skip to content

Upgrading to 0.6

Several behavioral and API changes accumulated across recent releases, tracked individually in changelog.d/ fragments (see CHANGELOG.md). This page collects them into one migration story instead of asking you to piece it together from dozens of fragments — organized around the version already used as the "Changed in 0.6" marker in Python API.

What might actually break your scripts

Change What breaks Fix
CompareResult instead of a bare tuple result, old, new = run_compare(...) (positional unpack) result, old, new = run_compare(...).as_tuple() — one-line fix
Authoritative contract evaluation If you already pass --contract-evaluation, the compatibility verdict can only move one direction — same or less severe — since policy now scores just the EVALUATED subset it used to score in full (a finding proven out-of-contract stops blocking; one that can't be resolved stops gating as an ABI break — it still stays in changes/audit ledgers, NOT_EVALUATED). The overall process exit can move the other way too, but via the separate mechanism in the next row: the newly-authoritative contract-coverage contribution can raise a clean exit to 1 Re-check any script that parsed verdict/exit code under --contract-evaluation — see Contract-Aware Compatibility
Contract-coverage exit 1 A script that treats every exit 1 as "a severity error" will now also see 1 for incomplete contract evidence Read contract_coverage_exit_contribution (or the severity block's own pre-fold exit_code) to tell the two apart — see Exit Codes
Aggregate schema 1.2/1.3 A JSON consumer with a fixed field list will silently ignore the new finding_matrix/profile_matrix/contract_coverage blocks (additive, not a breaking JSON change) — but a consumer asserting an exact key set will fail Check aggregate_schema_version if you need to react to the new blocks; see Aggregate Reports
Scoped severity correction A compare --used-by/--required-symbol script that assumed --severity-* flags were silently ignored on the scoped path now gets real severity-aware exit codes there too Re-check any script relying on the old fixed legacy 0/2/4 mapping for a scoped run — see Application Compatibility → Exit codes
Project profiles becoming load-bearing A .abicheck.yml profiles: block written before recent phases used os/dependency_source/compile.frontend as inert documentation; they now actually schedule the runner, provision dependencies, and select the AST frontend for that profile's cell Review your profiles: block against Scenario S17 if you rely on a specific runner/toolchain per profile — an already-correct declaration needs no changes, but a previously-inert one now takes effect
MCP server removed The abicheck-mcp executable, the abicheck[mcp] optional-dependency group, and every importable abicheck.mcp_* module are gone. pip install "abicheck[mcp]" no longer fails outright — pip just warns that the distribution doesn't provide the mcp extra and installs the base package — but any subsequent abicheck.mcp_* import, the abicheck-mcp executable, or an abi_compare/abi_dump/abi_scan/abi_deps/abi_aggregate/abi_project_validate/abi_project_plan MCP tool call now fails outright. There is no replacement protocol server Point agent/CI integrations at the abicheck CLI (with --format json/--format sarif for machine consumption) or the typed Python API (from abicheck.service import ..., see Python API) — both expose the same resolution/classification pipeline the MCP tools called into. See the retired ADR-021 for historical context

CompareResult instead of a tuple

run_compare/run_compare_request returned a bare tuple[DiffResult, AbiSnapshot, AbiSnapshot] before 0.6. The new CompareResult dataclass adds a fourth field (suppression) — this does break a caller that unpacked the old return value positionally (result, old, new = run_compare(...)), since a dataclass isn't a tuple at all. What it doesn't break is attribute/keyword access to the three original values (result.diff, result.old_snapshot, result.new_snapshot) — which is exactly why future fields can be added to CompareResult without a repeat of this migration, the way they couldn't be added to a bare tuple:

# Before
result, old_snapshot, new_snapshot = run_compare(...)

# After
result, old_snapshot, new_snapshot = run_compare(...).as_tuple()

Full detail: Python API → Compare two libraries.

Authoritative contract evaluation

If you already had --contract-evaluation in a script or CI job before this landed, re-read Contract-Aware Compatibility — relevance used to be computed and reported but never consulted by policy (a shadow annotation); it is now what decides whether a finding reaches policy at all. This is the one item on this page that can genuinely change a verdict for an already opted-in run — not for a plain compare without the flag, which is unaffected either way.

Contract-coverage exit 1

A new, independent reason for exit 1 under --contract-evaluation: incomplete evidence for the selected domain. It's folded with max against the ordinary severity gate, so it only ever raises a clean 0, never lowers a 2/4. A CI script that branches only on exit_code == 1 meaning "severity error" should read the compare report's contract_coverage_exit_contribution field (or inspect contract_coverage_failures) to tell the two apart. See Exit Codes.

Aggregate schema 1.2/1.3

  • 1.2 added finding_matrix (reconciling one finding across compiler profiles).
  • 1.3 added the top-level contract_coverage block and a contract_coverage_exit field per target.

Both are purely additive — an existing JSON consumer reading only gate/coverage/compatibility/targets needs no changes. See Aggregate Reports for what the new blocks mean.

Scoped severity correction

compare --used-by/--required-symbol(s) used to always compute its exit code from a fixed legacy 0/2/4 mapping, regardless of any --severity-* flag passed alongside it. That's fixed: a scoped run now resolves the same legacy/severity scheme plain compare does (an explicit --exit-code-scheme still pins one regardless of severity flags). If a script depended on the old, silently-ignored behavior, re-check it against Application Compatibility → Exit codes.

Project profiles

.abicheck.yml's profiles: block gained real teeth: os/dependency_source now schedule where a profile's check cell actually runs and how it provisions dependencies, and compile.frontend actually steers a normal (non-bundle) target cell's AST frontend end to end (not just documentation of intent) — a kind: bundle check is the one exception, where it's still projected but not applied, since a directory/package operand rejects any non-auto frontend. If your profiles: block already declared these correctly, nothing changes; if it declared them as inert notes (e.g. relying on a workflow-level default to override them), review Scenario S17 — they're load-bearing now.

See also