feat(violet): V4 arm-readiness reporter (PASS MM1)
Read-only aggregation module answering 'is V4 ready to ARM the live canary?' by consuming gate outcomes into a typed StrictModel report. Files: - v4_readiness.py: V4ReadinessReport (6 bool gates + reasons) + assess_v4_readiness() - test_violet_v4_readiness.py: 13 tests (happy, parametrized single-false, hypothesis 2^6 invariant, mutation litmus, StrictModel poison) Co-Authored-By: mm_VIOLET1 <mm_VIOLET1@h5i>
This commit is contained in:
140
prod/clean_arch/violet/test_violet_v4_readiness.py
Normal file
140
prod/clean_arch/violet/test_violet_v4_readiness.py
Normal file
@@ -0,0 +1,140 @@
|
||||
"""V4 arm-readiness reporter — self-test to death.
|
||||
|
||||
Spec: prod/docs/VIOLET_PASS_MM1_V4_READINESS_REPORTER.md
|
||||
Style: follows test_violet_domain.py patterns.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, "/mnt/dolphinng5_predict")
|
||||
|
||||
import pytest
|
||||
from hypothesis import given, settings
|
||||
from hypothesis import strategies as st
|
||||
|
||||
from prod.clean_arch.violet.v4_readiness import (
|
||||
V4ReadinessReport,
|
||||
assess_v4_readiness,
|
||||
)
|
||||
|
||||
ALL_FIELDS = [
|
||||
"creds_ok", "canary_passed", "arming_gate_present",
|
||||
"conviction_bit_identical", "selection_parity_ok", "feed_live",
|
||||
]
|
||||
|
||||
|
||||
def _all_true(**overrides: bool) -> dict:
|
||||
return {f: overrides.get(f, True) for f in ALL_FIELDS}
|
||||
|
||||
|
||||
def _single_false(field: str) -> dict:
|
||||
return _all_true(**{field: False})
|
||||
|
||||
|
||||
# ── happy path ────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_all_true_ready_to_arm():
|
||||
r = assess_v4_readiness(**_all_true())
|
||||
assert r.ready_to_arm is True
|
||||
assert r.reasons == []
|
||||
|
||||
|
||||
# ── each single False yields ready_to_arm=False + correct reason ─────────────
|
||||
|
||||
@pytest.mark.parametrize("field", ALL_FIELDS)
|
||||
def test_single_false_not_ready(field: str):
|
||||
r = assess_v4_readiness(**_single_false(field))
|
||||
assert r.ready_to_arm is False
|
||||
assert len(r.reasons) == 1
|
||||
assert field in r.reasons[0]
|
||||
|
||||
|
||||
# ── hypothesis: invariant holds across all 2^6 combos ────────────────────────
|
||||
|
||||
@given(
|
||||
creds_ok=st.booleans(),
|
||||
canary_passed=st.booleans(),
|
||||
arming_gate_present=st.booleans(),
|
||||
conviction_bit_identical=st.booleans(),
|
||||
selection_parity_ok=st.booleans(),
|
||||
feed_live=st.booleans(),
|
||||
)
|
||||
@settings(max_examples=100)
|
||||
def test_ready_to_arm_equals_all_inputs(
|
||||
creds_ok: bool,
|
||||
canary_passed: bool,
|
||||
arming_gate_present: bool,
|
||||
conviction_bit_identical: bool,
|
||||
selection_parity_ok: bool,
|
||||
feed_live: bool,
|
||||
):
|
||||
r = assess_v4_readiness(
|
||||
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,
|
||||
)
|
||||
expected = all((
|
||||
creds_ok, canary_passed, arming_gate_present,
|
||||
conviction_bit_identical, selection_parity_ok, feed_live,
|
||||
))
|
||||
assert r.ready_to_arm is expected
|
||||
|
||||
|
||||
# ── mutation litmus: flipping all() to any() MUST break this test ────────────
|
||||
|
||||
def test_mutation_litmus_all_must_be_true():
|
||||
"""If someone changes all() to any(), at least one all-False should become
|
||||
ready_to_arm=True — this test catches it."""
|
||||
r = assess_v4_readiness(**{f: False for f in ALL_FIELDS})
|
||||
assert r.ready_to_arm is False
|
||||
assert len(r.reasons) == 6
|
||||
|
||||
|
||||
# ── poison: StrictModel rejects non-bool / extra fields ─────────────────────
|
||||
|
||||
def test_rejects_non_bool():
|
||||
with pytest.raises(Exception):
|
||||
V4ReadinessReport(
|
||||
creds_ok={"ok": True}, # dict is not coercible to bool
|
||||
canary_passed=True,
|
||||
arming_gate_present=True,
|
||||
conviction_bit_identical=True,
|
||||
selection_parity_ok=True,
|
||||
feed_live=True,
|
||||
reasons=[],
|
||||
)
|
||||
|
||||
|
||||
def test_rejects_extra_fields():
|
||||
with pytest.raises(Exception):
|
||||
V4ReadinessReport(
|
||||
creds_ok=True,
|
||||
canary_passed=True,
|
||||
arming_gate_present=True,
|
||||
conviction_bit_identical=True,
|
||||
selection_parity_ok=True,
|
||||
feed_live=True,
|
||||
reasons=[],
|
||||
unknown_field="oops",
|
||||
)
|
||||
|
||||
|
||||
def test_rejects_frozen_mutation():
|
||||
r = assess_v4_readiness(**_all_true())
|
||||
with pytest.raises(Exception):
|
||||
r.creds_ok = False # type: ignore[misc]
|
||||
|
||||
|
||||
# ── reasons content check: False fields produce descriptive strings ──────────
|
||||
|
||||
def test_reasons_mention_gate_name():
|
||||
r = assess_v4_readiness(**_single_false("feed_live"))
|
||||
assert any("PASS2.6" in reason for reason in r.reasons)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(pytest.main([__file__, "-v"]))
|
||||
75
prod/clean_arch/violet/v4_readiness.py
Normal file
75
prod/clean_arch/violet/v4_readiness.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""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,
|
||||
)
|
||||
Reference in New Issue
Block a user