Scenario S5: Single-Build Audit, No Baseline¶
You want abicheck to run — surfacing internal-noise checks, cross-source
findings, a public-surface report — but there is nothing to compare against
yet: no prior release, no accepted-main history, nothing. This is scenario S5, and it is a real, distinct check kind, not a degraded form of
comparison — advisory by default, since there is no baseline-drift verdict
to gate CI on in the first place.
The declared target: compare --no-baseline¶
This scenario is a scope of the one comparison product rather than a
second command: abicheck compare --no-baseline CANDIDATE, with OLD recorded in the declared_absent acquisition state. scan is retired with no deprecation
window once that migration completes, so treat
compare --no-baseline as where this scenario is going.
It gets you there now. The two defects that made this section read
"use scan instead" until 2026-09-09 — a stored .abi.json candidate
aborting with an AssertionError, and a live binary plus -H rendering an
empty result — are both fixed, and
tests/parity/test_no_baseline_audit_corpus_parity.py pins that
compare --no-baseline reports at least every check scan does — counted
per finding kind, so no capability is lost — across all eleven G20 audit
fixtures, while manufacturing no comparison of its own. It is a
no-capability-loss floor, not an assertion that the two produce
byte-identical reports: scan's per-check coverage rows (status/detail/
providers) have no audit-report equivalent yet, see
known-gaps.md.
The CLI path: compare --no-baseline¶
This runs the same cross-source/single-release checks
(CROSS_SOURCE_EVOLUTION_CHECKS, e.g. exported_not_public,
private_header_leak, unversioned_exported_symbol,
rtti_for_internal_type, public_not_exported,
public_to_internal_dependency, and the rest of the registered set) that
scan CANDIDATE (no --against) runs, and reports whatever it finds under
findings[].
Two things to know about the report shape, both direct consequences of treating an audit as a comparison scope:
changes[]is always empty, andverdictis alwaysnull. An audit reports no addition, no removal, and no compatibility verdict — the candidate-side findings live underfindings[]. A consumer parsingchangesoff this report is reading the wrong key.- Every finding carries an evolution state, which with OLD
declared_absentis alwayspersistent(the check fired) ornot_evaluated(the check's evidence gate closed).introducedandresolvedare unreachable here by construction: both assert something about a baseline this run was told does not exist.
--header/--include, --sources/--build-info, --depth, --contract,
--policy/--suppress, --dry-run and -o/--output all work. The export
accepts json, markdown, sarif, junit and oneline; html and
review are a declared usage error, because both render a comparison
(verdict badge, OLD → NEW counts, release recommendation) and an audit has
none of those — oneline is the closest honest equivalent to a review
digest. An old=-prefixed evidence input (--sources old=…) is a usage
error too: there is no OLD side for it to describe.
Exit codes: hygiene findings are advisory and never gate on their own, so a
clean run and a run reporting several findings both exit 0 by default.
Gating on a hygiene finding is opt-in, via the audit-gate axis
:
a scan-based gating job migrating to compare --no-baseline needs exactly
one addition to keep gating on a BREAKING/API_BREAK-classified finding —
add --severity-preset default (or strict) — which then exits 3, never
2, so the hygiene gate can never be mistaken for a real compatibility
break; a non-gating job needs no change. The four orthogonal axes still
apply — see
exit codes.
The Action: mode: compare, no baseline¶
0.6 retired mode: scan outright — the root Action's mode: compare now
exposes --no-baseline directly: omit both old-library and abi-baseline
and it runs the audit-only shape. So at the Action level, a no-baseline
audit — including one that needs L3/L4 evidence — goes through
mode: compare with old-library/abi-baseline both left unset:
targets:
libfoo:
binary_pattern: "lib/libfoo.so*"
checks:
- channel: none
depth: source # or binary/headers/build -- an audit at any depth
- uses: abicheck/abicheck/actions/check-target@c9e135a3233b6d45e9571533f71293fde458a469 # not yet in a tagged release; pin main or newer
with:
name: libfoo
target-kind: library # required -- app-consumer/plugin-contract have
# no scan-mode equivalent to --used-by/--required-symbol
baseline-channel: none
requested-depth: source
Set baseline-channel: none. check-target detects this and skips
resolve-baseline entirely — it
never even attempts a baseline lookup, so this is not the same as an
unresolved required: true channel hitting not_found (a hard failure) —
which leaves old-library/abi-baseline both unset on the nested
mode: compare invocation, selecting its audit-only shape. A
.abicheck.yml checks: entry with channel: none defaults its own
gate_mode to advisory, matching this semantic.
See the check-target reference for the
full bypass mechanics, and
GitHub Action: Source Scans § Single-release audit
for the equivalent one-step mode: compare (no baseline) wiring.
When to move past this scenario¶
- You now have something to compare against → any other scenario; a
no-baseline audit (
compare --no-baseline CANDIDATEon the CLI,baseline-channel: noneon the Action) is a starting point, not a permanent choice for a project that will eventually publish a release or trackmain. target-kind: app-consumer/plugin-contract— not supported withbaseline-channel: none:compare --no-baselinehas no--used-by/--required-symbolequivalent, so an app-consumer/plugin-contract audit with no baseline has no scope to check against. Usekind: libraryfor a no-baseline audit.
See also¶
- Which Scenario Am I? — the full scenario index.
check-targetAction Reference — the full bypass mechanics and report shape.- GitHub Action: Source Scans — the one-step
mode: compare(no baseline) equivalent.