Skip to content

Commit d1cbc10

Browse files
cschanhniemOpenCode
andauthored
feat: Phase V V1 pre-release audit gateway schema (PRG-) -- single gate asserting BRC+PCC+ECI all pass; VALID_GATE_STATUSES (pass/fail/not_evaluated), REQUIRED_GATES (BRC/PCC/ECI), VALID_RELEASE_VERDICTS (approved/blocked); GateResult per gate; dry_lab_only=True; 53 tests (#998) (#998)
Co-authored-by: OpenCode <opencode@example.com>
1 parent fcec149 commit d1cbc10

3 files changed

Lines changed: 463 additions & 1 deletion

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"""PRG- pre-release audit gateway schema.
2+
3+
Single-gate record asserting BRC (batch release checklist), PCC (pipeline
4+
completeness), and ECI (evidence completeness index) all pass before external
5+
sharing. Blocks release until all three gates are green.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from dataclasses import dataclass
11+
12+
VALID_GATE_STATUSES: frozenset[str] = frozenset({"pass", "fail", "not_evaluated"})
13+
VALID_RELEASE_VERDICTS: frozenset[str] = frozenset({"approved", "blocked"})
14+
15+
REQUIRED_GATES: tuple[str, ...] = ("BRC", "PCC", "ECI")
16+
17+
18+
@dataclass
19+
class GateResult:
20+
gate_type: str
21+
artifact_id: str
22+
status: str
23+
details: str
24+
25+
26+
@dataclass
27+
class PreReleaseAuditGateway:
28+
prg_id: str
29+
batch_id: str
30+
pipeline_version: str
31+
gate_results: list[GateResult]
32+
n_gates_total: int
33+
n_gates_passed: int
34+
n_gates_failed: int
35+
release_verdict: str
36+
dry_lab_only: bool
37+
limitations: list[str]
38+
created_at: str
39+
40+
41+
def validate_pre_release_audit_gateway(prg: PreReleaseAuditGateway) -> None:
42+
if not prg.prg_id.startswith("PRG-"):
43+
raise ValueError(f"prg_id must start with 'PRG-': {prg.prg_id!r}")
44+
if not prg.batch_id:
45+
raise ValueError("batch_id must be non-empty")
46+
if not prg.pipeline_version:
47+
raise ValueError("pipeline_version must be non-empty")
48+
for gr in prg.gate_results:
49+
if gr.gate_type not in REQUIRED_GATES:
50+
raise ValueError(
51+
f"gate_type {gr.gate_type!r} must be one of {REQUIRED_GATES}"
52+
)
53+
if gr.status not in VALID_GATE_STATUSES:
54+
raise ValueError(
55+
f"gate status {gr.status!r} not in VALID_GATE_STATUSES"
56+
)
57+
gate_types = [gr.gate_type for gr in prg.gate_results]
58+
for req in REQUIRED_GATES:
59+
if gate_types.count(req) != 1:
60+
raise ValueError(f"Exactly one gate result required for {req!r}")
61+
if prg.release_verdict not in VALID_RELEASE_VERDICTS:
62+
raise ValueError(
63+
f"release_verdict {prg.release_verdict!r} not in VALID_RELEASE_VERDICTS"
64+
)
65+
if not prg.dry_lab_only:
66+
raise ValueError("dry_lab_only must be True")
67+
if not prg.limitations:
68+
raise ValueError("limitations must be non-empty")
69+
if not prg.created_at:
70+
raise ValueError("created_at must be non-empty")
71+
if prg.n_gates_total != len(prg.gate_results):
72+
raise ValueError("n_gates_total must equal len(gate_results)")
73+
n_passed = sum(1 for gr in prg.gate_results if gr.status == "pass")
74+
if prg.n_gates_passed != n_passed:
75+
raise ValueError("n_gates_passed mismatch")
76+
if prg.n_gates_failed != sum(
77+
1 for gr in prg.gate_results if gr.status == "fail"
78+
):
79+
raise ValueError("n_gates_failed mismatch")
80+
81+
82+
def build_pre_release_audit_gateway(
83+
*,
84+
prg_id: str,
85+
batch_id: str,
86+
pipeline_version: str,
87+
brc_artifact_id: str,
88+
brc_verdict: str,
89+
pcc_artifact_id: str,
90+
pcc_grade: str,
91+
eci_artifact_id: str,
92+
eci_grade: str,
93+
limitations: list[str],
94+
created_at: str,
95+
) -> PreReleaseAuditGateway:
96+
"""Build a pre-release audit gateway.
97+
98+
brc_verdict: e.g. "approved", "blocked", "conditional"
99+
pcc_grade: "A"/"B"/"C"/"D" — pass if A or B
100+
eci_grade: "A"/"B"/"C"/"D" — pass if A or B
101+
"""
102+
brc_pass = brc_verdict == "approved"
103+
pcc_pass = pcc_grade in ("A", "B")
104+
eci_pass = eci_grade in ("A", "B")
105+
106+
gate_results = [
107+
GateResult(
108+
gate_type="BRC",
109+
artifact_id=brc_artifact_id,
110+
status="pass" if brc_pass else "fail",
111+
details=f"BRC verdict={brc_verdict!r} — {'pass' if brc_pass else 'fail'}",
112+
),
113+
GateResult(
114+
gate_type="PCC",
115+
artifact_id=pcc_artifact_id,
116+
status="pass" if pcc_pass else "fail",
117+
details=f"PCC grade={pcc_grade!r} — {'pass' if pcc_pass else 'fail (need A or B)'}",
118+
),
119+
GateResult(
120+
gate_type="ECI",
121+
artifact_id=eci_artifact_id,
122+
status="pass" if eci_pass else "fail",
123+
details=f"ECI grade={eci_grade!r} — {'pass' if eci_pass else 'fail (need A or B)'}",
124+
),
125+
]
126+
n_total = len(gate_results)
127+
n_passed = sum(1 for gr in gate_results if gr.status == "pass")
128+
n_failed = sum(1 for gr in gate_results if gr.status == "fail")
129+
verdict = "approved" if n_failed == 0 else "blocked"
130+
131+
prg = PreReleaseAuditGateway(
132+
prg_id=prg_id,
133+
batch_id=batch_id,
134+
pipeline_version=pipeline_version,
135+
gate_results=gate_results,
136+
n_gates_total=n_total,
137+
n_gates_passed=n_passed,
138+
n_gates_failed=n_failed,
139+
release_verdict=verdict,
140+
dry_lab_only=True,
141+
limitations=limitations,
142+
created_at=created_at,
143+
)
144+
validate_pre_release_audit_gateway(prg)
145+
return prg
146+
147+
148+
def format_pre_release_audit_gateway(prg: PreReleaseAuditGateway) -> str:
149+
lines = [
150+
f"Pre-Release Audit Gateway — {prg.prg_id}",
151+
f"Batch: {prg.batch_id} | Pipeline: {prg.pipeline_version}",
152+
f"Verdict: {prg.release_verdict} | Gates: {prg.n_gates_total}",
153+
f"Passed: {prg.n_gates_passed} | Failed: {prg.n_gates_failed}",
154+
"Gate results:",
155+
]
156+
for gr in prg.gate_results:
157+
status_label = "PASS" if gr.status == "pass" else "FAIL"
158+
lines.append(f" [{status_label}] {gr.gate_type} ({gr.artifact_id}): {gr.details}")
159+
lines.append(f"Created: {prg.created_at}")
160+
lines.append(f"Limitations: {'; '.join(prg.limitations)}")
161+
lines.append(f"dry_lab_only: {prg.dry_lab_only}")
162+
return "\n".join(lines)

0 commit comments

Comments
 (0)