From f842816585f5b4856650cdf0b7c26212758a46ce Mon Sep 17 00:00:00 2001 From: OpenCode Date: Fri, 10 Jul 2026 15:15:43 +0700 Subject: [PATCH] feat: Phase U U2 batch release checklist schema (BRC-) -- machine-verifiable gate before wet-lab synthesis release; VALID_PCC_GRADES (A-D), PASSING_PCC_GRADES (A/B), BLOCKING_BEG_VERDICTS (critical_gaps/no_families); gates on PCC grade>=B + BEG verdict + SAT entries>=1; 3 ChecklistItem fields; dry_lab_only=True; 60 tests (#992) --- .../evidence/batch_release_checklist.py | 187 ++++++++++ .../evidence/test_batch_release_checklist.py | 332 ++++++++++++++++++ tests/test_test_count_regression.py | 2 +- 3 files changed, 520 insertions(+), 1 deletion(-) create mode 100644 src/openamp_foundry/evidence/batch_release_checklist.py create mode 100644 tests/evidence/test_batch_release_checklist.py diff --git a/src/openamp_foundry/evidence/batch_release_checklist.py b/src/openamp_foundry/evidence/batch_release_checklist.py new file mode 100644 index 00000000..0bed5191 --- /dev/null +++ b/src/openamp_foundry/evidence/batch_release_checklist.py @@ -0,0 +1,187 @@ +"""BRC- batch release checklist schema. + +Machine-verifiable checklist that must pass before a batch is released for +wet-lab synthesis. Gates on PCC grade >= B, no critical BEG gaps, at least +one SAT entry, and all required artifacts present. +""" + +from __future__ import annotations + +from dataclasses import dataclass + +VALID_PCC_GRADES: frozenset[str] = frozenset({"A", "B", "C", "D"}) +PASSING_PCC_GRADES: frozenset[str] = frozenset({"A", "B"}) + +VALID_BEG_VERDICTS: frozenset[str] = frozenset({ + "no_gaps", "partial_gaps", "critical_gaps", "no_families", +}) +BLOCKING_BEG_VERDICTS: frozenset[str] = frozenset({"critical_gaps", "no_families"}) + +VALID_RELEASE_VERDICTS: frozenset[str] = frozenset({ + "approved", "blocked", "conditional", +}) + + +@dataclass +class ChecklistItem: + item_id: str + description: str + passed: bool + details: str + + +@dataclass +class BatchReleaseChecklist: + brc_id: str + batch_id: str + pipeline_version: str + pcc_id: str + pcc_grade: str + beg_id: str + beg_verdict: str + sat_id: str + n_sat_entries: int + checklist_items: list[ChecklistItem] + n_items_total: int + n_items_passed: int + n_items_failed: int + release_verdict: str + dry_lab_only: bool + limitations: list[str] + created_at: str + + +def _compute_release_verdict(items: list[ChecklistItem]) -> str: + all_passed = all(it.passed for it in items) + if all_passed: + return "approved" + any_failed = any(not it.passed for it in items) + if any_failed: + return "blocked" + return "conditional" + + +def validate_batch_release_checklist(brc: BatchReleaseChecklist) -> None: + if not brc.brc_id.startswith("BRC-"): + raise ValueError(f"brc_id must start with 'BRC-': {brc.brc_id!r}") + if not brc.batch_id: + raise ValueError("batch_id must be non-empty") + if not brc.pipeline_version: + raise ValueError("pipeline_version must be non-empty") + if not brc.pcc_id.startswith("PCC-"): + raise ValueError(f"pcc_id must start with 'PCC-': {brc.pcc_id!r}") + if brc.pcc_grade not in VALID_PCC_GRADES: + raise ValueError(f"pcc_grade {brc.pcc_grade!r} not in VALID_PCC_GRADES") + if not brc.beg_id.startswith("BEG-"): + raise ValueError(f"beg_id must start with 'BEG-': {brc.beg_id!r}") + if brc.beg_verdict not in VALID_BEG_VERDICTS: + raise ValueError(f"beg_verdict {brc.beg_verdict!r} not in VALID_BEG_VERDICTS") + if not brc.sat_id.startswith("SAT-"): + raise ValueError(f"sat_id must start with 'SAT-': {brc.sat_id!r}") + if brc.n_sat_entries < 0: + raise ValueError("n_sat_entries must be >= 0") + if brc.release_verdict not in VALID_RELEASE_VERDICTS: + raise ValueError( + f"release_verdict {brc.release_verdict!r} not in VALID_RELEASE_VERDICTS" + ) + if not brc.dry_lab_only: + raise ValueError("dry_lab_only must be True") + if not brc.limitations: + raise ValueError("limitations must be non-empty") + if not brc.created_at: + raise ValueError("created_at must be non-empty") + if brc.n_items_total != len(brc.checklist_items): + raise ValueError("n_items_total must equal len(checklist_items)") + n_passed = sum(1 for it in brc.checklist_items if it.passed) + if brc.n_items_passed != n_passed: + raise ValueError("n_items_passed mismatch") + if brc.n_items_failed != brc.n_items_total - n_passed: + raise ValueError("n_items_failed mismatch") + + +def build_batch_release_checklist( + *, + brc_id: str, + batch_id: str, + pipeline_version: str, + pcc_id: str, + pcc_grade: str, + beg_id: str, + beg_verdict: str, + sat_id: str, + n_sat_entries: int, + limitations: list[str], + created_at: str, +) -> BatchReleaseChecklist: + items: list[ChecklistItem] = [] + + pcc_pass = pcc_grade in PASSING_PCC_GRADES + items.append(ChecklistItem( + item_id="PCC-GRADE", + description="PCC grade must be A or B", + passed=pcc_pass, + details=f"PCC grade is {pcc_grade!r} — {'pass' if pcc_pass else 'fail'}", + )) + + beg_pass = beg_verdict not in BLOCKING_BEG_VERDICTS + items.append(ChecklistItem( + item_id="BEG-VERDICT", + description="BEG verdict must not be critical_gaps or no_families", + passed=beg_pass, + details=f"BEG verdict is {beg_verdict!r} — {'pass' if beg_pass else 'fail'}", + )) + + sat_pass = n_sat_entries >= 1 + items.append(ChecklistItem( + item_id="SAT-ENTRIES", + description="At least one SAT entry must exist", + passed=sat_pass, + details=f"SAT entries: {n_sat_entries} — {'pass' if sat_pass else 'fail'}", + )) + + n_total = len(items) + n_passed = sum(1 for it in items if it.passed) + n_failed = n_total - n_passed + verdict = _compute_release_verdict(items) + + brc = BatchReleaseChecklist( + brc_id=brc_id, + batch_id=batch_id, + pipeline_version=pipeline_version, + pcc_id=pcc_id, + pcc_grade=pcc_grade, + beg_id=beg_id, + beg_verdict=beg_verdict, + sat_id=sat_id, + n_sat_entries=n_sat_entries, + checklist_items=items, + n_items_total=n_total, + n_items_passed=n_passed, + n_items_failed=n_failed, + release_verdict=verdict, + dry_lab_only=True, + limitations=limitations, + created_at=created_at, + ) + validate_batch_release_checklist(brc) + return brc + + +def format_batch_release_checklist(brc: BatchReleaseChecklist) -> str: + lines = [ + f"Batch Release Checklist — {brc.brc_id}", + f"Batch: {brc.batch_id} | Pipeline: {brc.pipeline_version}", + f"Verdict: {brc.release_verdict} | Items: {brc.n_items_total}", + f"Passed: {brc.n_items_passed} | Failed: {brc.n_items_failed}", + f"PCC: {brc.pcc_id} (grade={brc.pcc_grade}) | BEG: {brc.beg_id} ({brc.beg_verdict})", + f"SAT: {brc.sat_id} (entries={brc.n_sat_entries})", + "Checklist:", + ] + for item in brc.checklist_items: + status = "PASS" if item.passed else "FAIL" + lines.append(f" [{status}] {item.item_id}: {item.description}") + lines.append(f" {item.details}") + lines.append(f"Created: {brc.created_at}") + lines.append(f"Limitations: {'; '.join(brc.limitations)}") + lines.append(f"dry_lab_only: {brc.dry_lab_only}") + return "\n".join(lines) diff --git a/tests/evidence/test_batch_release_checklist.py b/tests/evidence/test_batch_release_checklist.py new file mode 100644 index 00000000..e696af0e --- /dev/null +++ b/tests/evidence/test_batch_release_checklist.py @@ -0,0 +1,332 @@ +"""Tests for BRC- batch release checklist schema.""" + +import pytest +from openamp_foundry.evidence.batch_release_checklist import ( + BatchReleaseChecklist, + ChecklistItem, + VALID_PCC_GRADES, + VALID_BEG_VERDICTS, + VALID_RELEASE_VERDICTS, + PASSING_PCC_GRADES, + BLOCKING_BEG_VERDICTS, + build_batch_release_checklist, + format_batch_release_checklist, + validate_batch_release_checklist, +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +def _build(**kwargs): + defaults = dict( + brc_id="BRC-001", + batch_id="BATCH-01", + pipeline_version="v1.0", + pcc_id="PCC-001", + pcc_grade="A", + beg_id="BEG-001", + beg_verdict="no_gaps", + sat_id="SAT-001", + n_sat_entries=3, + limitations=["dry-lab only"], + created_at="2026-07-10", + ) + defaults.update(kwargs) + return build_batch_release_checklist(**defaults) + + +# --------------------------------------------------------------------------- +# 1. Constants +# --------------------------------------------------------------------------- + + +def test_valid_pcc_grades_is_frozenset(): + assert isinstance(VALID_PCC_GRADES, frozenset) + + +def test_valid_pcc_grades_contains_a(): + assert "A" in VALID_PCC_GRADES + + +def test_valid_pcc_grades_contains_b(): + assert "B" in VALID_PCC_GRADES + + +def test_valid_pcc_grades_contains_c(): + assert "C" in VALID_PCC_GRADES + + +def test_valid_pcc_grades_contains_d(): + assert "D" in VALID_PCC_GRADES + + +def test_passing_pcc_grades_is_frozenset(): + assert isinstance(PASSING_PCC_GRADES, frozenset) + + +def test_passing_pcc_grades_contains_a_and_b(): + assert PASSING_PCC_GRADES == frozenset({"A", "B"}) + + +def test_valid_beg_verdicts_is_frozenset(): + assert isinstance(VALID_BEG_VERDICTS, frozenset) + + +def test_blocking_beg_verdicts_includes_critical_gaps(): + assert "critical_gaps" in BLOCKING_BEG_VERDICTS + + +def test_blocking_beg_verdicts_includes_no_families(): + assert "no_families" in BLOCKING_BEG_VERDICTS + + +def test_valid_release_verdicts_is_frozenset(): + assert isinstance(VALID_RELEASE_VERDICTS, frozenset) + + +def test_valid_release_verdicts_contains_approved(): + assert "approved" in VALID_RELEASE_VERDICTS + + +def test_valid_release_verdicts_contains_blocked(): + assert "blocked" in VALID_RELEASE_VERDICTS + + +def test_valid_release_verdicts_contains_conditional(): + assert "conditional" in VALID_RELEASE_VERDICTS + + +# --------------------------------------------------------------------------- +# 2. build – happy paths +# --------------------------------------------------------------------------- + + +def test_build_returns_batch_release_checklist(): + assert isinstance(_build(), BatchReleaseChecklist) + + +def test_build_brc_id_stored(): + assert _build().brc_id == "BRC-001" + + +def test_build_batch_id_stored(): + assert _build().batch_id == "BATCH-01" + + +def test_build_pipeline_version_stored(): + assert _build().pipeline_version == "v1.0" + + +def test_build_dry_lab_only_true(): + assert _build().dry_lab_only is True + + +def test_build_approved_when_all_pass(): + r = _build() + assert r.release_verdict == "approved" + + +def test_build_blocked_when_pcc_grade_c(): + r = _build(pcc_grade="C") + assert r.release_verdict == "blocked" + + +def test_build_blocked_when_pcc_grade_d(): + r = _build(pcc_grade="D") + assert r.release_verdict == "blocked" + + +def test_build_blocked_when_critical_gaps(): + r = _build(beg_verdict="critical_gaps") + assert r.release_verdict == "blocked" + + +def test_build_blocked_when_no_families(): + r = _build(beg_verdict="no_families") + assert r.release_verdict == "blocked" + + +def test_build_blocked_when_no_sat_entries(): + r = _build(n_sat_entries=0) + assert r.release_verdict == "blocked" + + +def test_build_n_items_total_is_3(): + assert _build().n_items_total == 3 + + +def test_build_n_items_passed_3_when_all_pass(): + r = _build() + assert r.n_items_passed == 3 + + +def test_build_n_items_failed_0_when_all_pass(): + r = _build() + assert r.n_items_failed == 0 + + +def test_build_n_items_failed_1_on_bad_pcc(): + r = _build(pcc_grade="C") + assert r.n_items_failed == 1 + + +def test_build_checklist_items_are_checklist_item(): + for item in _build().checklist_items: + assert isinstance(item, ChecklistItem) + + +def test_build_pcc_grade_item_passes_for_a(): + r = _build(pcc_grade="A") + pcc_item = next(it for it in r.checklist_items if it.item_id == "PCC-GRADE") + assert pcc_item.passed is True + + +def test_build_pcc_grade_item_passes_for_b(): + r = _build(pcc_grade="B") + pcc_item = next(it for it in r.checklist_items if it.item_id == "PCC-GRADE") + assert pcc_item.passed is True + + +def test_build_pcc_grade_item_fails_for_c(): + r = _build(pcc_grade="C") + pcc_item = next(it for it in r.checklist_items if it.item_id == "PCC-GRADE") + assert pcc_item.passed is False + + +def test_build_beg_verdict_item_passes_for_no_gaps(): + r = _build(beg_verdict="no_gaps") + beg_item = next(it for it in r.checklist_items if it.item_id == "BEG-VERDICT") + assert beg_item.passed is True + + +def test_build_beg_verdict_item_passes_for_partial_gaps(): + r = _build(beg_verdict="partial_gaps") + beg_item = next(it for it in r.checklist_items if it.item_id == "BEG-VERDICT") + assert beg_item.passed is True + + +def test_build_sat_entries_item_passes_when_positive(): + r = _build(n_sat_entries=1) + sat_item = next(it for it in r.checklist_items if it.item_id == "SAT-ENTRIES") + assert sat_item.passed is True + + +def test_build_sat_entries_item_fails_when_zero(): + r = _build(n_sat_entries=0) + sat_item = next(it for it in r.checklist_items if it.item_id == "SAT-ENTRIES") + assert sat_item.passed is False + + +def test_build_limitations_stored(): + assert _build().limitations == ["dry-lab only"] + + +def test_build_created_at_stored(): + assert _build().created_at == "2026-07-10" + + +# --------------------------------------------------------------------------- +# 3. validate – rejection cases +# --------------------------------------------------------------------------- + + +def test_validate_rejects_bad_brc_id_prefix(): + with pytest.raises(ValueError, match="BRC-"): + _build(brc_id="BAD-001") + + +def test_validate_rejects_empty_batch_id(): + with pytest.raises(ValueError): + _build(batch_id="") + + +def test_validate_rejects_empty_pipeline_version(): + with pytest.raises(ValueError): + _build(pipeline_version="") + + +def test_validate_rejects_bad_pcc_id_prefix(): + with pytest.raises(ValueError, match="PCC-"): + _build(pcc_id="BAD-001") + + +def test_validate_rejects_invalid_pcc_grade(): + with pytest.raises(ValueError, match="pcc_grade"): + _build(pcc_grade="Z") + + +def test_validate_rejects_bad_beg_id_prefix(): + with pytest.raises(ValueError, match="BEG-"): + _build(beg_id="BAD-001") + + +def test_validate_rejects_invalid_beg_verdict(): + with pytest.raises(ValueError, match="beg_verdict"): + _build(beg_verdict="UNKNOWN") + + +def test_validate_rejects_bad_sat_id_prefix(): + with pytest.raises(ValueError, match="SAT-"): + _build(sat_id="BAD-001") + + +def test_validate_rejects_empty_limitations(): + with pytest.raises(ValueError, match="limitations"): + _build(limitations=[]) + + +def test_validate_rejects_empty_created_at(): + with pytest.raises(ValueError): + _build(created_at="") + + +# --------------------------------------------------------------------------- +# 4. format +# --------------------------------------------------------------------------- + + +def test_format_contains_brc_id(): + assert "BRC-001" in format_batch_release_checklist(_build()) + + +def test_format_contains_batch_id(): + assert "BATCH-01" in format_batch_release_checklist(_build()) + + +def test_format_contains_verdict(): + assert "approved" in format_batch_release_checklist(_build()) + + +def test_format_contains_pcc_grade(): + assert "grade=A" in format_batch_release_checklist(_build()) + + +def test_format_contains_beg_verdict(): + assert "no_gaps" in format_batch_release_checklist(_build()) + + +def test_format_contains_sat_entries(): + assert "entries=3" in format_batch_release_checklist(_build()) + + +def test_format_contains_pass_markers(): + assert "[PASS]" in format_batch_release_checklist(_build()) + + +def test_format_contains_fail_marker_on_blocked(): + r = _build(pcc_grade="D") + assert "[FAIL]" in format_batch_release_checklist(r) + + +def test_format_contains_limitations(): + assert "dry-lab only" in format_batch_release_checklist(_build()) + + +def test_format_contains_dry_lab_only(): + assert "dry_lab_only: True" in format_batch_release_checklist(_build()) + + +def test_format_is_string(): + assert isinstance(format_batch_release_checklist(_build()), str) diff --git a/tests/test_test_count_regression.py b/tests/test_test_count_regression.py index c245347e..51a43eb8 100644 --- a/tests/test_test_count_regression.py +++ b/tests/test_test_count_regression.py @@ -4,7 +4,7 @@ import sys import math -BASELINE = 9891 +BASELINE = 9954 def test_test_count_regression():