evidence-gate 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- evidence_gate-0.1.0/.github/workflows/ci.yml +21 -0
- evidence_gate-0.1.0/.gitignore +11 -0
- evidence_gate-0.1.0/CHANGELOG.md +9 -0
- evidence_gate-0.1.0/LICENSE +21 -0
- evidence_gate-0.1.0/PKG-INFO +185 -0
- evidence_gate-0.1.0/README.md +159 -0
- evidence_gate-0.1.0/examples/integrate_with_existing_gate.py +31 -0
- evidence_gate-0.1.0/examples/minimal_usage.py +16 -0
- evidence_gate-0.1.0/pyproject.toml +65 -0
- evidence_gate-0.1.0/src/evidence_gate/__init__.py +15 -0
- evidence_gate-0.1.0/src/evidence_gate/completeness.py +477 -0
- evidence_gate-0.1.0/src/evidence_gate/projection.py +270 -0
- evidence_gate-0.1.0/src/evidence_gate/scope.py +118 -0
- evidence_gate-0.1.0/src/evidence_gate/timestamps.py +62 -0
- evidence_gate-0.1.0/src/evidence_gate/types.py +105 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/RUN_META.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/RUN_STATUS.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/current_claim.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/hash_manifest.sha256 +1 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/lineage_manifest.json +67 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/anchor_refs.json +7 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/attempt_index.json +7 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/boundary_limits.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/event_timestamps.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/honesty_credits.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/owned_scope.json +7 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/projected_facts.json +9 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/projection_hash_manifest.sha256 +1 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/projection_manifest.json +12 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/projected_evidence/supported_claims.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/quarantine/.gitkeep +0 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/raw_evidence/capture_manifest.json +8 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/raw_evidence/github/commit.json +3 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/raw_evidence/github/pull_request.json +6 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/raw_evidence/github/workflow_run.json +5 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/raw_evidence/raw_hash_manifest.sha256 +1 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/timestamp_provenance.json +14 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/verdict/gate_request.json +3 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/verdict/gate_response.json +3 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/verdict/scenario_result.json +3 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/verdict/verdict_hash_manifest.sha256 +1 -0
- evidence_gate-0.1.0/tests/fixtures/sample_pass_run/verdict/verdict_summary.json +3 -0
- evidence_gate-0.1.0/tests/test_completeness.py +562 -0
- evidence_gate-0.1.0/tests/test_integration.py +124 -0
- evidence_gate-0.1.0/tests/test_projection.py +266 -0
- evidence_gate-0.1.0/tests/test_scope.py +100 -0
- evidence_gate-0.1.0/tests/test_timestamps.py +70 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: pip install -e ".[dev]"
|
|
20
|
+
- run: pytest -q
|
|
21
|
+
- run: ruff check .
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick Cunningham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evidence-gate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Catches green CI verdicts whose audit trail is missing, late, or unclear.
|
|
5
|
+
Project-URL: Homepage, https://github.com/blazingRadar/evidence-gate
|
|
6
|
+
Project-URL: Repository, https://github.com/blazingRadar/evidence-gate
|
|
7
|
+
Project-URL: Issues, https://github.com/blazingRadar/evidence-gate/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/blazingRadar/evidence-gate/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Nick Cunningham <nick.lee.cunningham@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: audit,ci,evidence,github-actions,provenance
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# evidence-gate
|
|
28
|
+
|
|
29
|
+
`evidence-gate` catches green CI verdicts whose audit trail is missing, late,
|
|
30
|
+
or unclear.
|
|
31
|
+
|
|
32
|
+
It is a small Python library for GitHub Actions evidence bundles. It reads files
|
|
33
|
+
that your pipeline already captured, then reports whether the evidence trail is
|
|
34
|
+
complete, temporally bounded, and explicit about scope. It does not fetch from
|
|
35
|
+
GitHub, run jobs, validate claim semantics, or decide whether the underlying CI
|
|
36
|
+
result is correct.
|
|
37
|
+
|
|
38
|
+
The v0.1.0 bundle contract expects root metadata files (`current_claim.json`,
|
|
39
|
+
`RUN_META.json`, `RUN_STATUS.json`, `timestamp_provenance.json`, and
|
|
40
|
+
`lineage_manifest.json`), raw evidence under `raw_evidence/`, projected evidence
|
|
41
|
+
under `projected_evidence/`, and verdict artifacts under `verdict/`. Hash
|
|
42
|
+
manifests are supported through `ArtifactExpectation`, but they are optional in
|
|
43
|
+
the default v0.1.0 completeness contract.
|
|
44
|
+
|
|
45
|
+
## The Four Patterns
|
|
46
|
+
|
|
47
|
+
### Completeness Check
|
|
48
|
+
|
|
49
|
+
A CI verdict can be fact-correct while its audit trail is broken. The
|
|
50
|
+
completeness check runs after your existing verdict and verifies that the bundle
|
|
51
|
+
contains the expected raw evidence, projected evidence, lineage, timestamp,
|
|
52
|
+
verdict, and quarantine artifacts. It reports exact unsatisfied condition names
|
|
53
|
+
so a caller can fail closed without guessing what is missing.
|
|
54
|
+
|
|
55
|
+
### Capture-Window Timestamp Ordering
|
|
56
|
+
|
|
57
|
+
Evidence captured after a claim should not silently support that claim. The
|
|
58
|
+
timestamp check recomputes whether raw authority was fetched inside the declared
|
|
59
|
+
capture window and by the claim time. The completeness check uses this
|
|
60
|
+
recomputation to catch timestamp provenance files whose stored booleans do not
|
|
61
|
+
match their recorded fetch times. The report names that failure as
|
|
62
|
+
`timestamp_provenance_self_consistent`.
|
|
63
|
+
|
|
64
|
+
A `record_fetch(label, timestamps)` helper is available for building the
|
|
65
|
+
`raw_github_fetch_times` mapping that `timestamp_provenance.json` consumes. It
|
|
66
|
+
writes a UTC timestamp into a caller-owned dict; the caller decides when to
|
|
67
|
+
serialize the dict to disk.
|
|
68
|
+
|
|
69
|
+
### Raw vs Projected Evidence
|
|
70
|
+
|
|
71
|
+
Raw API responses and derived gate facts serve different purposes. This package
|
|
72
|
+
keeps them in separate trees and writes per-file projection lineage that names
|
|
73
|
+
which source files produced each projected artifact. That makes the derived
|
|
74
|
+
facts reviewable without mutating the raw capture.
|
|
75
|
+
|
|
76
|
+
### Scope Bundle
|
|
77
|
+
|
|
78
|
+
An evidence bundle should state what it owns and what it does not claim. The
|
|
79
|
+
scope helpers validate and write `owned_scope`, `boundary_limits`, and
|
|
80
|
+
`honesty_credits` files. Empty honesty credits are allowed but reported as a
|
|
81
|
+
warning.
|
|
82
|
+
|
|
83
|
+
## Minimal Usage
|
|
84
|
+
|
|
85
|
+
From a clone:
|
|
86
|
+
|
|
87
|
+
```shell
|
|
88
|
+
python3 -m venv .venv
|
|
89
|
+
. .venv/bin/activate
|
|
90
|
+
python3 -m pip install -e .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
After publication:
|
|
94
|
+
|
|
95
|
+
```shell
|
|
96
|
+
python3 -m venv .venv
|
|
97
|
+
. .venv/bin/activate
|
|
98
|
+
python3 -m pip install evidence-gate
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Save this script in the clone root to try the included fixture:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from pathlib import Path
|
|
105
|
+
|
|
106
|
+
from evidence_gate.completeness import check_completeness
|
|
107
|
+
|
|
108
|
+
run_dir = Path(__file__).resolve().parent / "tests" / "fixtures" / "sample_pass_run"
|
|
109
|
+
report = check_completeness(run_dir)
|
|
110
|
+
|
|
111
|
+
print(report.completeness_verdict)
|
|
112
|
+
print(report.unsatisfied_conditions)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Integrating With An Existing Gate
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from pathlib import Path
|
|
119
|
+
|
|
120
|
+
from evidence_gate.completeness import check_completeness
|
|
121
|
+
|
|
122
|
+
existing_ci_verdict = "PASS"
|
|
123
|
+
evidence_report = check_completeness(Path("/path/to/evidence-run"))
|
|
124
|
+
acceptable_evidence_verdicts = {"complete", "complete_with_quarantine"}
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
existing_ci_verdict == "PASS"
|
|
128
|
+
and evidence_report.completeness_verdict not in acceptable_evidence_verdicts
|
|
129
|
+
):
|
|
130
|
+
raise SystemExit("CI passed, but the audit trail is incomplete")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Bundle Sequence
|
|
134
|
+
|
|
135
|
+
For the default v0.1.0 contract:
|
|
136
|
+
|
|
137
|
+
1. Capture raw GitHub Actions files under `raw_evidence/`.
|
|
138
|
+
2. Write root metadata files: `current_claim.json`, `RUN_META.json`,
|
|
139
|
+
`RUN_STATUS.json`, and `timestamp_provenance.json`.
|
|
140
|
+
3. Run `project_github_run(raw_evidence_dir, projected_evidence_dir, ...)`.
|
|
141
|
+
The raw directory must be named `raw_evidence` for the helper to write root
|
|
142
|
+
`lineage_manifest.json`.
|
|
143
|
+
4. Call `write_scope_bundle(...)` to write user-authored scope files.
|
|
144
|
+
5. Write verdict artifacts under `verdict/`.
|
|
145
|
+
6. Run `check_completeness(run_dir)`.
|
|
146
|
+
|
|
147
|
+
`evidence-gate` is tied to the v0.1.0 root metadata contract above. Within that
|
|
148
|
+
contract, pass `ArtifactExpectation` to `check_completeness` to adapt required
|
|
149
|
+
raw, projected, verdict, optional hash, or lineage-covered files for another
|
|
150
|
+
bundle shape.
|
|
151
|
+
|
|
152
|
+
Claim semantics are caller responsibility in v0.1.0. `project_github_run()`
|
|
153
|
+
preserves the caller's `declared_claims` in projected artifacts, but it does not
|
|
154
|
+
infer whether those claims are semantically supported by the raw GitHub JSON.
|
|
155
|
+
|
|
156
|
+
## Quarantine Rule
|
|
157
|
+
|
|
158
|
+
An empty `quarantine/` directory is accepted. A non-empty `quarantine/` directory
|
|
159
|
+
must include one of:
|
|
160
|
+
|
|
161
|
+
- `README.md`
|
|
162
|
+
- `EXPLANATION.md`
|
|
163
|
+
- `quarantine_explanation.json`
|
|
164
|
+
|
|
165
|
+
Without one of those files, the completeness report marks
|
|
166
|
+
`quarantine_empty_or_explained` as unsatisfied.
|
|
167
|
+
|
|
168
|
+
## What This Is Not
|
|
169
|
+
|
|
170
|
+
- Not a CI runner.
|
|
171
|
+
- Not a replacement for branch protection.
|
|
172
|
+
- Not a verifier for whether the CI verdict itself is correct.
|
|
173
|
+
- Not a network client.
|
|
174
|
+
- Not a multi-platform adapter layer in v0.1.0.
|
|
175
|
+
|
|
176
|
+
## Status
|
|
177
|
+
|
|
178
|
+
`evidence-gate` is in pre-release alpha extraction. Maintenance commitment for
|
|
179
|
+
v0.1.0: fix correctness bugs, keep the public API small, and avoid adding
|
|
180
|
+
platform support beyond GitHub Actions until there is clear demand.
|
|
181
|
+
|
|
182
|
+
## Acknowledgments
|
|
183
|
+
|
|
184
|
+
These patterns came from repeated review of GitHub Actions evidence bundles and
|
|
185
|
+
the failure modes that made those bundles hard to audit.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# evidence-gate
|
|
2
|
+
|
|
3
|
+
`evidence-gate` catches green CI verdicts whose audit trail is missing, late,
|
|
4
|
+
or unclear.
|
|
5
|
+
|
|
6
|
+
It is a small Python library for GitHub Actions evidence bundles. It reads files
|
|
7
|
+
that your pipeline already captured, then reports whether the evidence trail is
|
|
8
|
+
complete, temporally bounded, and explicit about scope. It does not fetch from
|
|
9
|
+
GitHub, run jobs, validate claim semantics, or decide whether the underlying CI
|
|
10
|
+
result is correct.
|
|
11
|
+
|
|
12
|
+
The v0.1.0 bundle contract expects root metadata files (`current_claim.json`,
|
|
13
|
+
`RUN_META.json`, `RUN_STATUS.json`, `timestamp_provenance.json`, and
|
|
14
|
+
`lineage_manifest.json`), raw evidence under `raw_evidence/`, projected evidence
|
|
15
|
+
under `projected_evidence/`, and verdict artifacts under `verdict/`. Hash
|
|
16
|
+
manifests are supported through `ArtifactExpectation`, but they are optional in
|
|
17
|
+
the default v0.1.0 completeness contract.
|
|
18
|
+
|
|
19
|
+
## The Four Patterns
|
|
20
|
+
|
|
21
|
+
### Completeness Check
|
|
22
|
+
|
|
23
|
+
A CI verdict can be fact-correct while its audit trail is broken. The
|
|
24
|
+
completeness check runs after your existing verdict and verifies that the bundle
|
|
25
|
+
contains the expected raw evidence, projected evidence, lineage, timestamp,
|
|
26
|
+
verdict, and quarantine artifacts. It reports exact unsatisfied condition names
|
|
27
|
+
so a caller can fail closed without guessing what is missing.
|
|
28
|
+
|
|
29
|
+
### Capture-Window Timestamp Ordering
|
|
30
|
+
|
|
31
|
+
Evidence captured after a claim should not silently support that claim. The
|
|
32
|
+
timestamp check recomputes whether raw authority was fetched inside the declared
|
|
33
|
+
capture window and by the claim time. The completeness check uses this
|
|
34
|
+
recomputation to catch timestamp provenance files whose stored booleans do not
|
|
35
|
+
match their recorded fetch times. The report names that failure as
|
|
36
|
+
`timestamp_provenance_self_consistent`.
|
|
37
|
+
|
|
38
|
+
A `record_fetch(label, timestamps)` helper is available for building the
|
|
39
|
+
`raw_github_fetch_times` mapping that `timestamp_provenance.json` consumes. It
|
|
40
|
+
writes a UTC timestamp into a caller-owned dict; the caller decides when to
|
|
41
|
+
serialize the dict to disk.
|
|
42
|
+
|
|
43
|
+
### Raw vs Projected Evidence
|
|
44
|
+
|
|
45
|
+
Raw API responses and derived gate facts serve different purposes. This package
|
|
46
|
+
keeps them in separate trees and writes per-file projection lineage that names
|
|
47
|
+
which source files produced each projected artifact. That makes the derived
|
|
48
|
+
facts reviewable without mutating the raw capture.
|
|
49
|
+
|
|
50
|
+
### Scope Bundle
|
|
51
|
+
|
|
52
|
+
An evidence bundle should state what it owns and what it does not claim. The
|
|
53
|
+
scope helpers validate and write `owned_scope`, `boundary_limits`, and
|
|
54
|
+
`honesty_credits` files. Empty honesty credits are allowed but reported as a
|
|
55
|
+
warning.
|
|
56
|
+
|
|
57
|
+
## Minimal Usage
|
|
58
|
+
|
|
59
|
+
From a clone:
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
python3 -m venv .venv
|
|
63
|
+
. .venv/bin/activate
|
|
64
|
+
python3 -m pip install -e .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
After publication:
|
|
68
|
+
|
|
69
|
+
```shell
|
|
70
|
+
python3 -m venv .venv
|
|
71
|
+
. .venv/bin/activate
|
|
72
|
+
python3 -m pip install evidence-gate
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Save this script in the clone root to try the included fixture:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pathlib import Path
|
|
79
|
+
|
|
80
|
+
from evidence_gate.completeness import check_completeness
|
|
81
|
+
|
|
82
|
+
run_dir = Path(__file__).resolve().parent / "tests" / "fixtures" / "sample_pass_run"
|
|
83
|
+
report = check_completeness(run_dir)
|
|
84
|
+
|
|
85
|
+
print(report.completeness_verdict)
|
|
86
|
+
print(report.unsatisfied_conditions)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Integrating With An Existing Gate
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from pathlib import Path
|
|
93
|
+
|
|
94
|
+
from evidence_gate.completeness import check_completeness
|
|
95
|
+
|
|
96
|
+
existing_ci_verdict = "PASS"
|
|
97
|
+
evidence_report = check_completeness(Path("/path/to/evidence-run"))
|
|
98
|
+
acceptable_evidence_verdicts = {"complete", "complete_with_quarantine"}
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
existing_ci_verdict == "PASS"
|
|
102
|
+
and evidence_report.completeness_verdict not in acceptable_evidence_verdicts
|
|
103
|
+
):
|
|
104
|
+
raise SystemExit("CI passed, but the audit trail is incomplete")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Bundle Sequence
|
|
108
|
+
|
|
109
|
+
For the default v0.1.0 contract:
|
|
110
|
+
|
|
111
|
+
1. Capture raw GitHub Actions files under `raw_evidence/`.
|
|
112
|
+
2. Write root metadata files: `current_claim.json`, `RUN_META.json`,
|
|
113
|
+
`RUN_STATUS.json`, and `timestamp_provenance.json`.
|
|
114
|
+
3. Run `project_github_run(raw_evidence_dir, projected_evidence_dir, ...)`.
|
|
115
|
+
The raw directory must be named `raw_evidence` for the helper to write root
|
|
116
|
+
`lineage_manifest.json`.
|
|
117
|
+
4. Call `write_scope_bundle(...)` to write user-authored scope files.
|
|
118
|
+
5. Write verdict artifacts under `verdict/`.
|
|
119
|
+
6. Run `check_completeness(run_dir)`.
|
|
120
|
+
|
|
121
|
+
`evidence-gate` is tied to the v0.1.0 root metadata contract above. Within that
|
|
122
|
+
contract, pass `ArtifactExpectation` to `check_completeness` to adapt required
|
|
123
|
+
raw, projected, verdict, optional hash, or lineage-covered files for another
|
|
124
|
+
bundle shape.
|
|
125
|
+
|
|
126
|
+
Claim semantics are caller responsibility in v0.1.0. `project_github_run()`
|
|
127
|
+
preserves the caller's `declared_claims` in projected artifacts, but it does not
|
|
128
|
+
infer whether those claims are semantically supported by the raw GitHub JSON.
|
|
129
|
+
|
|
130
|
+
## Quarantine Rule
|
|
131
|
+
|
|
132
|
+
An empty `quarantine/` directory is accepted. A non-empty `quarantine/` directory
|
|
133
|
+
must include one of:
|
|
134
|
+
|
|
135
|
+
- `README.md`
|
|
136
|
+
- `EXPLANATION.md`
|
|
137
|
+
- `quarantine_explanation.json`
|
|
138
|
+
|
|
139
|
+
Without one of those files, the completeness report marks
|
|
140
|
+
`quarantine_empty_or_explained` as unsatisfied.
|
|
141
|
+
|
|
142
|
+
## What This Is Not
|
|
143
|
+
|
|
144
|
+
- Not a CI runner.
|
|
145
|
+
- Not a replacement for branch protection.
|
|
146
|
+
- Not a verifier for whether the CI verdict itself is correct.
|
|
147
|
+
- Not a network client.
|
|
148
|
+
- Not a multi-platform adapter layer in v0.1.0.
|
|
149
|
+
|
|
150
|
+
## Status
|
|
151
|
+
|
|
152
|
+
`evidence-gate` is in pre-release alpha extraction. Maintenance commitment for
|
|
153
|
+
v0.1.0: fix correctness bugs, keep the public API small, and avoid adding
|
|
154
|
+
platform support beyond GitHub Actions until there is clear demand.
|
|
155
|
+
|
|
156
|
+
## Acknowledgments
|
|
157
|
+
|
|
158
|
+
These patterns came from repeated review of GitHub Actions evidence bundles and
|
|
159
|
+
the failure modes that made those bundles hard to audit.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""How to layer evidence-gate around an existing CI verdict."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from evidence_gate.completeness import check_completeness
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
fixture_root = Path(__file__).resolve().parent.parent / "tests" / "fixtures"
|
|
10
|
+
run_dir = fixture_root / "sample_pass_run"
|
|
11
|
+
|
|
12
|
+
existing_ci_verdict = "PASS"
|
|
13
|
+
evidence_report = check_completeness(run_dir)
|
|
14
|
+
acceptable_evidence_verdicts = {"complete", "complete_with_quarantine"}
|
|
15
|
+
|
|
16
|
+
if (
|
|
17
|
+
existing_ci_verdict == "PASS"
|
|
18
|
+
and evidence_report.completeness_verdict not in acceptable_evidence_verdicts
|
|
19
|
+
):
|
|
20
|
+
raise SystemExit("CI passed, but the audit trail is incomplete")
|
|
21
|
+
|
|
22
|
+
print(
|
|
23
|
+
{
|
|
24
|
+
"ci_verdict": existing_ci_verdict,
|
|
25
|
+
"evidence_verdict": evidence_report.completeness_verdict,
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Minimal evidence-gate usage."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from evidence_gate.completeness import check_completeness
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main() -> None:
|
|
9
|
+
fixture_root = Path(__file__).resolve().parent.parent / "tests" / "fixtures"
|
|
10
|
+
report = check_completeness(fixture_root / "sample_pass_run")
|
|
11
|
+
print(report.completeness_verdict)
|
|
12
|
+
print(report.unsatisfied_conditions)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "evidence-gate"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Catches green CI verdicts whose audit trail is missing, late, or unclear."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Nick Cunningham", email = "nick.lee.cunningham@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ci", "github-actions", "audit", "provenance", "evidence"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
25
|
+
]
|
|
26
|
+
dependencies = []
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/blazingRadar/evidence-gate"
|
|
30
|
+
Repository = "https://github.com/blazingRadar/evidence-gate"
|
|
31
|
+
Issues = "https://github.com/blazingRadar/evidence-gate/issues"
|
|
32
|
+
Changelog = "https://github.com/blazingRadar/evidence-gate/blob/main/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=8",
|
|
37
|
+
"ruff>=0.5",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/evidence_gate"]
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.sdist]
|
|
44
|
+
exclude = [
|
|
45
|
+
"/.git",
|
|
46
|
+
"/.pytest_cache",
|
|
47
|
+
"/.ruff_cache",
|
|
48
|
+
"/.venv",
|
|
49
|
+
"/.build-venv",
|
|
50
|
+
"/.*-venv",
|
|
51
|
+
"/dist",
|
|
52
|
+
"**/__pycache__",
|
|
53
|
+
"*.pyc",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
pythonpath = ["src"]
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
line-length = 88
|
|
62
|
+
target-version = "py310"
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Deterministic CI audit-trail checks for GitHub Actions evidence bundles."""
|
|
2
|
+
|
|
3
|
+
from evidence_gate.completeness import check_completeness
|
|
4
|
+
from evidence_gate.projection import project_github_run
|
|
5
|
+
from evidence_gate.scope import validate_scope_bundle, write_scope_bundle
|
|
6
|
+
from evidence_gate.timestamps import check_capture_window, record_fetch
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"check_capture_window",
|
|
10
|
+
"check_completeness",
|
|
11
|
+
"project_github_run",
|
|
12
|
+
"record_fetch",
|
|
13
|
+
"validate_scope_bundle",
|
|
14
|
+
"write_scope_bundle",
|
|
15
|
+
]
|