diff --git a/packages/populace-build/src/populace/build/us_runtime/release_gate_preflight.py b/packages/populace-build/src/populace/build/us_runtime/release_gate_preflight.py index 31f353e..f986e02 100644 --- a/packages/populace-build/src/populace/build/us_runtime/release_gate_preflight.py +++ b/packages/populace-build/src/populace/build/us_runtime/release_gate_preflight.py @@ -597,6 +597,8 @@ def check_smoke_probe_support( when a materially-signed leaf's net weighted sign contradicts its probe's declared ``expected_sign`` (populace#432, ``farm_operations_income`` imputed net-positive where the Schedule-F instrument is loss-heavy). + Probes declaring ``expected_sign="either"`` assert no direction and are + exempt from the sign check. """ probes = tuple(probes) selected_ids = np.asarray(selected_ids) @@ -609,6 +611,10 @@ def check_smoke_probe_support( rows: list[dict[str, Any]] = [] for probe in probes: + # An "either"-sign probe (populace#437: net direction is a frame + # property, not a coverage property) declares no direction, so the + # sign-contradiction check below never applies to it. + directional = probe.expected_sign in ("positive", "negative") expected_positive = probe.expected_sign == "positive" for leaf in probe.binding_inputs: entity = _leaf_entity(base_frame, leaf) @@ -665,7 +671,8 @@ def check_smoke_probe_support( ) net_positive = net > 0 if ( - materially_signed + directional + and materially_signed and pool_support > 0 and net != 0.0 and net_positive != expected_positive diff --git a/packages/populace-build/tests/test_us_release_gate_preflight.py b/packages/populace-build/tests/test_us_release_gate_preflight.py index 617953d..41a73a4 100644 --- a/packages/populace-build/tests/test_us_release_gate_preflight.py +++ b/packages/populace-build/tests/test_us_release_gate_preflight.py @@ -406,6 +406,42 @@ def test__smoke_probe_support__signed_net_contradicts_expected_sign__at_risk() - assert result.at_risks and "contradicts" in result.at_risks[0] +def test__smoke_probe_support__either_sign_probe__no_sign_flag() -> None: + # The same materially-signed net-positive pool as the contradiction test, + # but the probe declares expected_sign="either" (populace#437: direction + # is a frame property, not coverage) — no sign flag, PASS. + pool = [ + {"hid": 1, "syear": 2024, "shh": 11, "chan": "asec", "clone": 0, "farm": 900.0}, + {"hid": 2, "syear": 2024, "shh": 22, "chan": "asec", "clone": 0, "farm": 800.0}, + {"hid": 3, "syear": 2023, "shh": 11, "chan": "puf", "clone": 1, "farm": -600.0}, + {"hid": 4, "syear": 2023, "shh": 44, "chan": "puf", "clone": 1, "farm": -400.0}, + ] + base = _frame(pool) + source = _selection( + [(2024, 11, "asec", 0), (2024, 22, "asec", 0), (2023, 11, "puf", 1)] + ) + mask, _ = source.base_selection_mask(base, mode="frozen_support") + selected = selected_household_ids(base, mask) + + result = check_smoke_probe_support( + base, + selected, + [ + _probe( + leaf="farm_operations_income", expected_sign="either", probe_id="farm" + ) + ], + min_selected_records=1, + ) + + assert result.status == "PASS" + assert not result.at_risks + (row,) = [r for r in result.rows if r["leaf"] == "farm_operations_income"] + assert row["pool_net"] > 0 + assert row["expected_sign"] == "either" + assert row["verdict"] == "supported" + + def test__smoke_probe_support__one_signed_leaf__no_false_sign_flag() -> None: # A one-signed (all-positive) leaf with expected_sign="negative" must NOT be # flagged for a sign contradiction — only genuinely-signed columns are.