"""V4 arm-readiness reporter — DARK, read-only, non-colliding. Aggregates V4 gate outcomes into a typed report answering: "is V4 ready to ARM the live canary?" Does NOT run gates — CONSUMES their results (passed in) and applies fail-closed readiness logic. Spec: prod/docs/VIOLET_PASS_MM1_V4_READINESS_REPORTER.md """ from __future__ import annotations from .domain import StrictModel, typed __all__ = ["V4ReadinessReport", "assess_v4_readiness"] class V4ReadinessReport(StrictModel): """Typed V4 readiness report — all six gates must pass for ready_to_arm.""" creds_ok: bool canary_passed: bool arming_gate_present: bool conviction_bit_identical: bool selection_parity_ok: bool feed_live: bool reasons: list[str] @property def ready_to_arm(self) -> bool: return all(( self.creds_ok, self.canary_passed, self.arming_gate_present, self.conviction_bit_identical, self.selection_parity_ok, self.feed_live, )) @typed def assess_v4_readiness( *, creds_ok: bool, canary_passed: bool, arming_gate_present: bool, conviction_bit_identical: bool, selection_parity_ok: bool, feed_live: bool, ) -> V4ReadinessReport: """Build a V4ReadinessReport from gate outcomes. Fail-closed: any False means ready_to_arm is False. No partial-arm.""" reasons: list[str] = [] if not creds_ok: reasons.append("creds_ok: PASS2.1 not satisfied") if not canary_passed: reasons.append("canary_passed: PASS2.3 canary not round-tripped flat") if not arming_gate_present: reasons.append("arming_gate_present: PASS2.2 not satisfied") if not conviction_bit_identical: reasons.append("conviction_bit_identical: PASS2.5 fidelity mismatch") if not selection_parity_ok: reasons.append("selection_parity_ok: PASS2.7/3.1 parity mismatch") if not feed_live: reasons.append("feed_live: PASS2.6 HZ feed stale or deaf") return V4ReadinessReport( creds_ok=creds_ok, canary_passed=canary_passed, arming_gate_present=arming_gate_present, conviction_bit_identical=conviction_bit_identical, selection_parity_ok=selection_parity_ok, feed_live=feed_live, reasons=reasons, )