DEV Community

Nick Cunningham
Nick Cunningham

Posted on

The completeness check that runs after your CI verdict

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",                                                                                                                                             
  ]                                                                  
Enter fullscreen mode Exit fullscreen mode

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")                                                                                                                  
Enter fullscreen mode Exit fullscreen mode

## 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)