A CI verdict can be correct and still leave behind a broken audit trail.
For example: the workflow_run.json captured as evidence has a head_sha that does not match the commit the build actually ran on. Tests passed, the merge button unlocked, the
deployment shipped. The verdict was right; the bundle is fabricable. A reviewer who needs the lineage from the deployed binary back to the source SHA cannot get it.
Most CI tooling does not check for this. The verdict is the contract; the audit trail is whatever happened to land alongside it. evidence-gate is a small Python library that names
this surface. It reads files your pipeline already captured, then asks a different question: is the trail behind this verdict complete enough to trust? It does not run CI jobs, fetch
from GitHub, validate claim semantics, or decide whether the build itself was right. Its only job is the meta-verdict.
## The strongest pattern is timestamp recomputation
Evidence has a time boundary. If raw authority was fetched after the claim time, it cannot honestly support that claim. evidence-gate records fetch timestamps and recomputes
whether each fetch happened inside the declared capture window. It does not trust a stored boolean called all_raw_authority_fetched_by_claim_time. The fetch times need to agree
with what was recorded. When they disagree, the report fails timestamp_provenance_self_consistent and the bundle is incomplete.
Most CI tools do not check this. The cost to defend against it is one timestamp per fetch and one comparison.
## The completeness check builds on that pattern
After your CI verdict, evidence-gate verifies the bundle has every required raw evidence file, every projected evidence file, every lineage entry, every verdict artifact. It
returns the exact unsatisfied condition names so a caller fails closed without guessing:
verdict: incomplete
unsatisfied_conditions: [
"source_sha_lineage_proven",
"timestamp_provenance_self_consistent",
]
That output is the receipt. It says, in plain language, that the audit trail behind your green build does not hold together.
## Raw evidence and projected evidence are not the same thing
Raw API responses and derived gate facts have different roles. Raw evidence is the immutable capture. Projected evidence is the smaller shape your gate actually consumes. A reviewer
should be able to follow any projected fact back to the raw files it was derived from. evidence-gate writes per-file projection lineage that makes this traceable, and the
completeness check fails if the lineage does not cover every projected file present in the bundle.
## A bundle should state what it does not claim
The fourth pattern is the scope bundle: owned_scope, boundary_limits, honesty_credits. A CI run that says nothing about its boundaries is harder to audit than one that
explicitly disclaims what it does not prove. The package validates the scope triple and fails completeness when the bundle's claims and disclaimed limits are missing or invalid.
## Minimum viable integration
Capture your existing GitHub Actions JSON. Write raw evidence and projected evidence into separate directories. Record fetch timestamps with record_fetch. Run your existing CI
verdict. Then call check_completeness on the bundle and refuse to publish a green verdict if the audit trail is incomplete.
from pathlib import Path
from evidence_gate.completeness import check_completeness
evidence_report = check_completeness(Path("/path/to/run"))
acceptable = {"complete", "complete_with_quarantine"}
if existing_ci_verdict == "PASS" and evidence_report.completeness_verdict not in acceptable:
raise SystemExit("CI passed, but the audit trail is incomplete")
## What this does not solve
Every CI integrity problem. evidence-gate does not replace branch protection, verify test semantics, or handle every CI platform. It does not prevent compromised runners from
lying. It is deliberately smaller than that. It catches a common failure mode: a verdict that outruns its evidence.
CI already has enough tools that say pass or fail. It needs more tools that ask whether the pass or fail can be audited.
evidence-gate is on PyPI: pip install evidence-gate. Source and audit history at https://github.com/blazingRadar/evidence-gate.
Top comments (0)