From 737c1554a4d6c0ae65def21ecc75441ff71ee931 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 23 Sep 2026 06:13:51 -0400 Subject: [PATCH] Record strike_benefits as a reviewed degenerate input No respondent in the pinned public ASEC person files reports other-income source OI_OFF == 12 ("strike benefits" in the ASEC 2024 public use data dictionary): pppub23, pppub24 and pppub25 carry codes 0-10, 13, 19 and 20 only. The PUF half has no strike-benefit field, so alimony.py's code-12 split is identically zero, and route A's fresh-base release (2026-09-23) refused at the pre-solve degenerate-input gate: "strike_benefits: every observed value equals the engine default (0)". The mapping is right (policyengine-us-data uses the same code), so this is the gate's documented remedy, a reviewed exclusion, not a code change. A vintage with a code-12 reporter makes the entry stale and fails the gate. An archive-gated test (MICROCOSM_ASEC_ARCHIVE_DIR) checks the zero-reporter claim against the three official archives. Co-Authored-By: Claude Opus 5.5 --- ...trike-benefits-reviewed-exclusion.fixed.md | 1 + .../tests/test_us_documented_absent_inputs.py | 32 +++++++++++++++++++ tools/build_us_fiscal_refresh_release.py | 9 ++++++ 3 files changed, 42 insertions(+) create mode 100644 changelog.d/us-strike-benefits-reviewed-exclusion.fixed.md diff --git a/changelog.d/us-strike-benefits-reviewed-exclusion.fixed.md b/changelog.d/us-strike-benefits-reviewed-exclusion.fixed.md new file mode 100644 index 0000000000..317913f4bd --- /dev/null +++ b/changelog.d/us-strike-benefits-reviewed-exclusion.fixed.md @@ -0,0 +1 @@ +`strike_benefits` joins the reviewed degenerate-input exclusions: no respondent in the pinned public ASEC person files (income years 2022-2024) reports other-income source code 12 (strike benefits), so the column is identically zero and a fresh-base release refused at the pre-solve degenerate-input gate. The OI_OFF mapping is unchanged and correct; a vintage with a code-12 reporter makes the entry stale and fails the gate again. diff --git a/packages/microcosm-build/tests/test_us_documented_absent_inputs.py b/packages/microcosm-build/tests/test_us_documented_absent_inputs.py index 1b14545843..63bd70f540 100644 --- a/packages/microcosm-build/tests/test_us_documented_absent_inputs.py +++ b/packages/microcosm-build/tests/test_us_documented_absent_inputs.py @@ -71,3 +71,35 @@ def test_register_is_disjoint_from_the_degenerate_exclusions() -> None: assert not set(builder.US_DOCUMENTED_ABSENT_INPUTS) & set( builder.US_DEGENERATE_INPUT_REVIEWED_EXCLUSIONS ) + + +def test_strike_benefits_exclusion_rests_on_zero_code_12_reporters() -> None: + """The ``strike_benefits`` reviewed exclusion says no pinned ASEC person + file carries OI_OFF code 12 (strike benefits). Check that against the + official archives when ``MICROCOSM_ASEC_ARCHIVE_DIR`` holds them + (asecpub23csv.zip, asecpub24csv.zip, asecpub25csv.zip); the archives are + public but large, so no CI lane carries them.""" + + import csv + import io + import os + import zipfile + from pathlib import Path + + import pytest + + builder = _load_builder_module() + assert "strike_benefits" in builder.US_DEGENERATE_INPUT_REVIEWED_EXCLUSIONS + root = os.environ.get("MICROCOSM_ASEC_ARCHIVE_DIR", "") + archives = [Path(root) / f"asecpub{year}csv.zip" for year in (23, 24, 25)] + if not root or not all(path.exists() for path in archives): + pytest.skip("set MICROCOSM_ASEC_ARCHIVE_DIR to the pinned ASEC archives") + for archive in archives: + member = f"pppub{archive.name[7:9]}.csv" + with zipfile.ZipFile(archive) as bundle, bundle.open(member) as raw: + reader = csv.reader(io.TextIOWrapper(raw, encoding="utf-8")) + header = next(reader) + column = header.index("OI_OFF") + codes = {row[column] for row in reader} + assert "12" not in codes, f"{member} has an OI_OFF == 12 reporter" + assert "20" in codes, f"{member} lost the alimony code; check the layout" diff --git a/tools/build_us_fiscal_refresh_release.py b/tools/build_us_fiscal_refresh_release.py index c6dda36861..30bca162d7 100644 --- a/tools/build_us_fiscal_refresh_release.py +++ b/tools/build_us_fiscal_refresh_release.py @@ -827,6 +827,15 @@ def _automatic_gc_suspended(): "Combined partnership/S-corp income is carried in partnership_income " "in pre-PUF-support bases; the S-corp leaf is constant zero there." ), + "strike_benefits": ( + "No respondent in the pinned public ASEC files reports strike benefits: " + "OI_OFF code 12 ('strike benefits', ASEC 2024 public use data " + "dictionary) has zero person rows in pppub23, pppub24 and pppub25 " + "(income years 2022-2024), and the PUF half carries no strike-benefit " + "field, so the OI_OFF == 12 split in alimony.py is identically zero. " + "The mapping is correct; a vintage with a code-12 reporter makes this " + "entry stale and fails the gate." + ), } #: Person inputs SNAP work-requirement rules read that have NO CPS ASEC