From 937bfd4ef3dcc7e67314b507177f46e787bf7aee Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 12 Jul 2026 22:05:22 -0400 Subject: [PATCH 1/4] Wire core SSA SSI recipients and state payments into the US fiscal target registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The retired us-data/eCPS pipeline calibrated SSI recipients (nation/ssa/ssi_recipients) but the populace registry silently dropped them. The v8 consumer feed carries ssa_supplement.ssi_recipients (7,404,820 national plus 51 states) and ssa_supplement.ssi_payments by area/category, none of which compiled — only the six oasdi_ssi_payments aggregates did. Add a source_name == "ssa" dispatch (_ssa_ssi_reference_from_fact) mirroring the census_pep / census_stc idiom: - oasdi_ssi_payments: delegated unchanged to _direct_reference_from_fact, so the six certified ssa targets (social_security_total ... ssi_total) never regress. - ssi_recipients.by_area_category "total": indicator sum of engine ssi receipt (role ssi_recipients), national and state (state_fips) grain. - ssi_payments.by_area_category "total": dollar sum of engine ssi (role ssi_state_payments), state rows only — the national all_areas_total duplicates the OASDI-table ssi_total already compiled. - aged/blind/disabled eligibility categories: dropped here, documented as reviewed exclusions (PolicyEngine-US has no SSI eligibility-category split). Registry 5,586 -> 5,689 specs, deterministic. Engine-computed counterparts (ssi), not reported-survey proxies. Six targeted tests. Co-Authored-By: Claude Fable 5 --- .../build/us_runtime/fiscal_targets.py | 116 +++++++++++ .../tests/test_us_fiscal_targets.py | 184 ++++++++++++++++++ 2 files changed, 300 insertions(+) diff --git a/packages/populace-build/src/populace/build/us_runtime/fiscal_targets.py b/packages/populace-build/src/populace/build/us_runtime/fiscal_targets.py index 7bffed8a..0622d050 100644 --- a/packages/populace-build/src/populace/build/us_runtime/fiscal_targets.py +++ b/packages/populace-build/src/populace/build/us_runtime/fiscal_targets.py @@ -2043,6 +2043,8 @@ def _reference_from_ledger_fact( include_congressional_district_targets ), ) + if source_name == "ssa": + return _ssa_ssi_reference_from_fact(fact, target_period=target_period) if source_name == "jct": return None return _direct_reference_from_fact(fact, target_period=target_period) @@ -2330,6 +2332,120 @@ def _population_age_reference_from_fact( ) +# SSA by-area record sets carry an eligibility category (total/aged/blind/ +# disabled) fused into ``layout.groupby_value_id`` (``all_areas_total``, +# ``alabama_aged`` …). Only the all-category ``total`` has a PolicyEngine-US +# counterpart: the model computes SSI receipt and amount but not the SSI +# eligibility-category split, so aged/blind/disabled sub-rows are reviewed +# exclusions in the target-parity manifest rather than silent drops. +_SSA_OASDI_SSI_PAYMENTS_RECORD_SET_TOKEN = "oasdi_ssi_payments" +_SSA_SSI_CALIBRATABLE_AREA_CATEGORY = "total" +#: Engine-computed SSI counterpart both by-area families bind through. Receipt +#: is an indicator sum of person-level ``ssi`` (recipients); federal payments +#: are its dollar sum — the same variable the national ``ssi_total`` payment +#: target already materializes. +_SSA_SSI_BASE_VARIABLE = "ssi" +SSA_SSI_RECIPIENTS_TARGET_ROLE = "ssi_recipients" +SSA_SSI_STATE_PAYMENTS_TARGET_ROLE = "ssi_state_payments" + + +def _ssa_area_category(fact: object) -> str: + """The SSA by-area eligibility category (``total``/``aged``/``blind``/...). + + Encoded as the suffix of ``layout.groupby_value_id``; empty when the fact is + not an area-category row (e.g. the national OASDI payment aggregates). + """ + groupby_value_id = _str_at(fact, "layout", "groupby_value_id") + if not groupby_value_id: + return "" + return groupby_value_id.rsplit("_", 1)[-1] + + +def _ssa_ssi_reference_from_fact( + fact: object, + *, + target_period: int | str, +) -> LedgerTargetReference | None: + """Compile an SSA (``source_name == "ssa"``) fact into a US target. + + Three SSA record sets reach here: + + - ``oasdi_ssi_payments`` — the six national OASDI benefit and SSI payment + aggregates. Delegated unchanged to :func:`_direct_reference_from_fact` so + the standing ``DIRECT_LEDGER_TARGETS`` ``ssa`` mappings (family ``ssa``: + ``social_security_total`` … ``ssi_total``) never regress. + - ``ssi_recipients.by_area_category`` — SSI recipients (a count). The + all-category ``total`` maps to an indicator sum of engine ``ssi`` receipt + at national and state grain (role ``ssi_recipients``). This is the + ``nation/ssa/ssi_recipients`` administrative family the retired + us-data/eCPS pipeline calibrated to that the populace registry dropped. + - ``ssi_payments.by_area_category`` — SSI federal payments (a dollar sum). + The national ``all_areas_total`` equals the ``oasdi_ssi_payments`` + ``ssi_payments`` figure already compiled as ``ssi_total``, so only the + state ``total`` rows the national OASDI table does not carry are compiled + here (role ``ssi_state_payments``). + + Non-``total`` eligibility categories (aged/blind/disabled) return ``None``; + PolicyEngine-US does not model the SSI eligibility-category split, so those + sub-rows are reviewed exclusions in the target-parity manifest. + """ + record_set_id = _normalized_record_set_id(_str_at(fact, "layout", "record_set_id")) + if _SSA_OASDI_SSI_PAYMENTS_RECORD_SET_TOKEN in record_set_id: + return _direct_reference_from_fact(fact, target_period=target_period) + + measure_id = _measure_id(fact) + if measure_id == "recipient_count": + target_role = SSA_SSI_RECIPIENTS_TARGET_ROLE + measure_mode = "indicator_sum" + elif measure_id == "payment_amount": + target_role = SSA_SSI_STATE_PAYMENTS_TARGET_ROLE + measure_mode = "sum" + else: + return None + + if _ssa_area_category(fact) != _SSA_SSI_CALIBRATABLE_AREA_CATEGORY: + return None + + geography_level = _geography_level(fact) + if geography_level == "state": + state_fips = _state_fips(fact) + if state_fips is None: + return None + elif geography_level == "country": + # The national by-area SSI payment duplicates the OASDI-table ssi_total + # already compiled from oasdi_ssi_payments; keep only the state rows. + if measure_id == "payment_amount": + return None + state_fips = None + else: + return None + + source_record_id = _source_record_id(fact) + if not source_record_id: + return None + + metadata = { + "materializer": "policyengine_variable", + "measure_mode": measure_mode, + "base_variable": _SSA_SSI_BASE_VARIABLE, + "target_role": target_role, + "source_measure_id": measure_id, + "source_period": str(_period_value(fact)), + "target_period": str(target_period), + } + if state_fips: + metadata["state_fips"] = state_fips + return LedgerTargetReference( + name=source_record_id, + ledger_source_record_id=source_record_id, + entity="household", + measure=source_record_id, + period=target_period, + family="ssa", + metadata=metadata, + ) + + def _direct_reference_from_fact( fact: object, *, diff --git a/packages/populace-build/tests/test_us_fiscal_targets.py b/packages/populace-build/tests/test_us_fiscal_targets.py index 77594f2a..66c2889c 100644 --- a/packages/populace-build/tests/test_us_fiscal_targets.py +++ b/packages/populace-build/tests/test_us_fiscal_targets.py @@ -1124,6 +1124,190 @@ def _usda_snap_caseload_fact( } +def _ssa_ssi_by_area_fact( + *, + measure_id: str, + value: float, + record_set_concept: str, + area_category: str, + geography_level: str = "country", + geography_id: str = "0100000US", +) -> dict[str, object]: + """A synthetic SSA OASDI/SSI ledger fact (source_name ``ssa``). + + ``record_set_concept`` is one of ``ssi_recipients.by_area_category``, + ``ssi_payments.by_area_category`` or ``oasdi_ssi_payments``; + ``area_category`` is the fused area+category groupby value id + (``all_areas_total``, ``alabama_aged``, ``ssi_payments`` …). + """ + record_set_id = f"ssa_supplement.cy2024.{record_set_concept}" + source_record_id = f"{record_set_id}.{area_category}.{measure_id}" + return _dynamic_ledger_fact( + source_record_id=source_record_id, + source_name="ssa", + measure_id=measure_id, + value=value, + geography_level=geography_level, + geography_id=geography_id, + groupby_dimension="ssa_supplement.area_category", + groupby_value_id=area_category, + layout_record_set_id=record_set_id, + ) + + +def test_ssa_ssi_recipients_national_fact_maps_to_ssi_indicator() -> None: + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="recipient_count", + value=7_404_820, + record_set_concept="ssi_recipients.by_area_category", + area_category="all_areas_total", + ), + ] + ) + + spec = {spec.name: spec for spec in registry.specs}[ + "ssa_supplement.cy2024.ssi_recipients.by_area_category" + ".all_areas_total.recipient_count" + ] + assert spec.family == "ssa" + assert spec.value == 7_404_820 + assert spec.metadata["target_role"] == "ssi_recipients" + assert spec.metadata["base_variable"] == "ssi" + assert spec.metadata["measure_mode"] == "indicator_sum" + assert "state_fips" not in spec.metadata + + +def test_ssa_ssi_recipients_state_fact_compiles_with_state_fips() -> None: + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="recipient_count", + value=137_206, + record_set_concept="ssi_recipients.by_area_category", + area_category="alabama_total", + geography_level="state", + geography_id="0400000US01", + ), + ] + ) + + spec = {spec.name: spec for spec in registry.specs}[ + "ssa_supplement.cy2024.ssi_recipients.by_area_category" + ".alabama_total.recipient_count" + ] + assert spec.family == "ssa" + assert spec.metadata["target_role"] == "ssi_recipients" + assert spec.metadata["measure_mode"] == "indicator_sum" + assert spec.metadata["base_variable"] == "ssi" + assert spec.metadata["state_fips"] == "01" + + +def test_ssa_ssi_recipient_eligibility_category_subrows_are_not_compiled() -> None: + # PolicyEngine-US does not model the aged/blind/disabled SSI eligibility + # split, so only the all-category "total" is a model counterpart; the + # sub-rows are reviewed exclusions in the target-parity manifest. + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="recipient_count", + value=1_161_623, + record_set_concept="ssi_recipients.by_area_category", + area_category="all_areas_aged", + ), + ] + ) + + assert not [spec for spec in registry.specs if "ssi_recipients" in spec.name] + + +def test_ssa_ssi_state_payment_fact_compiles_as_dollar_sum() -> None: + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="payment_amount", + value=954_623_000, + record_set_concept="ssi_payments.by_area_category", + area_category="alabama_total", + geography_level="state", + geography_id="0400000US01", + ), + ] + ) + + spec = {spec.name: spec for spec in registry.specs}[ + "ssa_supplement.cy2024.ssi_payments.by_area_category" + ".alabama_total.payment_amount" + ] + assert spec.family == "ssa" + assert spec.metadata["target_role"] == "ssi_state_payments" + assert spec.metadata["measure_mode"] == "sum" + assert spec.metadata["base_variable"] == "ssi" + assert spec.metadata["state_fips"] == "01" + + +def test_ssa_ssi_national_payment_by_area_is_not_double_counted() -> None: + # The national by-area SSI payment equals the OASDI-table ssi_payments + # figure already compiled as ssi_total; only the state rows are compiled. + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="payment_amount", + value=63_079_493_000, + record_set_concept="ssi_payments.by_area_category", + area_category="all_areas_total", + ), + ] + ) + + assert not [ + spec for spec in registry.specs if "ssi_payments.by_area_category" in spec.name + ] + + +def test_ssa_oasdi_ssi_payment_aggregates_still_compile() -> None: + # Regression guard: the six national OASDI + SSI payment aggregates keep the + # standing DIRECT_LEDGER_TARGETS mappings (family ssa) through the new SSA + # dispatch, so the SSI recipient/payment extension never regresses them. + registry = compile_us_fiscal_target_registry( + [ + *packaged_reference_facts(), + _ssa_ssi_by_area_fact( + measure_id="payment_amount", + value=1_471_195_000_000, + record_set_concept="oasdi_ssi_payments", + area_category="social_security_benefits", + ), + _ssa_ssi_by_area_fact( + measure_id="payment_amount", + value=63_079_493_000, + record_set_concept="oasdi_ssi_payments", + area_category="ssi_payments", + ), + ] + ) + + specs = {spec.name: spec for spec in registry.specs} + social_security = specs[ + "ssa_supplement.cy2024.oasdi_ssi_payments" + ".social_security_benefits.payment_amount" + ] + assert social_security.metadata["target_role"] == "social_security_total" + assert social_security.metadata["base_variable"] == "social_security" + ssi_payments = specs[ + "ssa_supplement.cy2024.oasdi_ssi_payments.ssi_payments.payment_amount" + ] + assert ssi_payments.metadata["target_role"] == "ssi_total" + assert ssi_payments.metadata["base_variable"] == "ssi" + assert ssi_payments.metadata["measure_mode"] == "sum" + + def test_dynamic_us_fiscal_targets_use_builder_target_period() -> None: source_record_id = "irs_soi.ty2023.table_3_3.us.all.income_tax_liability_amount" From 29fc471789103f6d5fc5a0b250648bc9983f5580 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 12 Jul 2026 22:16:15 -0400 Subject: [PATCH 2/4] Add the US target-parity contract: manifest, release gate, and anti-rot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The target-side analog of the input-column coverage contract, as a launch gate. Every administrative target family the retired us-data/eCPS pipeline calibrated to must be compiled into the populace registry or carry a reviewed exclusion naming its evidence — silent omission is the failure this kills (the SSI recipient family was dropped with no record of it). - release_target_parity.py: family-id derivation (namespace.concept of the ledger source_record_id), the TargetFamily/TargetParityManifest model, the us_release_target_parity_gate (fails the build when a compiled family is absent from the registry, or a reviewed exclusion has started compiling — cannot-rot per #286/#337), and assert_target_parity_manifest_current (manifest vs the checked-in sha-pinned feed inventory, plus the red line that the core SSA SSI recipient family can never be downgraded off compiled). - target_parity_manifest.json + target_parity_feed_families.json: generated, sha-pinned to consumer_facts_buildh_v8 (94b7155f). 25 compiled families, 55 reviewed exclusions (52 feed-only, each with a classification + evidence, and 3 source-absent us-data families: BLS CE, WIC, HUD housing). - build_us_target_parity_manifest.py: regenerates both artifacts; halts if a feed family neither compiles nor has a declared, evidence-naming exclusion. - Wired into build_us_fiscal_refresh_release.py on the compiled + substituted registry, before the diagnostic JCT skip, mirroring the input-coverage gate. - 28 tests mirroring the input-coverage suite (gate green/red/cannot-rot, anti-rot drift + red-line, feed reconciliation, guarded regeneration). Co-Authored-By: Claude Fable 5 --- .../build/us/target_parity_feed_families.json | 83 +++ .../build/us/target_parity_manifest.json | 420 ++++++++++++++ .../src/populace/build/us_runtime/__init__.py | 28 + .../build/us_runtime/release_target_parity.py | 534 ++++++++++++++++++ .../tests/test_release_target_parity.py | 361 ++++++++++++ tools/build_us_fiscal_refresh_release.py | 19 + tools/build_us_target_parity_manifest.py | 442 +++++++++++++++ 7 files changed, 1887 insertions(+) create mode 100644 packages/populace-build/src/populace/build/us/target_parity_feed_families.json create mode 100644 packages/populace-build/src/populace/build/us/target_parity_manifest.json create mode 100644 packages/populace-build/src/populace/build/us_runtime/release_target_parity.py create mode 100644 packages/populace-build/tests/test_release_target_parity.py create mode 100644 tools/build_us_target_parity_manifest.py diff --git a/packages/populace-build/src/populace/build/us/target_parity_feed_families.json b/packages/populace-build/src/populace/build/us/target_parity_feed_families.json new file mode 100644 index 00000000..8bbfeab9 --- /dev/null +++ b/packages/populace-build/src/populace/build/us/target_parity_feed_families.json @@ -0,0 +1,83 @@ +{ + "feed": "consumer_facts_buildh_v8.jsonl", + "feed_sha256": "94b7155f7ca9e2de32ddb3a0add2fff2d8c66e73147fe5bd112cff3ba69b1669", + "families": { + "bea_nipa.business_current_transfer_receipts": 1, + "bea_nipa.defined_contribution_actual_contributions": 1, + "bea_nipa.defined_contribution_employer_contributions": 1, + "bea_nipa.disposable_personal_income": 1, + "bea_nipa.employer_government_social_insurance_contributions": 1, + "bea_nipa.employer_pension_and_insurance_contributions": 1, + "bea_nipa.farm_proprietors_income": 1, + "bea_nipa.government_social_benefits_to_persons": 1, + "bea_nipa.medicaid_benefits": 1, + "bea_nipa.medicare_benefits": 1, + "bea_nipa.nonfarm_proprietors_income": 1, + "bea_nipa.other_government_social_benefits": 1, + "bea_nipa.personal_current_taxes": 1, + "bea_nipa.personal_current_transfer_receipts": 1, + "bea_nipa.personal_dividend_income": 1, + "bea_nipa.personal_income": 1, + "bea_nipa.personal_interest_income": 1, + "bea_nipa.personal_outlays": 1, + "bea_nipa.personal_saving": 1, + "bea_nipa.personal_saving_rate": 1, + "bea_nipa.proprietors_income": 1, + "bea_nipa.rental_income_of_persons": 1, + "bea_nipa.social_security_benefits": 1, + "bea_nipa.supplements_to_wages_and_salaries": 1, + "bea_nipa.total_wages_salaries": 3, + "bea_nipa.unemployment_insurance_benefits": 1, + "bea_nipa.veterans_benefits": 1, + "bea_regional.state_contributions_for_government_social_insurance": 52, + "bea_regional.state_dividends_interest_rent": 52, + "bea_regional.state_personal_current_transfer_receipts": 52, + "bea_regional.state_personal_income": 52, + "bea_regional.state_proprietors_income": 52, + "bea_regional.state_residence_adjustment": 52, + "bea_regional.state_supplements_to_wages_salaries": 52, + "bea_regional.state_wages_salaries": 52, + "cbo.revenue_projection": 30, + "cbo.revenues": 1, + "census.popproj2023": 86, + "census_acs.acs1_2023": 468, + "census_pep.national_resident_population_age": 19, + "census_pep.v2024": 969, + "census_stc.individual_income_tax_collections": 46, + "cms_aca.oep2024": 153, + "cms_medicaid.state_enrollment": 475, + "cms_medicare.part_b_premium_income": 1, + "cms_nhe.medicaid_title_xix_expenditures": 1, + "federal_reserve_z1.households_nonprofits_balance_sheet": 1, + "hhs_acf_liheap.national_profile": 2, + "hhs_acf_tanf.average_monthly_families": 55, + "hhs_acf_tanf.average_monthly_recipients": 3, + "hhs_acf_tanf.cash_assistance": 52, + "irs_soi.congressional_district_2022": 26880, + "irs_soi.filing_season_week47": 34, + "irs_soi.form_w2_401k_elective_deferrals": 1, + "irs_soi.form_w2_designated_roth_401k_contributions": 1, + "irs_soi.form_w2_social_security_tips": 3, + "irs_soi.historic_table_2": 5654, + "irs_soi.roth_ira_contributions": 2, + "irs_soi.state_2022": 4, + "irs_soi.table_1_1": 84, + "irs_soi.table_1_2": 7, + "irs_soi.table_1_4": 557, + "irs_soi.table_2_1": 39, + "irs_soi.table_2_5": 472, + "irs_soi.table_4_3": 18, + "irs_soi.traditional_ira_contributions": 2, + "jct.tax_expenditures": 5, + "kff.marketplace_effectuated_enrollment": 52, + "ssa_supplement.oasdi_ssi_payments": 6, + "ssa_supplement.ssi_payments": 208, + "ssa_supplement.ssi_recipients": 208, + "usda_snap.national_average_monthly_households": 1, + "usda_snap.national_average_monthly_persons": 2, + "usda_snap.national_benefits": 1, + "usda_snap.state_average_monthly_households": 53, + "usda_snap.state_average_monthly_persons": 106, + "usda_snap.state_benefits": 53 + } +} diff --git a/packages/populace-build/src/populace/build/us/target_parity_manifest.json b/packages/populace-build/src/populace/build/us/target_parity_manifest.json new file mode 100644 index 00000000..4fdefefe --- /dev/null +++ b/packages/populace-build/src/populace/build/us/target_parity_manifest.json @@ -0,0 +1,420 @@ +{ + "schema_version": 1, + "reference": { + "feed": "consumer_facts_buildh_v8.jsonl", + "feed_sha256": "94b7155f7ca9e2de32ddb3a0add2fff2d8c66e73147fe5bd112cff3ba69b1669", + "target_period": "2024", + "registry_compile": "compile_us_fiscal_target_registry(age_targets=True) + apply_us_medicaid_enrollment_substitutions", + "us_data_source": "policyengine-us-data (archived): db/etl_national_targets.py, db/etl_*.py, utils/national_target_parity.py", + "family_granularity": "namespace.concept of the ledger source_record_id (us_target_family_id)", + "compiled_families": "25", + "reviewed_exclusions": "55" + }, + "families": { + "bea_nipa.business_current_transfer_receipts": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.defined_contribution_actual_contributions": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.defined_contribution_employer_contributions": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.disposable_personal_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.employer_government_social_insurance_contributions": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.employer_pension_and_insurance_contributions": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.farm_proprietors_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.government_social_benefits_to_persons": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.medicaid_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.medicare_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.nonfarm_proprietors_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.other_government_social_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_current_taxes": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_current_transfer_receipts": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_dividend_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_interest_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_outlays": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_saving": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.personal_saving_rate": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.proprietors_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.rental_income_of_persons": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.social_security_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.supplements_to_wages_and_salaries": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.total_wages_salaries": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.unemployment_insurance_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_nipa.veterans_benefits": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA NIPA national-accounts aggregate (calendar-year macro control total). Not a household-linear administrative level PolicyEngine-US calibrates against: household income components are fit from the IRS SOI micro tables (irs_soi.historic_table_2, compiled) and CBO revenue-by-source projections (cbo.revenue_projection, compiled). The NIPA aggregate is retained in the ledger as a macro cross-check, not a calibration target.", + "evidence": "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection supply the household-linear income surface; sample fact bea_nipa.cy2023.personal_income.a065rc.amount" + }, + "bea_regional.state_contributions_for_government_social_insurance": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_dividends_interest_rent": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_personal_current_transfer_receipts": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_personal_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_proprietors_income": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_residence_adjustment": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_supplements_to_wages_salaries": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bea_regional.state_wages_salaries": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "BEA regional (state) personal-income account aggregate. State income is calibrated from the IRS SOI state tables (irs_soi.state_2022 and the historic_table_2 state rows, compiled) and Census STC (census_stc.individual_income_tax_collections, compiled); the BEA regional total is a macro state control without a per-record model counterpart.", + "evidence": "compiled siblings irs_soi.state_2022 and census_stc.individual_income_tax_collections; sample fact bea_regional.cy2023.state_personal_income.us.amount" + }, + "bls.consumer_expenditure": { + "status": "reviewed_exclusion", + "classification": "source_absent", + "reason": "BLS Consumer Expenditure Survey aggregates targeted by the retired us-data pipeline (nation/bls/ce). The pinned consumer feed carries no BLS source fact, so there is no ledger-shaped fact to compile; source-absent pending a Ledger BLS CE ingest.", + "evidence": "policyengine-us-data (archived) references nation/bls/ce; feed source families carry no 'bls' source" + }, + "cbo.revenue_projection": { + "status": "compiled" + }, + "cbo.revenues": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "CBO total federal revenue line (fiscal-year budget aggregate). The household-linear CBO surface populace calibrates is the income-by-source projection (cbo.revenue_projection, compiled as cbo:5); a total revenue outturn is an aggregate, not a per-record target.", + "evidence": "compiled sibling cbo.revenue_projection; sample fact cbo.fy2023.revenues.individual_income_taxes.actual_amount" + }, + "census.popproj2023": { + "status": "reviewed_exclusion", + "classification": "superseded", + "reason": "Census Bureau population PROJECTIONS (forward vintage popproj2023). Population is calibrated to the OBSERVED Census resident-population estimates (census_pep.v2024 and national_resident_population_age, compiled as census_pep:936); the projection series is a forward-looking alternative, not the observed administrative count.", + "evidence": "compiled sibling census_pep.v2024; sample fact census.popproj2023.cy2023.national_population.age_0.population" + }, + "census_acs.acs1_2023": { + "status": "reviewed_exclusion", + "classification": "survey_derived", + "reason": "American Community Survey 1-year estimates. ACS is a household SAMPLE SURVEY, not an administrative universe, so its aggregates are excluded by principle. ACS age detail is used only at congressional-district grain when include_congressional_district_targets is enabled (off for the national release).", + "evidence": "census_acs facts return None unless include_congressional_district_targets in fiscal_targets._reference_from_ledger_fact; sample fact census_acs.acs1_2023.b01001.female_age.01.age_15_to_17.female_population" + }, + "census_pep.national_resident_population_age": { + "status": "compiled" + }, + "census_pep.v2024": { + "status": "compiled" + }, + "census_stc.individual_income_tax_collections": { + "status": "compiled" + }, + "cms_aca.oep2024": { + "status": "compiled" + }, + "cms_medicaid.state_enrollment": { + "status": "compiled" + }, + "cms_medicare.part_b_premium_income": { + "status": "compiled" + }, + "cms_nhe.medicaid_title_xix_expenditures": { + "status": "reviewed_exclusion", + "classification": "non_linear", + "reason": "CMS National Health Expenditure Medicaid Title XIX spending. Already declared calibration_role=validation_only in DIRECT_LEDGER_TARGETS: PolicyEngine-US allocates Medicaid spending from state totals through person_weight-dependent denominators, so reweighting recomputes per-person costs and this is not a linear calibration row.", + "evidence": "DIRECT_LEDGER_TARGETS ('cms_nhe','expenditure_amount','medicaid_title_xix') metadata calibration_role=validation_only" + }, + "federal_reserve_z1.households_nonprofits_balance_sheet": { + "status": "reviewed_exclusion", + "classification": "macro_control_total", + "reason": "Federal Reserve Financial Accounts (Z.1) households & nonprofits net-worth aggregate. A macro balance-sheet total; household net worth is imputed and calibrated from SCF micro on the input side (scf_wealth source stage), not from this national aggregate.", + "evidence": "scf_wealth.py source stage supplies the net-worth micro; sample fact federal_reserve_z1.cy2023.households_nonprofits_balance_sheet.net_worth.fl152090005.amount_outstanding" + }, + "hhs_acf_liheap.national_profile": { + "status": "reviewed_exclusion", + "classification": "deferred", + "reason": "HHS LIHEAP national profile (households served / funds). PolicyEngine-US does not yet expose a LIHEAP receipt outcome to fit this count to a per-household model counterpart, so it is deferred rather than dropped; retained as a ledger reference fact.", + "evidence": "no LIHEAP target_role in INDICATOR_LEDGER_TARGETS/DIRECT_LEDGER_TARGETS; sample fact hhs_acf_liheap.fy2023.national_profile.state_programs.households_served" + }, + "hhs_acf_tanf.average_monthly_families": { + "status": "reviewed_exclusion", + "classification": "deferred", + "reason": "HHS ACF TANF average-monthly family caseload. TANF is calibrated on BENEFIT DOLLARS (hhs_acf_tanf.cash_assistance, compiled as hhs_acf_tanf:30); the caseload COUNT is not yet wired to a TANF-receipt indicator (the SNAP-household caseload analog for TANF), so it is deferred.", + "evidence": "compiled sibling hhs_acf_tanf.cash_assistance carries the dollar targets; sample fact hhs_acf_tanf.fy2024.average_monthly_families.us.us_total.total_families" + }, + "hhs_acf_tanf.average_monthly_recipients": { + "status": "reviewed_exclusion", + "classification": "deferred", + "reason": "HHS ACF TANF average-monthly recipient caseload. As with the family caseload, TANF is calibrated on benefit dollars (hhs_acf_tanf.cash_assistance, compiled); the recipient COUNT has no wired TANF-receipt indicator yet and is deferred.", + "evidence": "compiled sibling hhs_acf_tanf.cash_assistance; sample fact hhs_acf_tanf.fy2024.average_monthly_recipients.us.us_total.total_recipients" + }, + "hhs_acf_tanf.cash_assistance": { + "status": "compiled" + }, + "hud.housing_assistance": { + "status": "reviewed_exclusion", + "classification": "source_absent", + "reason": "HUD housing-assistance aggregates targeted by the retired us-data pipeline (db/etl_housing_assistance.py). The pinned feed carries no HUD source fact; source-absent pending a Ledger HUD ingest.", + "evidence": "policyengine-us-data (archived) db/etl_housing_assistance.py; feed carries no 'hud' source" + }, + "irs_soi.congressional_district_2022": { + "status": "reviewed_exclusion", + "classification": "off_by_default", + "reason": "IRS SOI congressional-district table. CD-level targets are opt-in (include_congressional_district_targets=False for the national release); the national and state SOI surfaces are compiled instead. Enabling CD targets compiles these \u2014 they are excluded for the national build by design, not dropped.", + "evidence": "test_soi_congressional_district_targets_are_opt_in + the include_congressional_district_targets gate in fiscal_targets._soi_reference_from_fact" + }, + "irs_soi.filing_season_week47": { + "status": "compiled" + }, + "irs_soi.form_w2_401k_elective_deferrals": { + "status": "reviewed_exclusion", + "classification": "input_side", + "reason": "IRS SOI / W-2 retirement-contribution aggregate. Retirement contributions are modeled as imputed INPUT columns (traditional/roth 401(k) and IRA contributions), governed by the input-coverage contract, not as reweighting targets. Calibrating them as fiscal targets would double-govern a quantity the input-coverage gate already owns.", + "evidence": "RESTORED_REFERENCE_ECPS_REQUIRED_INPUTS declares the *_contributions_desired input columns in release_input_coverage.py" + }, + "irs_soi.form_w2_designated_roth_401k_contributions": { + "status": "reviewed_exclusion", + "classification": "input_side", + "reason": "IRS SOI / W-2 retirement-contribution aggregate. Retirement contributions are modeled as imputed INPUT columns (traditional/roth 401(k) and IRA contributions), governed by the input-coverage contract, not as reweighting targets. Calibrating them as fiscal targets would double-govern a quantity the input-coverage gate already owns.", + "evidence": "RESTORED_REFERENCE_ECPS_REQUIRED_INPUTS declares the *_contributions_desired input columns in release_input_coverage.py" + }, + "irs_soi.form_w2_social_security_tips": { + "status": "reviewed_exclusion", + "classification": "not_modeled", + "reason": "IRS W-2 Social Security tip aggregates. Already declared in US_FISCAL_TARGET_SUPPORT_EXCLUSIONS: current US support does not materialize a positive tip_income source column, so the W-2 tip return counts need the SIPP/ORG tip source stage wired before calibration.", + "evidence": "US_FISCAL_TARGET_SUPPORT_EXCLUSIONS entry irs_soi.ty2023.form_w2_social_security_tips.box_7_social_security_tips.return_count" + }, + "irs_soi.historic_table_2": { + "status": "compiled" + }, + "irs_soi.roth_ira_contributions": { + "status": "reviewed_exclusion", + "classification": "input_side", + "reason": "IRS SOI / W-2 retirement-contribution aggregate. Retirement contributions are modeled as imputed INPUT columns (traditional/roth 401(k) and IRA contributions), governed by the input-coverage contract, not as reweighting targets. Calibrating them as fiscal targets would double-govern a quantity the input-coverage gate already owns.", + "evidence": "RESTORED_REFERENCE_ECPS_REQUIRED_INPUTS declares the *_contributions_desired input columns in release_input_coverage.py" + }, + "irs_soi.state_2022": { + "status": "compiled" + }, + "irs_soi.table_1_1": { + "status": "compiled" + }, + "irs_soi.table_1_2": { + "status": "compiled" + }, + "irs_soi.table_1_4": { + "status": "compiled" + }, + "irs_soi.table_2_1": { + "status": "compiled" + }, + "irs_soi.table_2_5": { + "status": "compiled" + }, + "irs_soi.table_4_3": { + "status": "compiled" + }, + "irs_soi.traditional_ira_contributions": { + "status": "reviewed_exclusion", + "classification": "input_side", + "reason": "IRS SOI / W-2 retirement-contribution aggregate. Retirement contributions are modeled as imputed INPUT columns (traditional/roth 401(k) and IRA contributions), governed by the input-coverage contract, not as reweighting targets. Calibrating them as fiscal targets would double-govern a quantity the input-coverage gate already owns.", + "evidence": "RESTORED_REFERENCE_ECPS_REQUIRED_INPUTS declares the *_contributions_desired input columns in release_input_coverage.py" + }, + "jct.tax_expenditures": { + "status": "compiled" + }, + "kff.marketplace_effectuated_enrollment": { + "status": "reviewed_exclusion", + "classification": "superseded", + "reason": "Kaiser Family Foundation state marketplace effectuated-enrollment compilation. ACA marketplace enrollment is calibrated from the primary CMS administrative source (cms_aca.oep2024, compiled as cms_aca:102); KFF is a secondary aggregator of the same underlying CMS data, excluded to avoid a duplicate target.", + "evidence": "compiled sibling cms_aca.oep2024; sample fact kff.marketplace_effectuated_enrollment.2024.state.us.total_effectuated_marketplace_enrollment" + }, + "ssa_supplement.oasdi_ssi_payments": { + "status": "compiled" + }, + "ssa_supplement.ssi_payments": { + "status": "compiled" + }, + "ssa_supplement.ssi_recipients": { + "status": "compiled" + }, + "usda_snap.national_average_monthly_households": { + "status": "compiled" + }, + "usda_snap.national_average_monthly_persons": { + "status": "reviewed_exclusion", + "classification": "not_modeled", + "reason": "USDA FNS SNAP average-monthly PERSONS. The SNAP assistance unit is often a subset of the SPM unit (FY2024 FNS persons-per-household 1.88 vs 2.82 simulated members per taker unit), so a person indicator overcounts FNS participants by ~50%. The household caseload (usda_snap.national/state_average_monthly_households, compiled) is the calibrated count; persons stay unmapped until sub-unit participation is modeled.", + "evidence": "INDICATOR_LEDGER_TARGETS average_monthly_households comment + test_snap_person_caseload_fact_is_not_compiled" + }, + "usda_snap.national_benefits": { + "status": "compiled" + }, + "usda_snap.state_average_monthly_households": { + "status": "compiled" + }, + "usda_snap.state_average_monthly_persons": { + "status": "reviewed_exclusion", + "classification": "not_modeled", + "reason": "USDA FNS SNAP average-monthly PERSONS. The SNAP assistance unit is often a subset of the SPM unit (FY2024 FNS persons-per-household 1.88 vs 2.82 simulated members per taker unit), so a person indicator overcounts FNS participants by ~50%. The household caseload (usda_snap.national/state_average_monthly_households, compiled) is the calibrated count; persons stay unmapped until sub-unit participation is modeled.", + "evidence": "INDICATOR_LEDGER_TARGETS average_monthly_households comment + test_snap_person_caseload_fact_is_not_compiled" + }, + "usda_snap.state_benefits": { + "status": "compiled" + }, + "wic.national_summary": { + "status": "reviewed_exclusion", + "classification": "source_absent", + "reason": "USDA WIC national annual summary targeted by the retired us-data pipeline (WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE in etl_national_targets). The pinned feed carries no WIC source fact; source-absent. WIC receipt is modeled via the would_claim_wic take-up input, not a target.", + "evidence": "policyengine-us-data (archived) db/etl_national_targets.py WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE; feed carries no 'wic' source" + } + } +} diff --git a/packages/populace-build/src/populace/build/us_runtime/__init__.py b/packages/populace-build/src/populace/build/us_runtime/__init__.py index 0d79dde7..19a5b52a 100644 --- a/packages/populace-build/src/populace/build/us_runtime/__init__.py +++ b/packages/populace-build/src/populace/build/us_runtime/__init__.py @@ -552,6 +552,21 @@ us_release_input_coverage_reviewed_exclusions, us_release_reform_coverage_probes, ) +from populace.build.us_runtime.release_target_parity import ( + SSI_RECIPIENT_RED_LINE_FAMILIES, + US_TARGET_PARITY_FEED_FAMILIES_RESOURCE, + US_TARGET_PARITY_MANIFEST_RESOURCE, + TargetFamily, + TargetParityManifest, + assert_target_parity_manifest_current, + load_target_parity_feed_families, + load_target_parity_manifest, + registry_target_family_ids, + us_release_target_parity_compiled_families, + us_release_target_parity_gate, + us_release_target_parity_reviewed_exclusions, + us_target_family_id, +) from populace.build.us_runtime.retirement_contributions import ( US_RETIREMENT_CONTRIBUTION_NONCONSTANT_PERSON_COLUMNS, US_RETIREMENT_CONTRIBUTION_OUTPUT_COLUMNS, @@ -1602,6 +1617,19 @@ "us_release_input_coverage_required_columns", "us_release_input_coverage_reviewed_exclusions", "us_release_reform_coverage_probes", + "SSI_RECIPIENT_RED_LINE_FAMILIES", + "US_TARGET_PARITY_FEED_FAMILIES_RESOURCE", + "US_TARGET_PARITY_MANIFEST_RESOURCE", + "TargetFamily", + "TargetParityManifest", + "assert_target_parity_manifest_current", + "load_target_parity_feed_families", + "load_target_parity_manifest", + "registry_target_family_ids", + "us_release_target_parity_compiled_families", + "us_release_target_parity_gate", + "us_release_target_parity_reviewed_exclusions", + "us_target_family_id", "us_reform_coverage_smoke_gate", "us_register_consistency_gate", "us_register_contradictions", diff --git a/packages/populace-build/src/populace/build/us_runtime/release_target_parity.py b/packages/populace-build/src/populace/build/us_runtime/release_target_parity.py new file mode 100644 index 00000000..aab9583a --- /dev/null +++ b/packages/populace-build/src/populace/build/us_runtime/release_target_parity.py @@ -0,0 +1,534 @@ +"""US release target-parity: the calibration-target surface as a HARD gate. + +The target-side analog of the input-column coverage contract +(:mod:`populace.build.us_runtime.release_input_coverage`). Where that module +guards *inputs the reference eCPS exports*, this one guards *administrative +targets the retired us-data/eCPS pipeline calibrated to*. + +The launch failure this closes: an administrative target family the retired +pipeline calibrated to is silently absent from the populace target registry. +The motivating case is SSA SSI recipients — the retired pipeline calibrated +``nation/ssa/ssi_recipients`` (SSA Annual Statistical Supplement, 7,404,820 +recipients in 2024), the pinned consumer feed carries the +``ssa_supplement.ssi_recipients`` facts, yet the registry compiled only the six +``oasdi_ssi_payments`` aggregates and dropped the recipient family with no +record of the omission. Column *mass* parity did not catch it because a target +family that never compiles contributes zero rows, not wrong ones. + +This module is the coverage contract at *family* granularity — a family is a +distinct administrative table/concept the ledger feed carries, identified by the +namespace and first concept token of a fact's ``source_record_id`` (e.g. +``ssa_supplement.ssi_recipients``, ``irs_soi.historic_table_2``, +``cbo.revenue_projection``). This is the granularity at which silent omission +happens: it is finer than the source family (``ssa`` alone would hide the +recipient gap behind the compiled OASDI payments) and coarser than the +individual row. + +The versioned in-repo manifest declares every family with a status: + +- ``compiled`` — the compiled registry must carry at least one target for it; +- ``reviewed_exclusion`` — a documented, classified gap allowed to be absent + (survey-derived, macro control total, non-linear, off-by-default, + source-absent, …), each naming its evidence. + +:func:`us_release_target_parity_gate` enforces the contract on the compiled +target registry and, wired into the release tool, hard-fails the build before +materialization exactly like the target-profile coverage gate. + +:func:`assert_target_parity_manifest_current` proves the manifest against the +checked-in, sha-pinned feed-family inventory (always) and the live compiled +registry (when supplied), so the register cannot silently rot (#286/#337): it +must declare exactly the feed's family surface plus any documented source-absent +us-data families, every ``compiled`` family must be in the registry, and — the +red line this contract exists for — the core SSA SSI recipient family must stay +``compiled`` and can never be quietly downgraded to a reviewed exclusion. +""" + +from __future__ import annotations + +import json +from collections.abc import Mapping +from dataclasses import dataclass, field +from importlib.resources import files +from pathlib import Path +from typing import Any + +from populace.build.gates import GateResult + +__all__ = [ + "US_TARGET_PARITY_MANIFEST_RESOURCE", + "US_TARGET_PARITY_FEED_FAMILIES_RESOURCE", + "COMPILED_STATUS", + "REVIEWED_EXCLUSION_STATUS", + "SOURCE_ABSENT_CLASSIFICATION", + "SSI_RECIPIENT_RED_LINE_FAMILIES", + "TargetFamily", + "TargetParityManifest", + "assert_target_parity_manifest_current", + "load_target_parity_feed_families", + "load_target_parity_manifest", + "registry_target_family_ids", + "us_release_target_parity_compiled_families", + "us_release_target_parity_gate", + "us_release_target_parity_reviewed_exclusions", + "us_target_family_id", +] + +US_TARGET_PARITY_MANIFEST_RESOURCE = "target_parity_manifest.json" +US_TARGET_PARITY_FEED_FAMILIES_RESOURCE = "target_parity_feed_families.json" + +_US_PACKAGE = "populace.build.us" + +COMPILED_STATUS = "compiled" +REVIEWED_EXCLUSION_STATUS = "reviewed_exclusion" +_VALID_STATUSES = frozenset({COMPILED_STATUS, REVIEWED_EXCLUSION_STATUS}) + +#: A reviewed exclusion whose family the pinned feed does not carry at all — a +#: us-data admin target with no ledger fact to compile. These are the only +#: declared families exempt from the feed-surface reconciliation. +SOURCE_ABSENT_CLASSIFICATION = "source_absent" + +#: The core SSA SSI recipient family. Per this contract it must ship +#: ``compiled`` with no reviewed exclusion — the whole point of the register is +#: that the family the retired pipeline calibrated (``nation/ssa/ssi_recipients``) +#: can never again be silently dropped. The anti-rot check refuses to let it be +#: downgraded, mirroring the #368 SSI countable-resource asset red line on the +#: input-coverage side. +SSI_RECIPIENT_RED_LINE_FAMILIES = ("ssa_supplement.ssi_recipients",) + + +def _is_target_period_token(value: str) -> bool: + """Whether a ``source_record_id`` token is a period tag. + + The canonical period-token test used across the ledger target machinery + (``irs_soi.ty2023``, ``ssa_supplement.cy2024``, ``usda_snap.fy2024``, bare + four-digit years, and ``YYYY_MM`` / ``monthYYYY_MM`` month tags). Kept in + lockstep with + :func:`populace.build.ledger_targets._is_period_token` so the family id is + derived identically to the registry's own record-set normalization. + """ + normalized = value.lower().replace("-", "_") + if normalized.startswith("month"): + normalized = normalized[len("month") :] + parts = normalized.split("_", maxsplit=1) + if len(parts) == 2 and all(part.isdigit() for part in parts): + return len(parts[0]) == 4 and len(parts[1]) in {1, 2} + if normalized[:2] in {"ty", "cy", "fy"}: + normalized = normalized[2:] + return normalized.isdigit() and len(normalized) == 4 + + +def us_target_family_id(source_record_id: str) -> str: + """The administrative target-family id of a Ledger ``source_record_id``. + + ``namespace.concept`` where ``namespace`` is the first token and ``concept`` + is the first following non-period token, e.g.:: + + ssa_supplement.cy2024.ssi_recipients.by_area_category.al_total... -> + ssa_supplement.ssi_recipients + irs_soi.ty2022.historic_table_2.us.all.ctc_amount -> + irs_soi.historic_table_2 + cbo.fy2023.revenues.individual_income_taxes.actual_amount -> + cbo.revenues + + A registry ``TargetSpec.name`` is exactly its ``source_record_id``, so this + maps compiled specs and raw feed facts onto the same family surface. + """ + tokens = [token for token in str(source_record_id).split(".") if token] + if not tokens: + return "" + namespace = tokens[0] + for token in tokens[1:]: + if not _is_target_period_token(token): + return f"{namespace}.{token}" + return namespace + + +@dataclass(frozen=True) +class TargetFamily: + """One declared target-parity family. + + Attributes: + name: The ``namespace.concept`` family id. + status: ``"compiled"`` (the registry must carry a target for it) or + ``"reviewed_exclusion"`` (a documented, classified gap). + classification: For a reviewed exclusion, the exclusion kind + (``survey_derived``, ``macro_control_total``, ``non_linear``, + ``off_by_default``, ``superseded``, ``source_absent``, + ``deferred``, ``input_side``, ``not_modeled``). Empty for a + compiled family. + reason: Why the gap is accepted — required for a reviewed exclusion. + evidence: The concrete fact/mechanism the reason names (a sample + ``source_record_id``, a code constant, a compiled sibling family). + Required for a reviewed exclusion — an undocumented exclusion is a + silent omission with extra steps. + issue: Optional tracking issue owning the gap's closure. + note: Optional free-text annotation. + """ + + name: str + status: str + classification: str = "" + reason: str = "" + evidence: str = "" + issue: str = "" + note: str = "" + + def __post_init__(self) -> None: + if not self.name: + raise ValueError("TargetFamily.name is required.") + if self.status not in _VALID_STATUSES: + raise ValueError( + f"{self.name}: status must be one of {sorted(_VALID_STATUSES)}, " + f"got {self.status!r}." + ) + if self.status == REVIEWED_EXCLUSION_STATUS: + if not self.reason: + raise ValueError( + f"{self.name}: a reviewed exclusion needs a reason " + "(an undocumented exclusion is a silent omission)." + ) + if not self.classification: + raise ValueError( + f"{self.name}: a reviewed exclusion needs a classification." + ) + if not self.evidence: + raise ValueError( + f"{self.name}: a reviewed exclusion needs evidence naming the " + "concrete fact or mechanism behind the reason." + ) + + @property + def is_source_absent(self) -> bool: + """Whether the family is excluded because the feed carries no fact.""" + return ( + self.status == REVIEWED_EXCLUSION_STATUS + and self.classification == SOURCE_ABSENT_CLASSIFICATION + ) + + +@dataclass(frozen=True) +class TargetParityManifest: + """The full parsed target-parity contract. + + Attributes: + reference: Provenance of the feed and us-data sources the surface is + derived from. + families: Every declared family, in name order. + schema_version: Manifest schema version. + """ + + reference: Mapping[str, str] + families: tuple[TargetFamily, ...] + schema_version: int = 1 + _by_name: dict[str, TargetFamily] = field( + default_factory=dict, repr=False, compare=False + ) + + def __post_init__(self) -> None: + by_name: dict[str, TargetFamily] = {} + for family in self.families: + if family.name in by_name: + raise ValueError(f"Duplicate manifest family {family.name!r}.") + by_name[family.name] = family + object.__setattr__(self, "_by_name", by_name) + + @property + def by_name(self) -> Mapping[str, TargetFamily]: + return self._by_name + + @property + def declared_families(self) -> frozenset[str]: + """Every family the manifest declares (compiled + reviewed).""" + return frozenset(self._by_name) + + @property + def compiled_families(self) -> frozenset[str]: + """Families the compiled registry must carry a target for.""" + return frozenset( + family.name for family in self.families if family.status == COMPILED_STATUS + ) + + @property + def reviewed_exclusions(self) -> dict[str, str]: + """Reviewed-exclusion families as ``name -> reason``.""" + return { + family.name: family.reason + for family in self.families + if family.status == REVIEWED_EXCLUSION_STATUS + } + + @property + def source_absent_families(self) -> frozenset[str]: + """Reviewed exclusions the pinned feed carries no fact for.""" + return frozenset( + family.name for family in self.families if family.is_source_absent + ) + + @property + def feed_surface_families(self) -> frozenset[str]: + """Declared families the pinned feed is expected to carry. + + Every declared family except the documented source-absent us-data ones; + this must equal the checked-in feed-family inventory exactly. + """ + return self.declared_families - self.source_absent_families + + +def _resource_text(resource: str) -> str: + candidate = Path(resource) + if candidate.exists(): + return candidate.read_text(encoding="utf-8") + return files(_US_PACKAGE).joinpath(resource).read_text(encoding="utf-8") + + +def _resource_payload(resource: str) -> Mapping[str, Any]: + raw = json.loads(_resource_text(resource)) + if not isinstance(raw, Mapping): + raise ValueError(f"{resource}: expected a JSON object.") + return raw + + +def load_target_parity_manifest( + resource: str = US_TARGET_PARITY_MANIFEST_RESOURCE, +) -> TargetParityManifest: + """Load and validate the release target-parity manifest. + + Raises: + ValueError: If the payload shape is wrong, a family has an unknown + status, a reviewed exclusion is missing its reason/classification/ + evidence, or the declared family set is empty (a silently-empty + manifest would make the gate vacuous). + """ + payload = _resource_payload(resource) + + raw_reference = payload.get("reference") + if not isinstance(raw_reference, Mapping): + raise ValueError(f"{resource}: 'reference' must be a JSON object.") + reference = {str(key): str(value) for key, value in raw_reference.items()} + + raw_families = payload.get("families") + if not isinstance(raw_families, Mapping) or not raw_families: + raise ValueError( + f"{resource}: 'families' must be a non-empty JSON object; a silently " + "empty manifest would make the target-parity gate vacuous." + ) + families: list[TargetFamily] = [] + for name, entry in sorted(raw_families.items()): + if not isinstance(entry, Mapping): + raise ValueError(f"{resource}: family {name!r} must be a JSON object.") + families.append( + TargetFamily( + name=str(name), + status=str(entry.get("status", "")), + classification=str(entry.get("classification", "")), + reason=str(entry.get("reason", "")), + evidence=str(entry.get("evidence", "")), + issue=str(entry.get("issue", "")), + note=str(entry.get("note", "")), + ) + ) + + schema_version = payload.get("schema_version", 1) + if not isinstance(schema_version, int): + raise ValueError(f"{resource}: 'schema_version' must be an integer.") + + return TargetParityManifest( + reference=reference, + families=tuple(families), + schema_version=schema_version, + ) + + +def load_target_parity_feed_families( + resource: str = US_TARGET_PARITY_FEED_FAMILIES_RESOURCE, +) -> Mapping[str, Any]: + """Load the checked-in, sha-pinned feed-family inventory. + + The authoritative feed surface the manifest cannot drift from: a mapping of + every family id the pinned consumer feed carries to its fact count, plus the + feed filename and sha256. Derived from the feed by + ``tools/build_us_target_parity_manifest.py`` and committed, so the manifest + consistency check runs without the 131 MB feed in CI (the same pattern the + input-coverage manifest uses against the checked-in eCPS parity reference). + """ + payload = _resource_payload(resource) + families = payload.get("families") + if not isinstance(families, Mapping) or not families: + raise ValueError(f"{resource}: 'families' must be a non-empty JSON object.") + return payload + + +def us_release_target_parity_compiled_families() -> frozenset[str]: + """The families the shipped manifest marks compiled.""" + return load_target_parity_manifest().compiled_families + + +def us_release_target_parity_reviewed_exclusions() -> dict[str, str]: + """The reviewed-exclusion register from the shipped manifest.""" + return load_target_parity_manifest().reviewed_exclusions + + +def registry_target_family_ids(registry: Any) -> frozenset[str]: + """The set of administrative target families a compiled registry carries.""" + return frozenset( + us_target_family_id(spec.name) + for spec in registry.specs + if us_target_family_id(spec.name) + ) + + +def us_release_target_parity_gate( + registry: Any, + *, + manifest: TargetParityManifest | None = None, +) -> GateResult: + """Build the named US release target-parity gate for a compiled registry. + + Every ``compiled`` manifest family must be carried by ``registry`` as at + least one target; a compiled family with no registry target fails the gate + (the silent-omission failure this gate exists to catch). A reviewed + exclusion whose family the registry now compiles is stale and fails too + (#286/#337 cannot-rot). Run on the compiled, substituted target registry + just before materialization, it hard-fails the release like the + target-profile coverage gate. + + Args: + registry: The compiled :class:`~populace.calibrate.TargetRegistry`. + manifest: Override the shipped manifest (tests). + + Returns: + The ``"us_release_target_parity"`` gate result. + """ + manifest = manifest or load_target_parity_manifest() + present = registry_target_family_ids(registry) + compiled = manifest.compiled_families + reviewed = set(manifest.reviewed_exclusions) + + failures: list[str] = [] + for family in sorted(compiled - present): + failures.append( + f"{family}: the manifest marks this administrative target family " + "compiled, but the compiled registry carries no target for it — the " + "silent omission this gate exists to catch. Wire it or reclassify it " + "as a reviewed exclusion with evidence." + ) + for family in sorted(reviewed & present): + failures.append( + f"{family}: the manifest marks this family a reviewed exclusion, but " + "the registry now compiles a target for it. Promote it to compiled " + "(the manifest cannot rot — PolicyEngine/populace#286/#337)." + ) + + return GateResult( + name="us_release_target_parity", + passed=not failures, + failures=tuple(failures), + details={ + "compiled_families": len(compiled), + "reviewed_exclusions": len(reviewed), + "registry_families": len(present), + }, + ) + + +def assert_target_parity_manifest_current( + *, + registry: Any | None = None, + manifest: TargetParityManifest | None = None, + feed_families: Mapping[str, Any] | None = None, +) -> None: + """Fail if the target-parity manifest has drifted from its sources. + + Checked against the checked-in feed-family inventory (always) and the live + compiled registry (when supplied): + + - The declared feed-surface families (every family except documented + source-absent ones) must equal the checked-in feed-family inventory. A + new family in the feed, or a manifest family the feed no longer carries, + is drift and must be reconciled by regenerating the manifest. + - The manifest's ``reference.feed_sha256`` must match the inventory's, so + the two artifacts describe the same pinned feed. + - The core SSA SSI recipient family must stay ``compiled`` with no reviewed + exclusion — the red line this contract exists for cannot be quietly undone. + - When a compiled registry is supplied, every ``compiled`` family must be + present in it, every ``reviewed_exclusion`` family must be absent from it, + and the registry must carry no family the manifest fails to declare. + + A no-op for the registry half when no registry is supplied (the workspace + test environment); the checked-in-facts half always runs. + + Raises: + ValueError: Naming every drift found. + """ + manifest = manifest or load_target_parity_manifest() + feed_families = feed_families or load_target_parity_feed_families() + failures: list[str] = [] + + feed_family_ids = frozenset(str(name) for name in feed_families["families"]) + declared_feed_surface = manifest.feed_surface_families + + missing_from_manifest = sorted(feed_family_ids - manifest.declared_families) + if missing_from_manifest: + failures.append( + "the pinned feed carries target family(ies) not declared in the " + f"manifest {missing_from_manifest}; regenerate with " + "tools/build_us_target_parity_manifest.py." + ) + extra_feed_surface = sorted(declared_feed_surface - feed_family_ids) + if extra_feed_surface: + failures.append( + "the manifest declares feed-surface family(ies) the pinned feed no " + f"longer carries {extra_feed_surface}; regenerate the manifest or " + "reclassify them source_absent." + ) + + manifest_sha = str(manifest.reference.get("feed_sha256", "")) + inventory_sha = str(feed_families.get("feed_sha256", "")) + if manifest_sha and inventory_sha and manifest_sha != inventory_sha: + failures.append( + "manifest reference.feed_sha256 " + f"{manifest_sha!r} does not match the feed-family inventory sha " + f"{inventory_sha!r}; regenerate both from the same pinned feed." + ) + + for family in SSI_RECIPIENT_RED_LINE_FAMILIES: + entry = manifest.by_name.get(family) + if entry is None: + failures.append( + f"{family}: core SSA SSI recipient family must be declared and " + "compiled (the target-parity red line)." + ) + elif entry.status != COMPILED_STATUS: + failures.append( + f"{family}: core SSA SSI recipient family must stay status=" + f"{COMPILED_STATUS!r}, not {entry.status!r} — it can never be " + "quietly downgraded to a reviewed exclusion." + ) + + if registry is not None: + present = registry_target_family_ids(registry) + undeclared = sorted(present - manifest.declared_families) + if undeclared: + failures.append( + "the compiled registry carries target family(ies) the manifest " + f"does not declare {undeclared}; regenerate the manifest." + ) + not_compiled = sorted(manifest.compiled_families - present) + if not_compiled: + failures.append( + "the manifest marks family(ies) compiled the registry does not " + f"carry {not_compiled}." + ) + stale = sorted(set(manifest.reviewed_exclusions) & present) + if stale: + failures.append( + "the manifest marks family(ies) a reviewed exclusion the registry " + f"now compiles {stale}; promote them to compiled." + ) + + if failures: + raise ValueError( + "US release target-parity manifest has drifted:\n" + + "\n".join(f" - {line}" for line in failures) + ) diff --git a/packages/populace-build/tests/test_release_target_parity.py b/packages/populace-build/tests/test_release_target_parity.py new file mode 100644 index 00000000..10759c51 --- /dev/null +++ b/packages/populace-build/tests/test_release_target_parity.py @@ -0,0 +1,361 @@ +"""US release target-parity contract: gate + anti-rot, isolated from the feed. + +The target-side analog of ``test_release_input_coverage``. The acceptance cases: + +1. every ``compiled`` family present in the registry passes; +2. a ``compiled`` family missing from the registry fails, named; +3. a ``reviewed_exclusion`` family the registry now compiles is stale and fails + (#286/#337 cannot-rot); +4. the anti-rot check rejects an undeclared feed family, a feed-surface family + the feed no longer carries, a feed-sha mismatch, and — the red line — any + attempt to downgrade the core SSA SSI recipient family off ``compiled``; +5. the shipped manifest is self-consistent with the checked-in feed inventory, + and (when the pinned feed is present) reproduces exactly from it. + +The registry is a lightweight stub exposing only ``.specs[].name`` (the single +surface the gate reads), so nothing here needs the 131 MB feed or +policyengine-us. Feed-dependent reproduction is guarded. +""" + +from __future__ import annotations + +import importlib.util +import json +from dataclasses import replace +from pathlib import Path +from types import SimpleNamespace + +import pytest + +from populace.build.us_runtime.release_target_parity import ( + COMPILED_STATUS, + REVIEWED_EXCLUSION_STATUS, + SSI_RECIPIENT_RED_LINE_FAMILIES, + TargetFamily, + TargetParityManifest, + assert_target_parity_manifest_current, + load_target_parity_feed_families, + load_target_parity_manifest, + registry_target_family_ids, + us_release_target_parity_gate, + us_target_family_id, +) + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_MANIFEST_GENERATOR = _REPO_ROOT / "tools" / "build_us_target_parity_manifest.py" +_US_PACKAGE_DIR = ( + _REPO_ROOT / "packages" / "populace-build" / "src" / "populace" / "build" / "us" +) + + +def _registry(family_names) -> SimpleNamespace: + """A stub registry whose specs' names resolve to ``family_names``. + + Appends a period tag + leaf so ``us_target_family_id`` maps each name back to + its family id (the concept token is the first non-period token). + """ + return SimpleNamespace( + specs=[ + SimpleNamespace(name=f"{family}.cy2024.leaf{index}") + for index, family in enumerate(family_names) + ] + ) + + +def _manifest(families: tuple[TargetFamily, ...]) -> TargetParityManifest: + return TargetParityManifest( + reference={"feed": "test", "feed_sha256": "abc"}, families=families + ) + + +_CONTRACT = _manifest( + ( + TargetFamily("ssa_supplement.ssi_recipients", COMPILED_STATUS), + TargetFamily("usda_snap.state_benefits", COMPILED_STATUS), + TargetFamily( + "bea_nipa.personal_income", + REVIEWED_EXCLUSION_STATUS, + classification="macro_control_total", + reason="NIPA macro aggregate; income fit from SOI + CBO.", + evidence="compiled sibling irs_soi.historic_table_2", + ), + ) +) + + +class TestFamilyId: + def test_ssa_recipients(self) -> None: + assert ( + us_target_family_id( + "ssa_supplement.cy2024.ssi_recipients.by_area_category" + ".all_areas_total.recipient_count" + ) + == "ssa_supplement.ssi_recipients" + ) + + def test_soi_historic_table(self) -> None: + assert ( + us_target_family_id("irs_soi.ty2022.historic_table_2.us.all.ctc_amount") + == "irs_soi.historic_table_2" + ) + + def test_cbo_revenues(self) -> None: + assert ( + us_target_family_id( + "cbo.fy2023.revenues.individual_income_taxes.actual_amount" + ) + == "cbo.revenues" + ) + + def test_namespace_only_when_no_concept(self) -> None: + assert us_target_family_id("cbo.fy2023") == "cbo" + + def test_empty(self) -> None: + assert us_target_family_id("") == "" + + +class TestGate: + def test_every_compiled_family_present_passes(self) -> None: + registry = _registry( + ["ssa_supplement.ssi_recipients", "usda_snap.state_benefits"] + ) + result = us_release_target_parity_gate(registry, manifest=_CONTRACT) + assert result.passed + assert result.name == "us_release_target_parity" + assert result.details["compiled_families"] == 2 + + def test_missing_compiled_family_fails_named(self) -> None: + registry = _registry(["ssa_supplement.ssi_recipients"]) + result = us_release_target_parity_gate(registry, manifest=_CONTRACT) + assert not result.passed + assert any("usda_snap.state_benefits" in failure for failure in result.failures) + + def test_stale_reviewed_exclusion_fails(self) -> None: + # A reviewed-exclusion family the registry now compiles must be promoted. + registry = _registry( + [ + "ssa_supplement.ssi_recipients", + "usda_snap.state_benefits", + "bea_nipa.personal_income", + ] + ) + result = us_release_target_parity_gate(registry, manifest=_CONTRACT) + assert not result.passed + assert any("bea_nipa.personal_income" in failure for failure in result.failures) + assert any("cannot rot" in failure for failure in result.failures) + + +class TestTargetFamilyValidation: + def test_reviewed_exclusion_requires_reason(self) -> None: + with pytest.raises(ValueError, match="needs a reason"): + TargetFamily( + "x.y", REVIEWED_EXCLUSION_STATUS, classification="c", evidence="e" + ) + + def test_reviewed_exclusion_requires_classification(self) -> None: + with pytest.raises(ValueError, match="needs a classification"): + TargetFamily("x.y", REVIEWED_EXCLUSION_STATUS, reason="r", evidence="e") + + def test_reviewed_exclusion_requires_evidence(self) -> None: + with pytest.raises(ValueError, match="needs evidence"): + TargetFamily( + "x.y", REVIEWED_EXCLUSION_STATUS, classification="c", reason="r" + ) + + def test_unknown_status_rejected(self) -> None: + with pytest.raises(ValueError, match="status must be one of"): + TargetFamily("x.y", "maybe") + + +class TestAntiRot: + def test_undeclared_feed_family_fails(self) -> None: + feed = {"feed_sha256": "abc", "families": {"new_source.new_concept": 3}} + with pytest.raises(ValueError, match="not declared in the manifest"): + assert_target_parity_manifest_current( + manifest=_CONTRACT, feed_families=feed + ) + + def test_feed_surface_family_no_longer_in_feed_fails(self) -> None: + # usda_snap.state_benefits is a declared compiled (feed-surface) family; + # a feed inventory omitting it is drift. + feed = { + "feed_sha256": "abc", + "families": { + "ssa_supplement.ssi_recipients": 52, + "bea_nipa.personal_income": 1, + }, + } + with pytest.raises(ValueError, match="no longer carries"): + assert_target_parity_manifest_current( + manifest=_CONTRACT, feed_families=feed + ) + + def test_feed_sha_mismatch_fails(self) -> None: + feed = { + "feed_sha256": "different", + "families": { + "ssa_supplement.ssi_recipients": 52, + "usda_snap.state_benefits": 51, + "bea_nipa.personal_income": 1, + }, + } + with pytest.raises(ValueError, match="does not match the feed-family"): + assert_target_parity_manifest_current( + manifest=_CONTRACT, feed_families=feed + ) + + def test_ssi_recipients_downgrade_is_rejected(self) -> None: + downgraded_families = tuple( + replace( + family, + status=REVIEWED_EXCLUSION_STATUS, + classification="deferred", + reason="pretend we dropped it", + evidence="none", + ) + if family.name in SSI_RECIPIENT_RED_LINE_FAMILIES + else family + for family in _CONTRACT.families + ) + downgraded = _manifest(downgraded_families) + feed = { + "feed_sha256": "abc", + "families": { + "ssa_supplement.ssi_recipients": 52, + "usda_snap.state_benefits": 51, + "bea_nipa.personal_income": 1, + }, + } + with pytest.raises(ValueError, match="must stay status='compiled'"): + assert_target_parity_manifest_current( + manifest=downgraded, feed_families=feed + ) + + def test_registry_half_flags_undeclared_registry_family(self) -> None: + registry = _registry( + [ + "ssa_supplement.ssi_recipients", + "usda_snap.state_benefits", + "surprise.family", + ] + ) + feed = { + "feed_sha256": "abc", + "families": { + "ssa_supplement.ssi_recipients": 52, + "usda_snap.state_benefits": 51, + "bea_nipa.personal_income": 1, + }, + } + with pytest.raises(ValueError, match="does not declare"): + assert_target_parity_manifest_current( + manifest=_CONTRACT, feed_families=feed, registry=registry + ) + + +class TestShippedManifest: + def test_manifest_loads_nonempty_with_compiled_and_reviewed(self) -> None: + manifest = load_target_parity_manifest() + assert manifest.compiled_families + assert manifest.reviewed_exclusions + assert manifest.schema_version == 1 + + def test_every_reviewed_exclusion_carries_reason_and_evidence(self) -> None: + manifest = load_target_parity_manifest() + for family in manifest.families: + if family.status == REVIEWED_EXCLUSION_STATUS: + assert family.reason, family.name + assert family.classification, family.name + assert family.evidence, family.name + + def test_ssi_recipient_family_is_compiled(self) -> None: + manifest = load_target_parity_manifest() + for family in SSI_RECIPIENT_RED_LINE_FAMILIES: + assert manifest.by_name[family].status == COMPILED_STATUS + + def test_ssi_state_payments_family_is_compiled(self) -> None: + manifest = load_target_parity_manifest() + assert manifest.by_name["ssa_supplement.ssi_payments"].status == COMPILED_STATUS + + def test_oasdi_family_stays_compiled(self) -> None: + manifest = load_target_parity_manifest() + assert ( + manifest.by_name["ssa_supplement.oasdi_ssi_payments"].status + == COMPILED_STATUS + ) + + def test_manifest_feed_surface_matches_checked_in_inventory(self) -> None: + manifest = load_target_parity_manifest() + feed = load_target_parity_feed_families() + assert manifest.feed_surface_families == frozenset(feed["families"]) + + def test_manifest_sha_matches_feed_inventory_sha(self) -> None: + manifest = load_target_parity_manifest() + feed = load_target_parity_feed_families() + assert manifest.reference["feed_sha256"] == feed["feed_sha256"] + + def test_checked_in_anti_rot_passes(self) -> None: + # The checked-in half (manifest vs committed feed inventory + red line) + # runs without the feed and must pass on the shipped artifacts. + assert_target_parity_manifest_current() + + def test_source_absent_families_are_not_in_feed_inventory(self) -> None: + manifest = load_target_parity_manifest() + feed = load_target_parity_feed_families() + for family in manifest.source_absent_families: + assert family not in feed["families"] + + +def _load_generator(): + spec = importlib.util.spec_from_file_location( + "build_us_target_parity_manifest", _MANIFEST_GENERATOR + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class TestRegeneration: + """Feed-dependent reproduction — guarded on the pinned feed being present.""" + + def _feed_or_skip(self, generator): + feed_path = generator.DEFAULT_FEED_PATH + if not feed_path.exists(): + pytest.skip(f"pinned feed not present at {feed_path}") + return feed_path + + def test_committed_artifacts_match_regeneration(self) -> None: + generator = _load_generator() + feed_path = self._feed_or_skip(generator) + facts, feed_sha256 = generator._load_feed(feed_path) + manifest, feed_families = generator.build_manifest( + facts, feed_sha256, generator.DEFAULT_FEED_NAME + ) + committed_manifest = json.loads( + (_US_PACKAGE_DIR / "target_parity_manifest.json").read_text() + ) + committed_feed = json.loads( + (_US_PACKAGE_DIR / "target_parity_feed_families.json").read_text() + ) + assert manifest == committed_manifest + assert feed_families == committed_feed + + def test_gate_passes_on_real_compiled_registry(self) -> None: + generator = _load_generator() + feed_path = self._feed_or_skip(generator) + from populace.build.us_runtime.fiscal_targets import ( + compile_us_fiscal_target_registry, + ) + from populace.build.us_runtime.medicaid_take_up import ( + apply_us_medicaid_enrollment_substitutions, + ) + + facts, _ = generator._load_feed(feed_path) + registry = compile_us_fiscal_target_registry( + facts, target_period=2024, age_targets=True + ) + registry, _ = apply_us_medicaid_enrollment_substitutions(registry) + result = us_release_target_parity_gate(registry) + assert result.passed, result.failures + assert_target_parity_manifest_current(registry=registry) + assert "ssa_supplement.ssi_recipients" in registry_target_family_ids(registry) diff --git a/tools/build_us_fiscal_refresh_release.py b/tools/build_us_fiscal_refresh_release.py index dfea0dfc..620ea98f 100644 --- a/tools/build_us_fiscal_refresh_release.py +++ b/tools/build_us_fiscal_refresh_release.py @@ -69,6 +69,7 @@ assert_release_input_coverage_manifest_current, assert_take_up_contract_current, assert_take_up_treatments_consistent, + assert_target_parity_manifest_current, assert_validation_leaf_registry_current, compile_us_fiscal_target_registry, fetch_asec_2023_weeks_unemployed_source, @@ -118,6 +119,7 @@ us_register_consistency_gate, us_relationship_inputs_signal_gate, us_release_input_coverage_gate, + us_release_target_parity_gate, us_retirement_contributions_signal_gate, us_retirement_distributions_signal_gate, us_salt_refund_income_signal_gate, @@ -6395,6 +6397,23 @@ def main() -> None: target_registry, medicaid_enrollment_substitutions = ( apply_us_medicaid_enrollment_substitutions(target_registry) ) + # Target-parity contract (launch gate): every administrative target family + # the retired us-data/eCPS pipeline calibrated to must be compiled into the + # registry or carry a reviewed exclusion (target_parity_manifest.json). Runs + # on the full compiled + substituted registry — before the optional + # diagnostic JCT skip — so the gate sees the true family surface, and + # hard-fails the build on a silently dropped family or a rotted manifest, + # exactly like the release input-coverage gate on the export frame. + assert_target_parity_manifest_current(registry=target_registry) + target_parity_gate = us_release_target_parity_gate(target_registry) + if not target_parity_gate.passed: + raise RuntimeError( + "Release gates failed: " + + "; ".join( + f"Target parity coverage failed: {failure}" + for failure in target_parity_gate.failures + ) + ) target_specs = target_registry.specs if args.diagnostic_skip_tax_expenditure_targets: tax_expenditure_measures = { diff --git a/tools/build_us_target_parity_manifest.py b/tools/build_us_target_parity_manifest.py new file mode 100644 index 00000000..66c18482 --- /dev/null +++ b/tools/build_us_target_parity_manifest.py @@ -0,0 +1,442 @@ +"""Regenerate the US release target-parity manifest and feed-family inventory. + +The target-side analog of ``tools/build_us_release_input_coverage_manifest.py``. +Where that tool declares which reference-eCPS input columns a release must +persist, this one declares which administrative target *families* the compiled +populace registry must carry — the families the retired us-data/eCPS pipeline +calibrated to. + +Derivation (fully from the sha-pinned consumer feed + the deterministic registry +compile): + +- **Feed-family inventory** = every ``namespace.concept`` family id the pinned + ``consumer_facts_*.jsonl`` carries, with its fact count. Written to + ``target_parity_feed_families.json`` so the manifest consistency check runs in + CI without the 131 MB feed. +- **Compiled families** = the families the registry compiles today + (``compile_us_fiscal_target_registry(age_targets=True)`` + the reviewed CMS + Medicaid enrollment substitution, exactly as the release builder compiles it). + Every compiled family gets ``status: compiled``. +- **Reviewed exclusions** = every feed family that does NOT compile, each + classified with a reason naming its evidence (a sample ``source_record_id``, a + code constant, or the compiled sibling family that supersedes it). A feed + family with neither a compile nor a declared exclusion HALTS generation — the + anti-rot guarantee that a new administrative family cannot be silently ignored. +- **Source-absent us-data families** = administrative targets the retired + us-data pipeline calibrated to for which the pinned feed carries no ledger + fact (BLS Consumer Expenditure, WIC, HUD housing assistance). Declared as + ``source_absent`` reviewed exclusions so the diff records them. + +Run: uv run python tools/build_us_target_parity_manifest.py +It rewrites packages/populace-build/src/populace/build/us/target_parity_manifest.json +and target_parity_feed_families.json. A test asserts the committed files match +this regeneration, so the manifest cannot silently drift from the pinned feed. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +from collections import Counter +from pathlib import Path + +from populace.build.us_runtime.fiscal_targets import compile_us_fiscal_target_registry +from populace.build.us_runtime.medicaid_take_up import ( + apply_us_medicaid_enrollment_substitutions, +) +from populace.build.us_runtime.release_target_parity import ( + COMPILED_STATUS, + REVIEWED_EXCLUSION_STATUS, + SOURCE_ABSENT_CLASSIFICATION, + us_target_family_id, +) + +REPO_ROOT = Path(__file__).resolve().parents[1] +US_PACKAGE_DIR = ( + REPO_ROOT / "packages" / "populace-build" / "src" / "populace" / "build" / "us" +) +MANIFEST_PATH = US_PACKAGE_DIR / "target_parity_manifest.json" +FEED_FAMILIES_PATH = US_PACKAGE_DIR / "target_parity_feed_families.json" + +#: The pinned consumer feed the certified registry compiles from. +DEFAULT_FEED_PATH = ( + Path.home() + / "PolicyEngine" + / "_buildh-runtime" + / "inputs" + / "consumer_facts_buildh_v8.jsonl" +) +DEFAULT_FEED_NAME = "consumer_facts_buildh_v8.jsonl" +EXPECTED_FEED_SHA256_PREFIX = "94b7155f" +TARGET_PERIOD = 2024 + +# --------------------------------------------------------------------------- +# Reviewed exclusions — every feed family that does not compile, classified with +# an evidence-naming reason. Namespace rules cover the many one-per-series BEA +# aggregates; exact rules cover the rest. Reasons cite a compiled sibling +# family, a code constant, or the mechanism that makes the family non-linear / +# survey-derived / off-by-default. +# --------------------------------------------------------------------------- + +# (classification, reason, evidence) keyed by source_record_id namespace. +_NAMESPACE_EXCLUSIONS: dict[str, tuple[str, str, str]] = { + "bea_nipa": ( + "macro_control_total", + "BEA NIPA national-accounts aggregate (calendar-year macro control " + "total). Not a household-linear administrative level PolicyEngine-US " + "calibrates against: household income components are fit from the IRS " + "SOI micro tables (irs_soi.historic_table_2, compiled) and CBO " + "revenue-by-source projections (cbo.revenue_projection, compiled). The " + "NIPA aggregate is retained in the ledger as a macro cross-check, not a " + "calibration target.", + "compiled siblings irs_soi.historic_table_2 and cbo.revenue_projection " + "supply the household-linear income surface; sample fact " + "bea_nipa.cy2023.personal_income.a065rc.amount", + ), + "bea_regional": ( + "macro_control_total", + "BEA regional (state) personal-income account aggregate. State income " + "is calibrated from the IRS SOI state tables (irs_soi.state_2022 and " + "the historic_table_2 state rows, compiled) and Census STC " + "(census_stc.individual_income_tax_collections, compiled); the BEA " + "regional total is a macro state control without a per-record model " + "counterpart.", + "compiled siblings irs_soi.state_2022 and " + "census_stc.individual_income_tax_collections; sample fact " + "bea_regional.cy2023.state_personal_income.us.amount", + ), +} + +_RETIREMENT_CONTRIBUTION_EXCLUSION = ( + "input_side", + "IRS SOI / W-2 retirement-contribution aggregate. Retirement contributions " + "are modeled as imputed INPUT columns (traditional/roth 401(k) and IRA " + "contributions), governed by the input-coverage contract, not as reweighting " + "targets. Calibrating them as fiscal targets would double-govern a quantity " + "the input-coverage gate already owns.", + "RESTORED_REFERENCE_ECPS_REQUIRED_INPUTS declares the " + "*_contributions_desired input columns in release_input_coverage.py", +) + +_SNAP_PERSONS_EXCLUSION = ( + "not_modeled", + "USDA FNS SNAP average-monthly PERSONS. The SNAP assistance unit is often a " + "subset of the SPM unit (FY2024 FNS persons-per-household 1.88 vs 2.82 " + "simulated members per taker unit), so a person indicator overcounts FNS " + "participants by ~50%. The household caseload " + "(usda_snap.national/state_average_monthly_households, compiled) is the " + "calibrated count; persons stay unmapped until sub-unit participation is " + "modeled.", + "INDICATOR_LEDGER_TARGETS average_monthly_households comment + " + "test_snap_person_caseload_fact_is_not_compiled", +) + +# (classification, reason, evidence) keyed by exact family id. +_FAMILY_EXCLUSIONS: dict[str, tuple[str, str, str]] = { + "cbo.revenues": ( + "macro_control_total", + "CBO total federal revenue line (fiscal-year budget aggregate). The " + "household-linear CBO surface populace calibrates is the income-by-" + "source projection (cbo.revenue_projection, compiled as cbo:5); a total " + "revenue outturn is an aggregate, not a per-record target.", + "compiled sibling cbo.revenue_projection; sample fact " + "cbo.fy2023.revenues.individual_income_taxes.actual_amount", + ), + "census.popproj2023": ( + "superseded", + "Census Bureau population PROJECTIONS (forward vintage popproj2023). " + "Population is calibrated to the OBSERVED Census resident-population " + "estimates (census_pep.v2024 and national_resident_population_age, " + "compiled as census_pep:936); the projection series is a forward-looking " + "alternative, not the observed administrative count.", + "compiled sibling census_pep.v2024; sample fact " + "census.popproj2023.cy2023.national_population.age_0.population", + ), + "census_acs.acs1_2023": ( + "survey_derived", + "American Community Survey 1-year estimates. ACS is a household SAMPLE " + "SURVEY, not an administrative universe, so its aggregates are excluded " + "by principle. ACS age detail is used only at congressional-district " + "grain when include_congressional_district_targets is enabled (off for " + "the national release).", + "census_acs facts return None unless include_congressional_district_" + "targets in fiscal_targets._reference_from_ledger_fact; sample fact " + "census_acs.acs1_2023.b01001.female_age.01.age_15_to_17.female_population", + ), + "cms_nhe.medicaid_title_xix_expenditures": ( + "non_linear", + "CMS National Health Expenditure Medicaid Title XIX spending. Already " + "declared calibration_role=validation_only in DIRECT_LEDGER_TARGETS: " + "PolicyEngine-US allocates Medicaid spending from state totals through " + "person_weight-dependent denominators, so reweighting recomputes " + "per-person costs and this is not a linear calibration row.", + "DIRECT_LEDGER_TARGETS ('cms_nhe','expenditure_amount','medicaid_title_" + "xix') metadata calibration_role=validation_only", + ), + "federal_reserve_z1.households_nonprofits_balance_sheet": ( + "macro_control_total", + "Federal Reserve Financial Accounts (Z.1) households & nonprofits " + "net-worth aggregate. A macro balance-sheet total; household net worth " + "is imputed and calibrated from SCF micro on the input side " + "(scf_wealth source stage), not from this national aggregate.", + "scf_wealth.py source stage supplies the net-worth micro; sample fact " + "federal_reserve_z1.cy2023.households_nonprofits_balance_sheet.net_worth" + ".fl152090005.amount_outstanding", + ), + "hhs_acf_liheap.national_profile": ( + "deferred", + "HHS LIHEAP national profile (households served / funds). PolicyEngine-US " + "does not yet expose a LIHEAP receipt outcome to fit this count to a " + "per-household model counterpart, so it is deferred rather than dropped; " + "retained as a ledger reference fact.", + "no LIHEAP target_role in INDICATOR_LEDGER_TARGETS/DIRECT_LEDGER_TARGETS; " + "sample fact hhs_acf_liheap.fy2023.national_profile.state_programs." + "households_served", + ), + "hhs_acf_tanf.average_monthly_families": ( + "deferred", + "HHS ACF TANF average-monthly family caseload. TANF is calibrated on " + "BENEFIT DOLLARS (hhs_acf_tanf.cash_assistance, compiled as " + "hhs_acf_tanf:30); the caseload COUNT is not yet wired to a TANF-receipt " + "indicator (the SNAP-household caseload analog for TANF), so it is " + "deferred.", + "compiled sibling hhs_acf_tanf.cash_assistance carries the dollar " + "targets; sample fact " + "hhs_acf_tanf.fy2024.average_monthly_families.us.us_total.total_families", + ), + "hhs_acf_tanf.average_monthly_recipients": ( + "deferred", + "HHS ACF TANF average-monthly recipient caseload. As with the family " + "caseload, TANF is calibrated on benefit dollars " + "(hhs_acf_tanf.cash_assistance, compiled); the recipient COUNT has no " + "wired TANF-receipt indicator yet and is deferred.", + "compiled sibling hhs_acf_tanf.cash_assistance; sample fact " + "hhs_acf_tanf.fy2024.average_monthly_recipients.us.us_total." + "total_recipients", + ), + "irs_soi.congressional_district_2022": ( + "off_by_default", + "IRS SOI congressional-district table. CD-level targets are opt-in " + "(include_congressional_district_targets=False for the national " + "release); the national and state SOI surfaces are compiled instead. " + "Enabling CD targets compiles these — they are excluded for the national " + "build by design, not dropped.", + "test_soi_congressional_district_targets_are_opt_in + the " + "include_congressional_district_targets gate in " + "fiscal_targets._soi_reference_from_fact", + ), + "irs_soi.form_w2_401k_elective_deferrals": _RETIREMENT_CONTRIBUTION_EXCLUSION, + "irs_soi.form_w2_designated_roth_401k_contributions": ( + _RETIREMENT_CONTRIBUTION_EXCLUSION + ), + "irs_soi.roth_ira_contributions": _RETIREMENT_CONTRIBUTION_EXCLUSION, + "irs_soi.traditional_ira_contributions": _RETIREMENT_CONTRIBUTION_EXCLUSION, + "irs_soi.form_w2_social_security_tips": ( + "not_modeled", + "IRS W-2 Social Security tip aggregates. Already declared in " + "US_FISCAL_TARGET_SUPPORT_EXCLUSIONS: current US support does not " + "materialize a positive tip_income source column, so the W-2 tip return " + "counts need the SIPP/ORG tip source stage wired before calibration.", + "US_FISCAL_TARGET_SUPPORT_EXCLUSIONS entry " + "irs_soi.ty2023.form_w2_social_security_tips.box_7_social_security_tips." + "return_count", + ), + "kff.marketplace_effectuated_enrollment": ( + "superseded", + "Kaiser Family Foundation state marketplace effectuated-enrollment " + "compilation. ACA marketplace enrollment is calibrated from the primary " + "CMS administrative source (cms_aca.oep2024, compiled as cms_aca:102); " + "KFF is a secondary aggregator of the same underlying CMS data, excluded " + "to avoid a duplicate target.", + "compiled sibling cms_aca.oep2024; sample fact " + "kff.marketplace_effectuated_enrollment.2024.state.us." + "total_effectuated_marketplace_enrollment", + ), + "usda_snap.national_average_monthly_persons": _SNAP_PERSONS_EXCLUSION, + "usda_snap.state_average_monthly_persons": _SNAP_PERSONS_EXCLUSION, +} + +# Administrative target families the retired us-data pipeline calibrated to for +# which the pinned feed carries NO ledger fact — source-absent by the feed. +# (family id, reason, evidence) with classification source_absent. +_SOURCE_ABSENT_US_DATA_FAMILIES: dict[str, tuple[str, str]] = { + "bls.consumer_expenditure": ( + "BLS Consumer Expenditure Survey aggregates targeted by the retired " + "us-data pipeline (nation/bls/ce). The pinned consumer feed carries no " + "BLS source fact, so there is no ledger-shaped fact to compile; " + "source-absent pending a Ledger BLS CE ingest.", + "policyengine-us-data (archived) references nation/bls/ce; feed source " + "families carry no 'bls' source", + ), + "wic.national_summary": ( + "USDA WIC national annual summary targeted by the retired us-data " + "pipeline (WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE in etl_national_targets). " + "The pinned feed carries no WIC source fact; source-absent. WIC receipt " + "is modeled via the would_claim_wic take-up input, not a target.", + "policyengine-us-data (archived) db/etl_national_targets.py " + "WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE; feed carries no 'wic' source", + ), + "hud.housing_assistance": ( + "HUD housing-assistance aggregates targeted by the retired us-data " + "pipeline (db/etl_housing_assistance.py). The pinned feed carries no HUD " + "source fact; source-absent pending a Ledger HUD ingest.", + "policyengine-us-data (archived) db/etl_housing_assistance.py; feed " + "carries no 'hud' source", + ), +} + + +def _load_feed(path: Path) -> tuple[list[dict], str]: + raw = path.read_bytes() + sha256 = hashlib.sha256(raw).hexdigest() + facts = [ + json.loads(line) for line in raw.decode("utf-8").splitlines() if line.strip() + ] + return facts, sha256 + + +def _feed_family_counts(facts: list[dict]) -> Counter[str]: + counts: Counter[str] = Counter() + for fact in facts: + source_record_id = (fact.get("lineage") or {}).get("source_record_id", "") + family = us_target_family_id(str(source_record_id)) + if family: + counts[family] += 1 + return counts + + +def _compiled_families(facts: list[dict]) -> set[str]: + registry = compile_us_fiscal_target_registry( + facts, target_period=TARGET_PERIOD, age_targets=True + ) + registry, _ = apply_us_medicaid_enrollment_substitutions(registry) + return { + us_target_family_id(spec.name) + for spec in registry.specs + if us_target_family_id(spec.name) + } + + +def _exclusion_for(family: str) -> tuple[str, str, str]: + if family in _FAMILY_EXCLUSIONS: + return _FAMILY_EXCLUSIONS[family] + namespace = family.split(".", 1)[0] + if namespace in _NAMESPACE_EXCLUSIONS: + return _NAMESPACE_EXCLUSIONS[namespace] + raise SystemExit( + f"Feed family {family!r} neither compiles nor has a declared reviewed " + "exclusion. Add an entry to _FAMILY_EXCLUSIONS or _NAMESPACE_EXCLUSIONS " + "in tools/build_us_target_parity_manifest.py naming its evidence — a new " + "administrative family must never be silently ignored." + ) + + +def build_manifest( + facts: list[dict], feed_sha256: str, feed_name: str +) -> tuple[dict, dict]: + feed_counts = _feed_family_counts(facts) + compiled = _compiled_families(facts) + + families: dict[str, dict] = {} + for family in sorted(feed_counts): + if family in compiled: + families[family] = {"status": COMPILED_STATUS} + else: + classification, reason, evidence = _exclusion_for(family) + families[family] = { + "status": REVIEWED_EXCLUSION_STATUS, + "classification": classification, + "reason": reason, + "evidence": evidence, + } + + # Compiled families with no feed fact should not exist (every compiled spec + # traces to a feed fact); guard against a mapping that invents a family. + invented = sorted(compiled - set(feed_counts)) + if invented: + raise SystemExit( + f"Registry compiles family(ies) with no feed fact: {invented}. " + "The family-id derivation or the wiring is inconsistent." + ) + + for family, (reason, evidence) in sorted(_SOURCE_ABSENT_US_DATA_FAMILIES.items()): + families[family] = { + "status": REVIEWED_EXCLUSION_STATUS, + "classification": SOURCE_ABSENT_CLASSIFICATION, + "reason": reason, + "evidence": evidence, + } + + n_compiled = sum(1 for e in families.values() if e["status"] == COMPILED_STATUS) + n_reviewed = len(families) - n_compiled + manifest = { + "schema_version": 1, + "reference": { + "feed": feed_name, + "feed_sha256": feed_sha256, + "target_period": str(TARGET_PERIOD), + "registry_compile": ( + "compile_us_fiscal_target_registry(age_targets=True) + " + "apply_us_medicaid_enrollment_substitutions" + ), + "us_data_source": ( + "policyengine-us-data (archived): db/etl_national_targets.py, " + "db/etl_*.py, utils/national_target_parity.py" + ), + "family_granularity": ( + "namespace.concept of the ledger source_record_id (us_target_family_id)" + ), + "compiled_families": str(n_compiled), + "reviewed_exclusions": str(n_reviewed), + }, + "families": dict(sorted(families.items())), + } + + feed_families = { + "feed": feed_name, + "feed_sha256": feed_sha256, + "families": dict(sorted(feed_counts.items())), + } + return manifest, feed_families + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--ledger-facts", + type=Path, + default=DEFAULT_FEED_PATH, + help="Path to the pinned consumer feed JSONL.", + ) + parser.add_argument( + "--feed-name", + default=DEFAULT_FEED_NAME, + help="Feed filename recorded in the manifest/inventory.", + ) + args = parser.parse_args() + + facts, feed_sha256 = _load_feed(args.ledger_facts) + if not feed_sha256.startswith(EXPECTED_FEED_SHA256_PREFIX): + raise SystemExit( + f"Feed sha256 {feed_sha256[:8]} does not match the pinned prefix " + f"{EXPECTED_FEED_SHA256_PREFIX}; refusing to regenerate against an " + "unexpected feed." + ) + + manifest, feed_families = build_manifest(facts, feed_sha256, args.feed_name) + + MANIFEST_PATH.write_text(json.dumps(manifest, indent=1) + "\n", encoding="utf-8") + FEED_FAMILIES_PATH.write_text( + json.dumps(feed_families, indent=1) + "\n", encoding="utf-8" + ) + print( + f"Wrote {MANIFEST_PATH.name}: " + f"{manifest['reference']['compiled_families']} compiled, " + f"{manifest['reference']['reviewed_exclusions']} reviewed exclusions " + f"({len(feed_families['families'])} feed families)." + ) + + +if __name__ == "__main__": + main() From ade995e81efa2e016d3c524da437bcee11c46ae9 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 12 Jul 2026 22:19:39 -0400 Subject: [PATCH 3/4] Stub the target-parity gate in the builder main() gate-failure test test_main_writes_diagnostics_before_post_calibration_gate_failure drives main() with a stub target registry, so it must stub the new target-parity gate and anti-rot exactly as it already stubs target_profile_coverage_gate and assert_release_input_coverage_manifest_current (a stub registry cannot carry the full 25-family surface the real gate requires). Co-Authored-By: Claude Fable 5 --- .../tests/test_us_fiscal_refresh_builder.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/populace-build/tests/test_us_fiscal_refresh_builder.py b/packages/populace-build/tests/test_us_fiscal_refresh_builder.py index 0bb64847..0f9d89a4 100644 --- a/packages/populace-build/tests/test_us_fiscal_refresh_builder.py +++ b/packages/populace-build/tests/test_us_fiscal_refresh_builder.py @@ -2298,6 +2298,18 @@ def n(self, entity): details={"checked": True}, ), ) + monkeypatch.setattr( + builder, "assert_target_parity_manifest_current", lambda **kwargs: None + ) + monkeypatch.setattr( + builder, + "us_release_target_parity_gate", + lambda registry, **kwargs: builder.GateResult( + name="us_release_target_parity", + passed=True, + details={"checked": True}, + ), + ) def fake_load_frame(path): captured["source_stage_events"].append("load_frame") From 1c3e76818a9d794b6e61801fdb05297f10ca3183 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 12 Jul 2026 22:28:31 -0400 Subject: [PATCH 4/4] Declare target-parity artifacts in the us country package; drop incumbent-repo token CI (wheels + spec-only tests) surfaced two shipping-contract violations: - the us country package must declare every file it ships: add target_parity_manifest.json and target_parity_feed_families.json to country_package.json resources. - the live tree may not name the retired incumbent data package: reword the source-absent provenance / us_data_source strings in the manifest generator (and regenerate the manifest) to "retired us-data pipeline (archived)" instead of the hyphenated incumbent package name, which test_no_incumbent_data_package_references_in_live_tree forbids outside the three allowlisted eCPS-parity-reference files. Co-Authored-By: Claude Fable 5 --- .../src/populace/build/us/country_package.json | 2 ++ .../populace/build/us/target_parity_manifest.json | 8 ++++---- tools/build_us_target_parity_manifest.py | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/populace-build/src/populace/build/us/country_package.json b/packages/populace-build/src/populace/build/us/country_package.json index 252887ea..07708026 100644 --- a/packages/populace-build/src/populace/build/us/country_package.json +++ b/packages/populace-build/src/populace/build/us/country_package.json @@ -19,6 +19,8 @@ "state_spm_poverty_levels.json", "support_spine.json", "take_up_contract.json", + "target_parity_feed_families.json", + "target_parity_manifest.json", "tax_expenditure_reforms.json" ] } diff --git a/packages/populace-build/src/populace/build/us/target_parity_manifest.json b/packages/populace-build/src/populace/build/us/target_parity_manifest.json index 4fdefefe..656fbce6 100644 --- a/packages/populace-build/src/populace/build/us/target_parity_manifest.json +++ b/packages/populace-build/src/populace/build/us/target_parity_manifest.json @@ -5,7 +5,7 @@ "feed_sha256": "94b7155f7ca9e2de32ddb3a0add2fff2d8c66e73147fe5bd112cff3ba69b1669", "target_period": "2024", "registry_compile": "compile_us_fiscal_target_registry(age_targets=True) + apply_us_medicaid_enrollment_substitutions", - "us_data_source": "policyengine-us-data (archived): db/etl_national_targets.py, db/etl_*.py, utils/national_target_parity.py", + "us_data_source": "retired us-data pipeline (archived): db/etl_national_targets.py, db/etl_*.py, utils/national_target_parity.py", "family_granularity": "namespace.concept of the ledger source_record_id (us_target_family_id)", "compiled_families": "25", "reviewed_exclusions": "55" @@ -225,7 +225,7 @@ "status": "reviewed_exclusion", "classification": "source_absent", "reason": "BLS Consumer Expenditure Survey aggregates targeted by the retired us-data pipeline (nation/bls/ce). The pinned consumer feed carries no BLS source fact, so there is no ledger-shaped fact to compile; source-absent pending a Ledger BLS CE ingest.", - "evidence": "policyengine-us-data (archived) references nation/bls/ce; feed source families carry no 'bls' source" + "evidence": "retired us-data pipeline (archived) references nation/bls/ce; feed source families carry no 'bls' source" }, "cbo.revenue_projection": { "status": "compiled" @@ -303,7 +303,7 @@ "status": "reviewed_exclusion", "classification": "source_absent", "reason": "HUD housing-assistance aggregates targeted by the retired us-data pipeline (db/etl_housing_assistance.py). The pinned feed carries no HUD source fact; source-absent pending a Ledger HUD ingest.", - "evidence": "policyengine-us-data (archived) db/etl_housing_assistance.py; feed carries no 'hud' source" + "evidence": "retired us-data pipeline (archived) db/etl_housing_assistance.py; feed carries no 'hud' source" }, "irs_soi.congressional_district_2022": { "status": "reviewed_exclusion", @@ -414,7 +414,7 @@ "status": "reviewed_exclusion", "classification": "source_absent", "reason": "USDA WIC national annual summary targeted by the retired us-data pipeline (WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE in etl_national_targets). The pinned feed carries no WIC source fact; source-absent. WIC receipt is modeled via the would_claim_wic take-up input, not a target.", - "evidence": "policyengine-us-data (archived) db/etl_national_targets.py WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE; feed carries no 'wic' source" + "evidence": "retired us-data pipeline (archived) db/etl_national_targets.py WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE; feed carries no 'wic' source" } } } diff --git a/tools/build_us_target_parity_manifest.py b/tools/build_us_target_parity_manifest.py index 66c18482..59b3ee80 100644 --- a/tools/build_us_target_parity_manifest.py +++ b/tools/build_us_target_parity_manifest.py @@ -266,23 +266,23 @@ "us-data pipeline (nation/bls/ce). The pinned consumer feed carries no " "BLS source fact, so there is no ledger-shaped fact to compile; " "source-absent pending a Ledger BLS CE ingest.", - "policyengine-us-data (archived) references nation/bls/ce; feed source " - "families carry no 'bls' source", + "retired us-data pipeline (archived) references nation/bls/ce; feed " + "source families carry no 'bls' source", ), "wic.national_summary": ( "USDA WIC national annual summary targeted by the retired us-data " "pipeline (WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE in etl_national_targets). " "The pinned feed carries no WIC source fact; source-absent. WIC receipt " "is modeled via the would_claim_wic take-up input, not a target.", - "policyengine-us-data (archived) db/etl_national_targets.py " + "retired us-data pipeline (archived) db/etl_national_targets.py " "WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE; feed carries no 'wic' source", ), "hud.housing_assistance": ( "HUD housing-assistance aggregates targeted by the retired us-data " "pipeline (db/etl_housing_assistance.py). The pinned feed carries no HUD " "source fact; source-absent pending a Ledger HUD ingest.", - "policyengine-us-data (archived) db/etl_housing_assistance.py; feed " - "carries no 'hud' source", + "retired us-data pipeline (archived) db/etl_housing_assistance.py; " + "feed carries no 'hud' source", ), } @@ -381,7 +381,7 @@ def build_manifest( "apply_us_medicaid_enrollment_substitutions" ), "us_data_source": ( - "policyengine-us-data (archived): db/etl_national_targets.py, " + "retired us-data pipeline (archived): db/etl_national_targets.py, " "db/etl_*.py, utils/national_target_parity.py" ), "family_granularity": (