Full example validation runbook¶
Use this workflow when a change can affect example detection, ground truth, fixtures, or validation harnesses. It is the source of truth for complete catalog accounting.
Choose the correct runner¶
| Question | Runner |
|---|---|
Is a single-library v1/v2 case classified correctly? |
tests/validate_examples.py |
| Does a case demonstrate a runtime effect? | validation/scripts/run_example_runtime_smoke.py |
| Are multi-library release bundles correct? | validation/scripts/run_bundle_examples.py |
| Do all non-compiler, non-bundle fixtures pass through public CLI workflows? | validation/scripts/run_special_cli_examples.py |
| Are audit, BTF, L3/L4/L5, Python API, reconcile, snapshot-pair, and KABI fixtures valid? | The dedicated pytest proof artifact below |
| Is every ground-truth case accounted for? | validation/scripts/collect_full_example_matrix.py |
| How accurate are evidence depths or external tools? | Benchmark/depth runners; measurement only |
validate_examples.py alone is not the full catalog. A scan of directories
that produce libv1.so/libv2.so also omits bundle, audit, fixture, Python,
BTF, KABI, and other dedicated-owner cases.
Prerequisites and trust boundary¶
Run on Linux x86_64 with the repository development environment and the same
tool dependencies as Examples Validation CI: gcc/g++, clang/clang++, CMake,
Ninja, CastXML, and binutils.
A single default gcc/clang is not enough to build the full catalog:
case115_bit_int_width_changed requires a C23 _BitInt-capable compiler
(GCC 14+, or a recent Clang) — GCC 13 and earlier reject _BitInt(N)
outright. The benchmark runners (scripts/benchmark_comparison.py,
scripts/generate_benchmark_report.py) prefer a versioned gcc-15/gcc-14
binary on PATH over the bare gcc alias for this one case; without any of
those installed, the case fails to build (reported ERROR) rather than
building at reduced fidelity. tests/feature_probe.py-gated pytest lanes
skip the case cleanly instead when the toolchain lacks the feature — the
benchmark scripts do not (yet) have the same graceful fallback, so treat an
ERROR on this case as a missing gcc-14+, not a product regression.
python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
mkdir -p results
export PYTHONPATH=.
export ABICHECK_TRUSTED_SOURCE_SMOKE_RUN=1
Activating the venv (rather than only installing into it) matters: every
python/pytest invocation below relies on PATH resolving to
.venv/bin/python, not an ambient interpreter that lacks the pinned dev
extras. If you cannot activate (e.g. a non-interactive shell), replace every
bare python command in this runbook with .venv/bin/python instead.
The source-smoke run mode executes fixture commands. Enable it only for a
trusted checkout containing reviewed repository-owned fixtures. Its default is
intentionally disabled. Without this opt-in, source-smoke-owned cases can be
SKIP in both compiler lanes and the collector reports them UNRESOLVED.
Reproduce the full matrix¶
1. Validate dedicated owners¶
The proof runner executes every dedicated owner separately and records its command, exit code, and bounded output in a machine-readable artifact.
2. Produce lane artifacts¶
python tests/validate_examples.py --toolchain gcc --json > results/validate-examples-gcc.json
python tests/validate_examples.py --toolchain clang --json > results/validate-examples-clang.json
python tests/validate_examples.py \
case01 case04 case98 case105 case122 case129 case130 case131 case132 case133 \
--artifact-variant build-source --json > results/validate-examples-build-source.json
python validation/scripts/run_example_runtime_smoke.py --json > results/example-runtime-smoke.json
python validation/scripts/run_bundle_examples.py --json > results/bundle-examples.json
python validation/scripts/run_special_cli_examples.py --json > results/special-cli-examples.json
3. Aggregate one row per case¶
python validation/scripts/collect_full_example_matrix.py \
--gcc results/validate-examples-gcc.json \
--clang results/validate-examples-clang.json \
--runtime results/example-runtime-smoke.json \
--build-source results/validate-examples-build-source.json \
--bundle results/bundle-examples.json \
--special-cli results/special-cli-examples.json \
--proofs results/example-owner-proofs.json \
--out results/full-example-matrix.json
The collector exits non-zero for UNRESOLVED or FAILED. Never use
--allow-unresolved for a release or correctness gate.
4. Verify the gate¶
python - <<'PY'
import json
from pathlib import Path
d = json.loads(Path("results/full-example-matrix.json").read_text())
total = d["ground_truth_cases"]
assert len(d["results"]) == total
assert d["summary"] == {"COVERED": total}
assert not d["artifact_errors"]
assert not d["unresolved_cases"]
assert not d["failed_cases"]
direct = d["direct_coverage"]
# A case can be COVERED two ways: directly (a compiler lane or public-CLI
# workflow reached the canonical verdict) or via known-gap-oracle (a
# reviewed known_gap plus the case's own source_smoke oracle proves the
# canonical truth while every evidence tier still XFAILs — see case111).
# direct_coverage deliberately excludes the latter, so asserting
# direct["covered"] == total is wrong on a fully-green matrix whenever any
# case is covered that way. Assert the two provenances sum to the total
# instead of hard-coding which cases (if any) are oracle-only.
known_gap_oracle = d["coverage_by_provenance"].get("known-gap-oracle", 0)
assert direct["covered"] + known_gap_oracle == total
print(
f"{total}/{total} COVERED; direct={direct['covered']}/{direct['total']}, "
f"known-gap-oracle={known_gap_oracle}"
)
PY
The exact count comes from examples/ground_truth.json; automation must not
hard-code a historic count. When this runbook was added, the proven result was
181/181 COVERED. As of this writing that is 180 direct + 1 known-gap-oracle
(case111) — a case only qualifies for known-gap-oracle provenance when its
own declared source_smoke oracle proved the canonical verdict; see
_single_library_status in collect_full_example_matrix.py.
Interpret results¶
- Ground truth is invariant across scan depths. A reduced-evidence lane that
cannot observe the expected change is an
XFAILdetection gap, not a second lane-specific expected verdict. Case98 demonstrates this: L0–L2 observesNO_CHANGEagainst the singleCOMPATIBLE_WITH_RISKtruth, while the L3 build-source lane proves the expected build-flag risk. FAILED: a lane ran and contradicted ground truth, or a proof failed.UNRESOLVED: no owner lane proved the case; inspectlanes,proof_lane, andnotein that row.artifact_errors: a required runner artifact was missing, malformed, partial, produced by the wrong runner/toolchain, or came from different ground truth.- A compiler-lane
SKIPis acceptable only when another designated owner proves that case and the final row isCOVERED. provenance=compilermeans GCC or Clang demonstrated a compilable pair.abicheck-cli-workflowmeans a public CLI command demonstrated a special input shape or richer build-source evidence. The dedicated-owner artifact remains a separate regression proof, but does not substitute for the required public-CLI artifact.- G20 audit risks are advisory at the default
scangate: the CLI emits the expected cross-check kinds/providers and exits 0 withCOMPATIBLE. The special runner validates that contract instead of falsely requiring the comparison-onlyCOMPATIBLE_WITH_RISKlabel. - Runtime statuses describe behavior; they do not replace verdict proof.
Agent checklist¶
- Read
examples/ground_truth.json; it defines scope and ownership. - Use the smallest owner runner while iterating.
- Before claiming full-catalog success, obtain every artifact above.
- Record commit SHA, tool versions, commands, exit codes, and artifact paths.
- Claim success only when collector JSON has one row per case and every row is
COVERED, with no unresolved or failed cases. - Never substitute pair counts, scan-depth totals, benchmark accuracy, or one green compiler lane for the full matrix.
- Keep generated binaries and ad-hoc results out of commits.
CI implementation: .github/workflows/examples-validation.yml. Keep this
runbook synchronized with its full-matrix job.