Verdicts¶
Every abicheck compare run produces one of five core verdicts, ordered from
safest to most severe: NO_CHANGE, COMPATIBLE, COMPATIBLE_WITH_RISK,
API_BREAK, BREAKING. The verdict is the worst classification across all
detected changes under the active policy.
Each change kind is partitioned into exactly one classification set in
checker_policy.py — BREAKING_KINDS, API_BREAK_KINDS, RISK_KINDS, or
COMPATIBLE_KINDS — and COMPATIBLE_KINDS is further split into additions
(ADDITION_KINDS, new public surface) and quality signals
(QUALITY_KINDS, hygiene/metadata). The Examples Encyclopedia
groups every fixture by both verdict and category.
A verdict is a fact; the release decision is policy. For how these verdicts map onto SemVer version bumps and product-contract decisions (and why the same change can be breaking for one product and a non-event for another), see Compatibility as a Product Contract.
Beyond the five core verdicts.
comparein severity-aware mode (any--severity-*flag) can also reportSEVERITY_ERRORwith exit code1when an addition/quality finding is promoted to error level — for example to block accidental public-API expansion. Thecompare-releasepackage mode addsREMOVED_LIBRARY(exit8) when a shared object present in the old package is absent from the new one. See the GitHub Action and Exit Codes for the full matrix.
The five verdicts¶
NO_CHANGE¶
The two snapshots are identical — no differences found.
CI action: pass.
COMPATIBLE¶
Changes found, but backwards-compatible — existing compiled consumers can upgrade without recompiling. abicheck splits this tier into two reportable categories:
Additions (ADDITION_KINDS) — new public surface:
- New exported symbol or global variable added
- Enum member appended at the end of an enum (no value shift)
- Union field added without growing the union's size
- Inline function outlined into the .so (new export, old inlined copies still work)
- experimental:: graduated to stable while keeping the old alias
Quality (QUALITY_KINDS) — hygiene/metadata signals, not ABI breaks:
- GLOBAL → WEAK symbol binding (ELF/Linux; relaxes interposition only)
- GNU IFUNC introduced/removed
- SONAME/visibility/versioning hygiene findings (missing SONAME, RPATH leak, executable stack)
Note:
noexceptremoval is notCOMPATIBLE— it isCOMPATIBLE_WITH_RISK(see below), because callers compiled assumingnoexceptomit exception landing pads.
CI action: warn; do not fail. Use a severity flag (e.g. --severity-addition error) to promote additions/quality to an error-level SEVERITY_ERROR if your policy requires it.
COMPATIBLE_WITH_RISK¶
A change that does not break existing compiled consumers (they are already linked and continue to work), but introduces a deployment risk that must be verified manually.
The library upgrade may fail on some target environments — for example, if the new library requires a newer glibc version that is absent on the deployment target — or the change is binary-linkable but semantically unsafe for binaries built under the old contract.
Examples (RISK_KINDS):
- New symbol version requirement added to DT_VERNEED (e.g. GLIBC_2.17) — existing binaries are safe, but the new .so won't load on systems with older glibc
- noexcept removed (case15) — links fine, but callers built assuming noexcept omit landing pads, so a real throw calls std::terminate
- A CPU-dispatch ISA family dropped (case83) — loads fine, but the optimized path a consumer expected is gone
CI action: warn; inspect the specific change kind and verify target environment requirements. Do not fail automatically unless your policy mandates it.
Use
abicheck compare --format jsonto check the exactverdictfield —COMPATIBLE_WITH_RISKexits with code0, same asCOMPATIBLE.
API_BREAK¶
A source-level API break — the public header contract changed in a way that breaks downstream source code, but does not break already-compiled binaries. Pre-compiled consumers continue to work at runtime. Consumers that recompile against new headers may get compile errors or semantic changes.
Examples:
- Field rename (same binary layout, different source name)
- Enum member rename
- Parameter default value removed
- Reduced access level (public → protected)
CI action: fail in API-strict pipelines or pipelines that test building from source; warn in ABI-only gates.
Note:
abicheck compatdoes emit exit code2forAPI_BREAKconditions. However, thecompatHTML/text report uses ABICC-style phrasing ("⚠️ API_BREAK — Source-level API change — recompilation required") rather than a bareAPI_BREAKverdict string. Useabicheck compare --format jsonfor machine-readable verdict values.
BREAKING¶
A binary ABI break — existing compiled consumers malfunction when the library is updated.
Examples:
- Symbol removed from .so
- Function parameter type changed
- Struct field removed or offset shifted
- C++ vtable reordered (virtual method inserted)
- const qualifier added to global variable (moves to .rodata, breaks writes)
CI action: always fail; do not ship.
CI policy templates (compare mode)¶
Strict production gate¶
abicheck compare old.json new.json
ret=$?
[ $ret -eq 4 ] && echo "BREAKING — release blocked" && exit 1
[ $ret -eq 2 ] && echo "API_BREAK — source-level break" && exit 1
[ $ret -ne 0 ] && echo "unexpected exit code $ret — check tool inputs" && exit 1
echo "OK (NO_CHANGE or COMPATIBLE)"
Warning-only gate¶
abicheck compare old.json new.json --format json -o result.json
ret=$?
[ $ret -eq 4 ] && echo "::error::BREAKING ABI change" && exit 1
[ $ret -ne 0 ] && [ $ret -ne 2 ] && echo "::error::unexpected exit code $ret" && exit 1
[ $ret -eq 2 ] && echo "::warning::API_BREAK (source-level)"
verdict=$(python3 -c "import json; print(json.load(open('result.json'))['verdict'])" 2>/dev/null || echo "")
[ "$verdict" = "COMPATIBLE" ] && echo "::warning::COMPATIBLE ABI change (new symbols or compatible modifications)"
echo "ABI check passed"
Permissive gate (binary breaks only)¶
abicheck compare old.json new.json
ret=$?
[ $ret -eq 4 ] && exit 1 # BREAKING only; API_BREAK (exit 2) allowed
[ $ret -ne 0 ] && [ $ret -ne 2 ] && exit 1 # unexpected exit code (tool failure)
exit 0
For
compatmode CI patterns, see ABICC Compatibility. Note: in compat mode, exit1= BREAKING, exit2= API_BREAK. Non-verdict failures use extended codes (3–11) — see Exit Codes.
Full exit code reference: Exit Codes