Skip to content

Case 151: Provider-Agreement Matrix (Corroboration Grows With Evidence)

Field Value
Verdict 🟢 COMPATIBLE
Category Quality (Compatible)
Classification Scenario — Capability / evidence demonstration · audit
Platforms Linux
Flags Bad practice
Detected ChangeKinds private_header_leak
Source files catalog/cases/case151_xcheck_provider_matrix/
Related rules audit-private-header-leak
Subject Export/declaration mismatches

Category: Quality (Audit) | Verdict: 🟢 COMPATIBLE (bad practice)

Verdict and consumer impact

Single-release audit: one build's evidence checked against itself, no baseline. An audit reports no compatibility verdict at all — there is no baseline to compare against — but the audit's private_header_leak finding (public function make_widget() returns a private-header type, same shape as case144) is the fixed point of this case; what varies is how much evidence corroborates it. The cross-check machinery records which providers (evidence sources) contributed to each finding, and this case demonstrates that the list grows — without the finding itself changing — as more evidence becomes available.

What this snapshot contains

Two fixtures for the same underlying leak, differing only in how much evidence is attached:

Fixture Evidence present Providers recorded for private_header_leak
thin.abi.json public-header AST (L2) only public_header_ast (1 provider)
snapshot.abi.json header AST (L2) + L5 source graph public_header_ast, source_index (2 providers)

abicheck command

abicheck compare --no-baseline thin.abi.json      # 1 provider
abicheck compare --no-baseline snapshot.abi.json  # + source_index corroboration

Expected abicheck finding

Both fixtures report the identical finding (this is snapshot.abi.json; thin.abi.json differs only in the header line's evidence tiers):

# ABI audit: libdemo.so (no baseline)

OLD side: **declared absent** (`--no-baseline`) -- this is an audit of the candidate build alone, not a compatibility comparison. No additions, removals, or compatibility verdict are reported.

- Candidate version: `1.0`
- Acquisition state (OLD): `declared_absent`
- Evidence tiers: elf, header

## Candidate-side findings

| Finding | Symbol | Severity | State | Detail |
| --- | --- | --- | --- | --- |
| `private_header_leak` | `_Z11make_widgetv` | potential_breaking | present in this build | Public API 'make_widget' exposes type 'detail::WidgetImpl', which is declared only in a private (non-installed) header. Consumers including the public header pull in an unshipped declaration. Make the header self-contained or install the leaked header. |

The provider list is where the two fixtures diverge. It is not carried by the audit report in any format today — the one-sided report states each finding and its evolution state, not the per-check coverage rows (status/detail/providers) legacy scan's own crosscheck block carried; that difference is recorded in docs/contribute/known-gaps.md. Read it directly off run_crosschecks(), the same call the audit's own cross-source pass drives:

python3 - <<'EOF'
from abicheck.serialization import load_snapshot
from abicheck.buildsource.cross_source_checks import run_crosschecks

for path in ("thin.abi.json", "snapshot.abi.json"):
    res = run_crosschecks(load_snapshot(path))
    print(path, "private_header_leak providers:", res.providers["private_header_leak"])
EOF
thin.abi.json     private_header_leak providers: ['public_header_ast']
snapshot.abi.json private_header_leak providers: ['public_header_ast', 'source_index']

Minimum evidence

min_evidence: L2 — the public-header AST alone (the thin.abi.json floor) is already enough to flag the leak with one provider. The L5 source graph, when present, adds a second, independent corroborating provider — it strengthens confidence in the same finding but is not required to reach the floor.

Why abicheck catches it

private_header_leak is a cross-source check: abicheck resolves every type referenced in a public signature and checks that type's own provenance. The public_header_ast provider alone is enough to know make_widget() returns a type recorded as origin: private_header. When an L5 source graph is also attached, source_index independently confirms the same declaration-to-private-type relationship by walking the graph's edges — a second source reaching the same conclusion, recorded as a second provider rather than a stronger verdict.

Scope. This case asserts the provider list differs. Deriving a per-finding confidence tag from the provider count (so 1-provider corroboration renders differently from 2) is a separate reporting enhancement, not part of this corpus.

Why this matters for a real release

A finding backed by one provider and a finding backed by two independent providers are not equally trustworthy, even though both fire the same ChangeKind. A CI pipeline that only ran a header scan (thin evidence) and one that also replayed the source tree (rich evidence) should both catch this leak — and do — but only the richer pipeline can tell a reviewer "two independent sources agree," which matters when deciding whether a finding is worth blocking a release over.

Safe redesign

Same as any private-header leak (see case144): opaque-handle the internal type, or install its header so it's a real, documented part of the public API.

Cross-tool comparison

private_header_leak is a cross-source check unique to abicheck's audit mode — it reconciles a public function's signature against the provenance of the type it references within the same build, which isn't something abidiff/abi-compliance-checker do (they diff two ABI dumps against each other, not a binary's public surface against its own header provenance). Provider-agreement tracking (this case's subject) has no equivalent in either tool.


Source files

  • snapshot.abi.json
  • thin.abi.json

See also: Compatibility Catalog · All COMPATIBLE cases · Category: Quality (Compatible) · Subject: Export/declaration mismatches.