diff --git a/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.png b/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.png
new file mode 100644
index 000000000..27b39ce6f
Binary files /dev/null and b/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.png differ
diff --git a/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.svg b/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.svg
new file mode 100644
index 000000000..d32ae28c0
--- /dev/null
+++ b/docs/analysis/pe_us_bridge_20260930/household_a_waterfall.svg
@@ -0,0 +1,4503 @@
+
+
+
diff --git a/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.png b/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.png
new file mode 100644
index 000000000..d1138027c
Binary files /dev/null and b/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.png differ
diff --git a/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.svg b/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.svg
new file mode 100644
index 000000000..b357e6694
--- /dev/null
+++ b/docs/analysis/pe_us_bridge_20260930/household_b_waterfall.svg
@@ -0,0 +1,5932 @@
+
+
+
diff --git a/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.png b/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.png
new file mode 100644
index 000000000..e03c7ece4
Binary files /dev/null and b/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.png differ
diff --git a/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.svg b/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.svg
new file mode 100644
index 000000000..e25ffcef2
--- /dev/null
+++ b/docs/analysis/pe_us_bridge_20260930/household_c_waterfall.svg
@@ -0,0 +1,4720 @@
+
+
+
diff --git a/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.json b/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.json
new file mode 100644
index 000000000..72040b219
--- /dev/null
+++ b/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.json
@@ -0,0 +1,5412 @@
+{
+ "schema_version": "pe_us_bridge.minimum_benefit_sample_households.v2",
+ "label": "Illustrative household, not survey data",
+ "reform": "a minimum benefit set at 73% of the aged poverty threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option 2)",
+ "provenance": {
+ "microcosm_dynamics": {
+ "repository": "PolicyEngine/microcosm-dynamics",
+ "commit": "a915b96e293cc141331c8b02196bbbcbdc8da44d",
+ "code_dirty": false,
+ "script": "scripts/pe_us_minimum_benefit_sample_households.py",
+ "bridge": "src/populace_dynamics/bridge/policyengine_us.py"
+ },
+ "policyengine_us": {
+ "release": {
+ "package": "policyengine-us",
+ "version": "2.18.0",
+ "index_url": "https://pypi.org/project/policyengine-us/2.18.0/",
+ "wheel": "policyengine_us-2.18.0-py3-none-any.whl",
+ "wheel_sha256": "28e32bc1339e8ffc1ed676ac1c9ecd468d191685608039643915f63e1357085f",
+ "package_record_digest": "9f9f090894638e3d0e0dd5ad8ab34f36cf8cb2134ef3627407d0653381906007",
+ "package_record_lines": 17551
+ },
+ "installed": {
+ "version": "2.18.0",
+ "policyengine_core_version": "3.32.11",
+ "python_version": "3.13.9",
+ "installer": "uv",
+ "record_sha256": "54849d9fe99112b30fee5a8ced3f16b549ac47d15e6291fab3ad6052362ace36"
+ },
+ "source_check": {
+ "kind": "index",
+ "version": "2.18.0",
+ "core_version": "3.32.11",
+ "record_sha256": "54849d9fe99112b30fee5a8ced3f16b549ac47d15e6291fab3ad6052362ace36",
+ "published": true,
+ "reasons": []
+ },
+ "record_check": {
+ "prefix": "policyengine_us/",
+ "files_checked": 17551,
+ "mismatched": 0,
+ "missing": 0,
+ "extra": 0,
+ "package_record_digest": "9f9f090894638e3d0e0dd5ad8ab34f36cf8cb2134ef3627407d0653381906007",
+ "package_record_lines": 17551
+ },
+ "interpreter": "POPULACE_DYNAMICS_PE_US_PYTHON: a uv virtual environment holding policyengine-us==2.18.0 from PyPI",
+ "parameter_updates": [
+ {
+ "parameter": "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single",
+ "file": "policyengine_us/parameters/gov/states/ca/cdss/state_supplement/payment_standard/aged_or_disabled/amount/single.yaml",
+ "states": [
+ "CA"
+ ],
+ "year": 2026,
+ "value": 1233.94,
+ "unit": "dollars a month",
+ "source": {
+ "publisher": "California Department of Social Services",
+ "title": "SSI Total Monthly Payment Amounts 2026",
+ "revision": "Rev. 1/26",
+ "effective": "2026-01-01",
+ "entry": "Single Person, Aged or with Qualifying Disability; Independent Living - Residing in Own Household: $1,233.94",
+ "url": "https://cdss.ca.gov/Portals/13/SHD/ParaRegIndex/SSI%20Monthly%20Payment%20Amounts%202026.pdf",
+ "retrieved": "2026-09-30",
+ "sha256": "75a9937075ed1891cf6a0dd481bd3f0516b1ad11331041555f1d8c047bb57228"
+ },
+ "file_sha256": "1d79c7dd6036c72702b5de44eb88ea6804614fa93c9524812e922c75ce28059b",
+ "installed_value": 1206.94,
+ "installed_value_dated": "2025-01-01"
+ }
+ ]
+ },
+ "payment_year": 2026
+ },
+ "worker": {
+ "label": "Illustrative household, not survey data",
+ "inputs": {
+ "birth_year": 1958,
+ "sex": "female",
+ "first_earnings_year": 1980,
+ "earnings_years": 22,
+ "share_of_average_wage_index": 0.25,
+ "entitlement_year": 2024
+ },
+ "earnings_history": {
+ "1980": 3128.36,
+ "1981": 3443.28,
+ "1982": 3632.84,
+ "1983": 3809.81,
+ "1984": 4033.77,
+ "1985": 4205.63,
+ "1986": 4330.45,
+ "1987": 4606.63,
+ "1988": 4833.51,
+ "1989": 5024.89,
+ "1990": 5256.99,
+ "1991": 5452.9,
+ "1992": 5733.85,
+ "1993": 5783.17,
+ "1994": 5938.38,
+ "1995": 6176.41,
+ "1996": 6478.48,
+ "1997": 6856.5,
+ "1998": 7215.36,
+ "1999": 7617.46,
+ "2000": 8038.7,
+ "2001": 8230.48
+ },
+ "earnings_rule": "25% of the national average wage index (ss.params.load_ssa_parameters().nawi) in each year, rounded to the cent",
+ "record_years": {
+ "basis": "old_age",
+ "birth_year": 1958,
+ "window_year": 2024,
+ "threshold_year": 2020,
+ "last_year": 2023,
+ "onset_year": null,
+ "death_year": null
+ },
+ "years_of_coverage": {
+ "years": 22,
+ "counted_years": [
+ 1980,
+ 1981,
+ 1982,
+ 1983,
+ 1984,
+ 1985,
+ 1986,
+ 1987,
+ 1988,
+ 1989,
+ 1990,
+ 1991,
+ 1992,
+ 1993,
+ 1994,
+ 1995,
+ 1996,
+ 1997,
+ 1998,
+ 1999,
+ 2000,
+ 2001
+ ],
+ "through_year": 2023,
+ "note": "Invented history: covered earnings in every year 1980-2001 and none after, by construction. Each counted year's earnings reach four quarters of coverage (coverage.count_coverage_years); the PSID observation fields do not apply."
+ },
+ "pia_record": {
+ "pia": 613.8,
+ "aime": 682.0,
+ "basis": "old_age",
+ "method": "ss.statutory_aime.aime + ss.benefits.pia",
+ "eligibility_year": 2020,
+ "computation_end_year": 2023
+ },
+ "threshold": {
+ "year": 2020,
+ "annual": 12413.0,
+ "source": {
+ "kind": "census_capture",
+ "path": "data/external/census_poverty_thresholds_1982_2022.json",
+ "sha256": "288399c475ae3ff02e6d8367425ee0a568b50d239da5656f24d0d2dfafabfb53",
+ "row": "one_65_plus",
+ "years": [
+ 1982,
+ 2022
+ ],
+ "captured_years": [
+ 1982,
+ 1986,
+ 1988,
+ 1989,
+ 1990,
+ 1991,
+ 1992,
+ 1994,
+ 1995,
+ 1996,
+ 1997,
+ 1998,
+ 1999,
+ 2000,
+ 2001,
+ 2002,
+ 2003,
+ 2004,
+ 2005,
+ 2006,
+ 2007,
+ 2008,
+ 2009,
+ 2010,
+ 2011,
+ 2012,
+ 2013,
+ 2014,
+ 2015,
+ 2016,
+ 2017,
+ 2018,
+ 2019,
+ 2020,
+ 2021,
+ 2022
+ ]
+ }
+ },
+ "option": {
+ "number": 2,
+ "label": "Standard price-indexed minimum benefit",
+ "schedule": "standard",
+ "indexing": "price",
+ "uniform_cut": 0.1281
+ },
+ "schedule_share": 0.73,
+ "outcome": {
+ "option": 2,
+ "in_window": true,
+ "work_years_star": 22.0,
+ "minimum": 755.1241666666666,
+ "cut_pia": 535.1722199999999,
+ "option_pia": 755.1241666666666,
+ "on_minimum": true,
+ "reason": "on_minimum"
+ },
+ "outcome_rows_checked": [
+ "MS0",
+ "MS1",
+ "MS4"
+ ],
+ "option_1_outcome": {
+ "option": 1,
+ "in_window": true,
+ "work_years_star": 22.0,
+ "minimum": 0.0,
+ "cut_pia": 537.3819,
+ "option_pia": 537.3819,
+ "on_minimum": false,
+ "reason": "no_minimum_option"
+ },
+ "claim_factor": 0.95555552,
+ "months_from_full_retirement_age": -8,
+ "current_law": {
+ "pia_at_eligibility": 613.8,
+ "cola_steps": [
+ {
+ "determination_year": 2020,
+ "rate": 0.013000000000000001,
+ "pia_before": 613.8,
+ "pia_after": 621.7
+ },
+ {
+ "determination_year": 2021,
+ "rate": 0.059000000000000004,
+ "pia_before": 621.7,
+ "pia_after": 658.3
+ },
+ {
+ "determination_year": 2022,
+ "rate": 0.087,
+ "pia_before": 658.3,
+ "pia_after": 715.5
+ },
+ {
+ "determination_year": 2023,
+ "rate": 0.032,
+ "pia_before": 715.5,
+ "pia_after": 738.3
+ },
+ {
+ "determination_year": 2024,
+ "rate": 0.025,
+ "pia_before": 738.3,
+ "pia_after": 756.7
+ },
+ {
+ "determination_year": 2025,
+ "rate": 0.028,
+ "pia_before": 756.7,
+ "pia_after": 777.8
+ }
+ ],
+ "pia_payment_year": 777.8,
+ "monthly_benefit": 743,
+ "annual_benefit": 8916
+ },
+ "reform": {
+ "pia_at_eligibility": 755.1241666666666,
+ "cola_steps": [
+ {
+ "determination_year": 2020,
+ "rate": 0.013000000000000001,
+ "pia_before": 755.1241666666666,
+ "pia_after": 764.9
+ },
+ {
+ "determination_year": 2021,
+ "rate": 0.059000000000000004,
+ "pia_before": 764.9,
+ "pia_after": 810.0
+ },
+ {
+ "determination_year": 2022,
+ "rate": 0.087,
+ "pia_before": 810.0,
+ "pia_after": 880.4
+ },
+ {
+ "determination_year": 2023,
+ "rate": 0.032,
+ "pia_before": 880.4,
+ "pia_after": 908.5
+ },
+ {
+ "determination_year": 2024,
+ "rate": 0.025,
+ "pia_before": 908.5,
+ "pia_after": 931.2
+ },
+ {
+ "determination_year": 2025,
+ "rate": 0.028,
+ "pia_before": 931.2,
+ "pia_after": 957.2
+ }
+ ],
+ "pia_payment_year": 957.2,
+ "monthly_benefit": 914,
+ "annual_benefit": 10968
+ },
+ "reduced_current_law_memo": {
+ "pia_at_eligibility": 537.3819,
+ "cola_steps": [
+ {
+ "determination_year": 2020,
+ "rate": 0.013000000000000001,
+ "pia_before": 537.3819,
+ "pia_after": 544.3
+ },
+ {
+ "determination_year": 2021,
+ "rate": 0.059000000000000004,
+ "pia_before": 544.3,
+ "pia_after": 576.4
+ },
+ {
+ "determination_year": 2022,
+ "rate": 0.087,
+ "pia_before": 576.4,
+ "pia_after": 626.5
+ },
+ {
+ "determination_year": 2023,
+ "rate": 0.032,
+ "pia_before": 626.5,
+ "pia_after": 646.5
+ },
+ {
+ "determination_year": 2024,
+ "rate": 0.025,
+ "pia_before": 646.5,
+ "pia_after": 662.6
+ },
+ {
+ "determination_year": 2025,
+ "rate": 0.028,
+ "pia_before": 662.6,
+ "pia_after": 681.1
+ }
+ ],
+ "pia_payment_year": 681.1,
+ "monthly_benefit": 650,
+ "annual_benefit": 7800
+ },
+ "cola": {
+ "history": {
+ "path": "data/external/ssa_cola_history.json",
+ "sha256": "12da7f0e0d33fc53eaa31567d86c3cc035a49feefc2e4695bceea3379c2a38db",
+ "content_sha256": "8f1d75cab8bd1dba226c83bdff28a0ca1b21167e803eb65ff1d13d5cbe2255e0",
+ "schema_version": "ssa_cola_history.v1",
+ "source_unit": "percent",
+ "runtime_unit": "fraction",
+ "year_basis": "determination_year",
+ "runtime_first_determination_year": 1979,
+ "runtime_last_determination_year": 2022,
+ "runtime_n_years": 44
+ },
+ "history_years": [
+ 1979,
+ 2022
+ ],
+ "cpi_w_path": "policyengine_us/parameters/gov/ssa/uprating.yaml",
+ "cpi_w_path_is_relative_to": "the installed policyengine-us release",
+ "cpi_w_sha256": "fcf5049f6e69444296fde0deb8f0d014b14dc806c5424561d475e5b2dda7468c",
+ "cpi_w_third_quarter_averages": {
+ "2022": 268.421,
+ "2023": 291.901,
+ "2024": 301.236,
+ "2025": 308.729,
+ "2026": 317.265
+ },
+ "derived_rates": {
+ "2022": 0.087,
+ "2023": 0.032,
+ "2024": 0.025,
+ "2025": 0.028
+ },
+ "overlap_checked": [
+ 2022
+ ],
+ "rule": "42 USC 415(i)(1)(D): the percentage by which the third-quarter CPI-W exceeds that of the last cost-of-living computation quarter, rounded to the nearest one-tenth of 1 percent"
+ },
+ "rounding": "PIA: ss.benefits.pia floors to a dime; each COLA step floors to a dime (42 USC 415(i)(2)(A)(ii)); the monthly benefit is the PIA times the claim factor rounded down to the dollar (the 415(g) reading recorded in min_benefit_track_m/policy.py's MINIMUM_ROUNDING_DIME note)",
+ "parameters": {
+ "ssa": "policyengine_us/parameters/gov/ssa in the pinned policyengine-us release (see provenance.policyengine_us)",
+ "quarter_of_coverage": {
+ "kind": "committed_capture",
+ "path": "data/external/ssa_quarter_of_coverage_amounts.json",
+ "sha256": "6f964e8fad41deb0985115b3cb18b4b92ea9b7ea10a11273e5e8ecc6834ae1f8",
+ "captured_from": "policyengine_us/parameters/gov/ssa/social_security/quarters_of_coverage_threshold.yaml",
+ "captured_from_sha256": "12354a0585756dbffcd9fae6aa6b49b2420619e9ec8e47c5ef953c41693f242b",
+ "pe_us_revision": "a03e82e503"
+ }
+ }
+ },
+ "households": [
+ {
+ "key": "A",
+ "label": "Household A",
+ "description": "The worker alone: no income besides Social Security, $1,500 in the bank (under the SSI resource limit), renting for $800 a month",
+ "taxable_private_pension_income": 0.0,
+ "purpose": "SSI-eligible at baseline and under the reform"
+ },
+ {
+ "key": "B",
+ "label": "Household B",
+ "description": "The same worker with a $4,800-a-year ($400-a-month) private pension, which puts her countable income above the federal SSI benefit rate",
+ "taxable_private_pension_income": 4800.0,
+ "purpose": "SSI-ineligible: the SNAP and state-supplement path"
+ },
+ {
+ "key": "C",
+ "label": "Household C",
+ "description": "The same worker with a $31,200-a-year ($2,600-a-month) private pension, enough for part of her Social Security to be taxable",
+ "taxable_private_pension_income": 31200.0,
+ "purpose": "The federal and state income-tax path"
+ }
+ ],
+ "states": {
+ "CA": {
+ "name": "California",
+ "mechanism": "A state SSI supplement that PolicyEngine-US models",
+ "evidence": [
+ "variables/gov/states/ca/cdss/state_supplement/ca_state_supplement.py:13-17: the supplement is the payment standard less federal SSI less SSI countable income, floored at zero, so it is reduced dollar for dollar by countable income above the federal benefit rate",
+ "parameters/gov/states/ca/cdss/state_supplement/payment_standard/aged_or_disabled/amount/single.yaml:12-13: the aged or disabled single payment standard is 1,206.94 a month from 2025-01-01, with no later entry; this script sets the 2026 value to CDSS's published 1,233.94 (PARAMETER_UPDATES)",
+ "parameters/gov/usda/snap/income/sources/unearned_spm_unit.yaml:13: SNAP counts the California supplement as unearned income"
+ ]
+ },
+ "MT": {
+ "name": "Montana",
+ "mechanism": "An income tax that reaches federally taxable Social Security, and an elderly renter credit that counts all Social Security",
+ "evidence": [
+ "variables/gov/states/mt/tax/income/base/mt_agi_indiv.py: Montana AGI starts from federal AGI, which includes taxable Social Security; the separate Montana Social Security adjustment applies only when gov.states.mt.tax.income.social_security.applies is true",
+ "parameters/gov/states/mt/tax/income/social_security/applies.yaml: false from 2024",
+ "variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit.py:26-42: a refundable credit of property tax plus countable rent (15 percent of rent; MCA 15-30-2337(11)) less an income reduction, floored at zero, capped at 1,150 and multiplied by a schedule of gross household income (MCA 15-30-2340(2), (5) and (6))",
+ "variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_net_household_income.py:18-33: despite its name, this variable is the income reduction, not net household income. Net household income is gross household income less a 12,600 standard exclusion (MCA 15-30-2337(8) calls it household income); the reduction is a share of it, 5 percent at 12,000 and over (MCA 15-30-2340(4))",
+ "variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py: gross household income adds the untaxed part of Social Security to federal AGI, so all of it counts"
+ ]
+ },
+ "FL": {
+ "name": "Florida",
+ "mechanism": "No state income tax, and no state supplement for a person in her own home",
+ "evidence": [
+ "parameters/gov/states/household/state_income_tax_before_refundable_credits.yaml: no entry for Florida",
+ "variables/gov/states/fl/dcf/oss/fl_oss_eligible.py: Florida's optional state supplement requires a facility living arrangement, mapped from fl_oss_community_care_type, whose default is none",
+ "parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/income/limit/individual.yaml:44-45: Florida's optional aged Medicaid pathway ends at 88 percent of the poverty guideline (reported as a memo; health coverage is outside default net income)"
+ ]
+ }
+ },
+ "common_inputs": {
+ "monthly_rent": 800.0,
+ "bank_account_assets": 1500.0,
+ "has_heating_cooling_expense": true,
+ "takes_up_housing_assistance_if_eligible": false,
+ "living_arrangements_allow_for_food_preparation": true,
+ "medicare_quarters_of_coverage": 88
+ },
+ "net_income_tree": {
+ "household_net_income": [
+ [
+ "household_market_income",
+ 1
+ ],
+ [
+ "household_benefits",
+ 1
+ ],
+ [
+ "household_refundable_tax_credits",
+ 1
+ ],
+ [
+ "household_tax_before_refundable_credits",
+ -1
+ ],
+ [
+ "household_health_costs",
+ -1
+ ]
+ ],
+ "household_health_costs": [],
+ "household_tax_before_refundable_credits": [
+ [
+ "employee_payroll_tax",
+ 1
+ ],
+ [
+ "self_employment_tax",
+ 1
+ ],
+ [
+ "income_tax_before_refundable_credits",
+ 1
+ ],
+ [
+ "flat_tax",
+ 1
+ ],
+ [
+ "household_state_tax_before_refundable_credits",
+ 1
+ ],
+ [
+ "local_income_tax_before_refundable_credits",
+ 1
+ ],
+ [
+ "local_occupational_tax",
+ 1
+ ]
+ ],
+ "household_state_tax_before_refundable_credits": [
+ [
+ "state_income_tax_before_refundable_credits",
+ 1
+ ],
+ [
+ "state_use_tax",
+ 1
+ ]
+ ],
+ "household_refundable_tax_credits": [
+ [
+ "income_tax_refundable_credits",
+ 1
+ ],
+ [
+ "household_refundable_state_tax_credits",
+ 1
+ ]
+ ],
+ "household_refundable_state_tax_credits": [
+ [
+ "al_refundable_credits",
+ 1
+ ],
+ [
+ "ar_refundable_credits",
+ 1
+ ],
+ [
+ "az_refundable_credits",
+ 1
+ ],
+ [
+ "ca_refundable_credits",
+ 1
+ ],
+ [
+ "co_refundable_credits",
+ 1
+ ],
+ [
+ "ct_refundable_credits",
+ 1
+ ],
+ [
+ "dc_refundable_credits",
+ 1
+ ],
+ [
+ "de_refundable_credits",
+ 1
+ ],
+ [
+ "ga_refundable_credits",
+ 1
+ ],
+ [
+ "hi_refundable_credits",
+ 1
+ ],
+ [
+ "ia_refundable_credits",
+ 1
+ ],
+ [
+ "id_refundable_credits",
+ 1
+ ],
+ [
+ "il_refundable_credits",
+ 1
+ ],
+ [
+ "in_refundable_credits",
+ 1
+ ],
+ [
+ "ks_refundable_credits",
+ 1
+ ],
+ [
+ "ky_refundable_credits",
+ 1
+ ],
+ [
+ "la_refundable_credits",
+ 1
+ ],
+ [
+ "ma_refundable_credits",
+ 1
+ ],
+ [
+ "me_refundable_credits",
+ 1
+ ],
+ [
+ "md_refundable_credits",
+ 1
+ ],
+ [
+ "mi_refundable_credits",
+ 1
+ ],
+ [
+ "mn_refundable_credits",
+ 1
+ ],
+ [
+ "mo_refundable_credits",
+ 1
+ ],
+ [
+ "ms_refundable_credits",
+ 1
+ ],
+ [
+ "mt_refundable_credits",
+ 1
+ ],
+ [
+ "nd_refundable_credits",
+ 1
+ ],
+ [
+ "ne_refundable_credits",
+ 1
+ ],
+ [
+ "nh_refundable_credits",
+ 1
+ ],
+ [
+ "nm_refundable_credits",
+ 1
+ ],
+ [
+ "nj_refundable_credits",
+ 1
+ ],
+ [
+ "ny_refundable_credits",
+ 1
+ ],
+ [
+ "oh_refundable_credits",
+ 1
+ ],
+ [
+ "ok_refundable_credits",
+ 1
+ ],
+ [
+ "or_refundable_credits",
+ 1
+ ],
+ [
+ "pa_refundable_tax_credits",
+ 1
+ ],
+ [
+ "ri_refundable_credits",
+ 1
+ ],
+ [
+ "sc_refundable_credits",
+ 1
+ ],
+ [
+ "wa_refundable_credits",
+ 1
+ ],
+ [
+ "nyc_refundable_credits",
+ 1
+ ],
+ [
+ "ut_refundable_credits",
+ 1
+ ],
+ [
+ "va_refundable_credits",
+ 1
+ ],
+ [
+ "vt_refundable_credits",
+ 1
+ ],
+ [
+ "wi_refundable_credits",
+ 1
+ ],
+ [
+ "wv_refundable_credits",
+ 1
+ ]
+ ],
+ "household_benefits": [
+ [
+ "social_security",
+ 1
+ ],
+ [
+ "ssi",
+ 1
+ ],
+ [
+ "snap",
+ 1
+ ],
+ [
+ "wic",
+ 1
+ ],
+ [
+ "free_school_meals",
+ 1
+ ],
+ [
+ "reduced_price_school_meals",
+ 1
+ ],
+ [
+ "acp",
+ 1
+ ],
+ [
+ "ebb",
+ 1
+ ],
+ [
+ "tanf",
+ 1
+ ],
+ [
+ "high_efficiency_electric_home_rebate",
+ 1
+ ],
+ [
+ "residential_efficiency_electrification_rebate",
+ 1
+ ],
+ [
+ "unemployment_compensation",
+ 1
+ ],
+ [
+ "child_support_received",
+ 1
+ ],
+ [
+ "workers_compensation",
+ 1
+ ],
+ [
+ "educational_assistance",
+ 1
+ ],
+ [
+ "financial_assistance",
+ 1
+ ],
+ [
+ "survivor_benefits",
+ 1
+ ],
+ [
+ "household_head_start_benefits",
+ 1
+ ],
+ [
+ "basic_income",
+ 1
+ ],
+ [
+ "trump_dividend",
+ 1
+ ],
+ [
+ "housing_assistance",
+ 1
+ ],
+ [
+ "household_state_benefits",
+ 1
+ ],
+ [
+ "commodity_supplemental_food_program",
+ 1
+ ],
+ [
+ "household_health_benefits",
+ 1
+ ]
+ ],
+ "household_health_benefits": [],
+ "household_state_benefits": [
+ [
+ "de_ssp",
+ 1
+ ],
+ [
+ "de_child_care_subsidies",
+ 1
+ ],
+ [
+ "in_ssp",
+ 1
+ ],
+ [
+ "in_child_care_subsidies",
+ 1
+ ],
+ [
+ "ct_ssp",
+ 1
+ ],
+ [
+ "ga_ssp",
+ 1
+ ],
+ [
+ "me_ssp",
+ 1
+ ],
+ [
+ "mo_ssp",
+ 1
+ ],
+ [
+ "mo_child_care_subsidies",
+ 1
+ ],
+ [
+ "mt_child_care_subsidies",
+ 1
+ ],
+ [
+ "ma_state_supplement",
+ 1
+ ],
+ [
+ "md_paa",
+ 1
+ ],
+ [
+ "mi_ssp",
+ 1
+ ],
+ [
+ "mn_msa",
+ 1
+ ],
+ [
+ "co_state_supplement",
+ 1
+ ],
+ [
+ "co_oap",
+ 1
+ ],
+ [
+ "co_child_care_subsidies",
+ 1
+ ],
+ [
+ "ca_child_care_subsidies",
+ 1
+ ],
+ [
+ "ca_cvrp",
+ 1
+ ],
+ [
+ "ca_care",
+ 1
+ ],
+ [
+ "ca_fera",
+ 1
+ ],
+ [
+ "ca_la_ez_save",
+ 1
+ ],
+ [
+ "ca_riv_share_payment",
+ 1
+ ],
+ [
+ "ca_la_infant_supplement",
+ 1
+ ],
+ [
+ "ca_la_expectant_parent_payment",
+ 1
+ ],
+ [
+ "ca_capi",
+ 1
+ ],
+ [
+ "ca_state_supplement",
+ 1
+ ],
+ [
+ "al_ssp",
+ 1
+ ],
+ [
+ "al_child_care_subsidies",
+ 1
+ ],
+ [
+ "ak_ssp",
+ 1
+ ],
+ [
+ "az_child_care_subsidies",
+ 1
+ ],
+ [
+ "fl_oss",
+ 1
+ ],
+ [
+ "id_aabd",
+ 1
+ ],
+ [
+ "id_child_care_subsidies",
+ 1
+ ],
+ [
+ "ky_ssp",
+ 1
+ ],
+ [
+ "ga_child_care_subsidies",
+ 1
+ ],
+ [
+ "md_child_care_subsidies",
+ 1
+ ],
+ [
+ "ne_aabd",
+ 1
+ ],
+ [
+ "ne_child_care_subsidies",
+ 1
+ ],
+ [
+ "nv_child_care_subsidies",
+ 1
+ ],
+ [
+ "ok_child_care_subsidies",
+ 1
+ ],
+ [
+ "nm_ssi_state_supplement",
+ 1
+ ],
+ [
+ "sc_ssi_state_supplement",
+ 1
+ ],
+ [
+ "tx_ssi_state_supplement",
+ 1
+ ],
+ [
+ "va_child_care_subsidies",
+ 1
+ ],
+ [
+ "wv_child_care_subsidies",
+ 1
+ ],
+ [
+ "nc_child_care_subsidies",
+ 1
+ ],
+ [
+ "sc_child_care_subsidies",
+ 1
+ ],
+ [
+ "ma_eaedc",
+ 1
+ ],
+ [
+ "dc_ossp",
+ 1
+ ],
+ [
+ "dc_child_care_subsidies",
+ 1
+ ],
+ [
+ "ks_sspp",
+ 1
+ ],
+ [
+ "hi_oss",
+ 1
+ ],
+ [
+ "wa_ssp",
+ 1
+ ],
+ [
+ "wa_sfa",
+ 1
+ ],
+ [
+ "wa_rca",
+ 1
+ ],
+ [
+ "la_oss",
+ 1
+ ],
+ [
+ "ar_child_care_subsidies",
+ 1
+ ],
+ [
+ "oh_child_care_subsidies",
+ 1
+ ],
+ [
+ "wa_child_care_subsidies",
+ 1
+ ],
+ [
+ "nj_property_tax_relief",
+ 1
+ ],
+ [
+ "ny_child_care_subsidies",
+ 1
+ ]
+ ],
+ "household_market_income": [
+ [
+ "employment_income",
+ 1
+ ],
+ [
+ "self_employment_income",
+ 1
+ ],
+ [
+ "sstb_self_employment_income",
+ 1
+ ],
+ [
+ "partnership_s_corp_income",
+ 1
+ ],
+ [
+ "gi_cash_assistance",
+ 1
+ ],
+ [
+ "farm_operations_income",
+ 1
+ ],
+ [
+ "farm_rent_income",
+ 1
+ ],
+ [
+ "capital_gains",
+ 1
+ ],
+ [
+ "interest_income",
+ 1
+ ],
+ [
+ "rental_income",
+ 1
+ ],
+ [
+ "dividend_income",
+ 1
+ ],
+ [
+ "pension_income",
+ 1
+ ],
+ [
+ "debt_relief",
+ 1
+ ],
+ [
+ "illicit_income",
+ 1
+ ],
+ [
+ "retirement_distributions",
+ 1
+ ],
+ [
+ "miscellaneous_income",
+ 1
+ ],
+ [
+ "alimony_income",
+ 1
+ ],
+ [
+ "strike_benefits",
+ 1
+ ],
+ [
+ "ak_permanent_fund_dividend",
+ 1
+ ]
+ ]
+ },
+ "table_rows": [
+ "social_security",
+ "ssi",
+ "state_benefits",
+ "snap",
+ "csfp",
+ "market_income",
+ "federal_income_tax",
+ "state_income_tax",
+ "state_refundable_credits",
+ "other_taxes"
+ ],
+ "chart_rows": [
+ "social_security",
+ "ssi",
+ "state_benefits",
+ "snap",
+ "federal_income_tax",
+ "state_income_tax",
+ "state_refundable_credits"
+ ],
+ "results": [
+ {
+ "household": "A",
+ "label": "Household A",
+ "state": "CA",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94
+ },
+ "with_health": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94,
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 0.0,
+ "baseline_net_income": 18925.28,
+ "reform_net_income": 18925.28,
+ "reported_baseline_net_income": 18925.279296875,
+ "reported_reform_net_income": 18925.279296875,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 2879.28,
+ "reform": 2879.28,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3228.0,
+ "reform": 3228.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "ssi",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "ssi",
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 3228.0,
+ "reform": 3228.0,
+ "change": 0.0
+ },
+ {
+ "variable": "ca_state_supplement",
+ "path": [
+ "household_net_income",
+ "household_benefits",
+ "household_state_benefits"
+ ],
+ "sign": 1,
+ "category": "state_benefits",
+ "baseline": 2879.28,
+ "reform": 2879.28,
+ "change": 0.0
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ {
+ "variable": "state_use_tax",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "other_taxes",
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 0.0,
+ "baseline_net_income": 28161.76,
+ "reform_net_income": 28161.76,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 2879.28,
+ "reform": 2879.28,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3228.0,
+ "reform": 3228.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 9236.48,
+ "reform": 9236.48,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 9236.4794921875,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 6156.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 9236.4794921875,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 8208.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "A",
+ "label": "Household A",
+ "state": "MT",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 0.0,
+ "baseline_net_income": 17569.0,
+ "reform_net_income": 17569.0,
+ "reported_baseline_net_income": 17569.0,
+ "reported_reform_net_income": 17569.0,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "ssi",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "ssi",
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ {
+ "variable": "mt_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_refundable_tax_credits",
+ "household_refundable_state_tax_credits"
+ ],
+ "sign": 1,
+ "category": "state_refundable_credits",
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 0.0,
+ "baseline_net_income": 28368.23,
+ "reform_net_income": 28368.23,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 10799.23,
+ "reform": 10799.23,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 10799.228515625,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 8676.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 10799.228515625,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 10728.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "A",
+ "label": "Household A",
+ "state": "FL",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 0.0,
+ "baseline_net_income": 16419.0,
+ "reform_net_income": 16419.0,
+ "reported_baseline_net_income": 16419.0,
+ "reported_reform_net_income": 16419.0,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "ssi",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "ssi",
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 0.0,
+ "baseline_net_income": 25619.24,
+ "reform_net_income": 25619.24,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 3252.0,
+ "reform": 1200.0,
+ "change": -2052.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3600.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 9200.24,
+ "reform": 9200.24,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 9200.23828125,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 8676.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 9200.23828125,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 10728.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "social_security",
+ "ssi"
+ ],
+ "traced_nodes": 356,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 0.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_a_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_a_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_a_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_a_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_a_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "B",
+ "label": "Household B",
+ "state": "CA",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94
+ },
+ "with_health": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94,
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 396.72,
+ "baseline_net_income": 18925.28,
+ "reform_net_income": 19322.0,
+ "reported_baseline_net_income": 18925.279296875,
+ "reported_reform_net_income": 19322.0,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 1331.28,
+ "reform": 0.0,
+ "change": -1331.28
+ },
+ "snap": {
+ "baseline": 3228.0,
+ "reform": 2904.0,
+ "change": -324.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 3228.0,
+ "reform": 2904.0,
+ "change": -324.0
+ },
+ {
+ "variable": "ca_state_supplement",
+ "path": [
+ "household_net_income",
+ "household_benefits",
+ "household_state_benefits"
+ ],
+ "sign": 1,
+ "category": "state_benefits",
+ "baseline": 1331.28,
+ "reform": 0.0,
+ "change": -1331.28
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ {
+ "variable": "state_use_tax",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "other_taxes",
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 396.72,
+ "baseline_net_income": 28161.76,
+ "reform_net_income": 28558.48,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 1331.28,
+ "reform": 0.0,
+ "change": -1331.28
+ },
+ "snap": {
+ "baseline": 3228.0,
+ "reform": 2904.0,
+ "change": -324.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 9236.48,
+ "reform": 9236.48,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -1.0,
+ "reform": -1.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 9236.4794921875,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 10956.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 9236.4794921875,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 13008.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "ca_state_supplement",
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 5322,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "ca_state_supplement",
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 5322,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "B",
+ "label": "Household B",
+ "state": "MT",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 1845.0,
+ "baseline_net_income": 19117.0,
+ "reform_net_income": 20962.0,
+ "reported_baseline_net_income": 19117.0,
+ "reported_reform_net_income": 20962.0,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3393.0,
+ "change": -207.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 3600.0,
+ "reform": 3393.0,
+ "change": -207.0
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ {
+ "variable": "mt_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_refundable_tax_credits",
+ "household_refundable_state_tax_credits"
+ ],
+ "sign": 1,
+ "category": "state_refundable_credits",
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 1845.0,
+ "baseline_net_income": 24451.8,
+ "reform_net_income": 26296.8,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 3600.0,
+ "reform": 3393.0,
+ "change": -207.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 5334.8,
+ "reform": 5334.8,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 1150.0,
+ "reform": 1150.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 5334.80078125,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 13476.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 5334.80078125,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 15528.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 5387,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 5387,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "B",
+ "label": "Household B",
+ "state": "FL",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 1128.0,
+ "baseline_net_income": 17355.0,
+ "reform_net_income": 18483.0,
+ "reported_baseline_net_income": 17355.0,
+ "reported_reform_net_income": 18483.0,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 2988.0,
+ "reform": 2064.0,
+ "change": -924.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "snap",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "snap",
+ "baseline": 2988.0,
+ "reform": 2064.0,
+ "change": -924.0
+ },
+ {
+ "variable": "commodity_supplemental_food_program",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "csfp",
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": -2737.44,
+ "baseline_net_income": 26555.24,
+ "reform_net_income": 23817.8,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 2988.0,
+ "reform": 2064.0,
+ "change": -924.0
+ },
+ "csfp": {
+ "baseline": 651.0,
+ "reform": 651.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 9200.24,
+ "reform": 5334.8,
+ "change": -3865.44
+ },
+ "market_income": {
+ "baseline": 4800.0,
+ "reform": 4800.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 9200.23828125,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 1.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 13476.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ },
+ "reform": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 5334.80078125,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 15528.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 1.0,
+ "is_qmb_eligible@2026-02": 1.0,
+ "is_qmb_eligible@2026-03": 1.0,
+ "is_qmb_eligible@2026-04": 1.0,
+ "is_qmb_eligible@2026-05": 1.0,
+ "is_qmb_eligible@2026-06": 1.0,
+ "is_qmb_eligible@2026-07": 1.0,
+ "is_qmb_eligible@2026-08": 1.0,
+ "is_qmb_eligible@2026-09": 1.0,
+ "is_qmb_eligible@2026-10": 1.0,
+ "is_qmb_eligible@2026-11": 1.0,
+ "is_qmb_eligible@2026-12": 1.0,
+ "commodity_supplemental_food_program_eligible": 1.0,
+ "taxable_social_security": 0.0
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 5393,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "medicaid_cost",
+ "msp_cost",
+ "snap",
+ "social_security"
+ ],
+ "traced_nodes": 7654,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 4800.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_b_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_b_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_b_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_b_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_b_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "C",
+ "label": "Household C",
+ "state": "CA",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94
+ },
+ "with_health": {
+ "gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single": 1233.94,
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 1942.31,
+ "baseline_net_income": 38789.86,
+ "reform_net_income": 40732.17,
+ "reported_baseline_net_income": 38789.8671875,
+ "reported_reform_net_income": 40732.17578125,
+ "reported_gap": 0.0,
+ "max_aggregate_gap": 0.0015869140625,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": -21.06,
+ "reform": -21.06,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -3.0,
+ "reform": -3.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "income_tax_before_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "federal_income_tax",
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ {
+ "variable": "state_income_tax_before_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "state_income_tax",
+ "baseline": -21.06,
+ "reform": -21.06,
+ "change": 0.0
+ },
+ {
+ "variable": "state_use_tax",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "other_taxes",
+ "baseline": -3.0,
+ "reform": -3.0,
+ "change": 0.0
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 1942.31,
+ "baseline_net_income": 38789.86,
+ "reform_net_income": 40732.17,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": -21.06,
+ "reform": -21.06,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": -3.0,
+ "reform": -3.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 37356.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 5867.2998046875
+ },
+ "reform": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 39408.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 22024.80078125,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 6781.39990234375
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "social_security"
+ ],
+ "traced_nodes": 4938,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "social_security"
+ ],
+ "traced_nodes": 4938,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_ca_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_ca_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_ca_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_ca_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_ca": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 6
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "C",
+ "label": "Household C",
+ "state": "MT",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 1886.5,
+ "baseline_net_income": 38485.67,
+ "reform_net_income": 40372.17,
+ "reported_baseline_net_income": 38485.671875,
+ "reported_reform_net_income": 40372.17578125,
+ "reported_gap": 0.01,
+ "max_aggregate_gap": 0.0010738372802734375,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": -341.09,
+ "reform": -384.06,
+ "change": -42.97
+ },
+ "state_refundable_credits": {
+ "baseline": 12.84,
+ "reform": 0.0,
+ "change": -12.84
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "mt_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_refundable_tax_credits",
+ "household_refundable_state_tax_credits"
+ ],
+ "sign": 1,
+ "category": "state_refundable_credits",
+ "baseline": 12.84,
+ "reform": 0.0,
+ "change": -12.84
+ },
+ {
+ "variable": "income_tax_before_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "federal_income_tax",
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ {
+ "variable": "state_income_tax_before_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "state_income_tax",
+ "baseline": -341.09,
+ "reform": -384.06,
+ "change": -42.97
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 1886.5,
+ "baseline_net_income": 38485.67,
+ "reform_net_income": 40372.17,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": -341.09,
+ "reform": -384.06,
+ "change": -42.97
+ },
+ "state_refundable_credits": {
+ "baseline": 12.84,
+ "reform": 0.0,
+ "change": -12.84
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 39876.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 5867.2998046875
+ },
+ "reform": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 41928.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 11970.0,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 6781.39990234375
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "mt_refundable_credits",
+ "social_security",
+ "state_income_tax_before_refundable_credits"
+ ],
+ "traced_nodes": 4493,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "mt_refundable_credits",
+ "social_security",
+ "state_income_tax_before_refundable_credits"
+ ],
+ "traced_nodes": 4493,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_mt_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_mt_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_mt_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_mt_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_mt": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 30
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "household": "C",
+ "label": "Household C",
+ "state": "FL",
+ "baseline_social_security_annual": 8916.0,
+ "reform_social_security_annual": 10968.0,
+ "parameter_overrides": {
+ "default": {},
+ "with_health": {
+ "gov.simulation.include_health_benefits_in_net_income": true
+ }
+ },
+ "decomposition": {
+ "net_change": 1942.31,
+ "baseline_net_income": 38813.92,
+ "reform_net_income": 40756.23,
+ "reported_baseline_net_income": 38813.92578125,
+ "reported_reform_net_income": 40756.23046875,
+ "reported_gap": -0.01,
+ "max_aggregate_gap": 0.0018310546875,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ },
+ "components": [
+ {
+ "variable": "pension_income",
+ "path": [
+ "household_net_income",
+ "household_market_income"
+ ],
+ "sign": 1,
+ "category": "market_income",
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ {
+ "variable": "social_security",
+ "path": [
+ "household_net_income",
+ "household_benefits"
+ ],
+ "sign": 1,
+ "category": "social_security",
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ {
+ "variable": "income_tax_before_refundable_credits",
+ "path": [
+ "household_net_income",
+ "household_tax_before_refundable_credits"
+ ],
+ "sign": -1,
+ "category": "federal_income_tax",
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ }
+ ]
+ },
+ "with_health_benefits_in_net_income": {
+ "net_change": 1942.31,
+ "baseline_net_income": 38813.92,
+ "reform_net_income": 40756.23,
+ "categories": {
+ "social_security": {
+ "baseline": 8916.0,
+ "reform": 10968.0,
+ "change": 2052.0
+ },
+ "ssi": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "snap": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "csfp": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_benefits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "health_net": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "market_income": {
+ "baseline": 31200.0,
+ "reform": 31200.0,
+ "change": 0.0
+ },
+ "federal_income_tax": {
+ "baseline": -1302.08,
+ "reform": -1411.77,
+ "change": -109.69
+ },
+ "federal_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_income_tax": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "state_refundable_credits": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ },
+ "other_taxes": {
+ "baseline": 0.0,
+ "reform": 0.0,
+ "change": 0.0
+ }
+ }
+ },
+ "memo": {
+ "baseline": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 39876.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 5867.2998046875
+ },
+ "reform": {
+ "medicaid_cost": 0.0,
+ "msp_cost": 0.0,
+ "is_medicaid_eligible": 0.0,
+ "medicaid_optional_senior_or_disabled_countable_income": 41928.0,
+ "medicaid_optional_senior_or_disabled_income_limit": 14044.7998046875,
+ "is_qmb_eligible@2026-01": 0.0,
+ "is_qmb_eligible@2026-02": 0.0,
+ "is_qmb_eligible@2026-03": 0.0,
+ "is_qmb_eligible@2026-04": 0.0,
+ "is_qmb_eligible@2026-05": 0.0,
+ "is_qmb_eligible@2026-06": 0.0,
+ "is_qmb_eligible@2026-07": 0.0,
+ "is_qmb_eligible@2026-08": 0.0,
+ "is_qmb_eligible@2026-09": 0.0,
+ "is_qmb_eligible@2026-10": 0.0,
+ "is_qmb_eligible@2026-11": 0.0,
+ "is_qmb_eligible@2026-12": 0.0,
+ "commodity_supplemental_food_program_eligible": 0.0,
+ "taxable_social_security": 6781.39990234375
+ }
+ },
+ "float32_guard": {
+ "default": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "social_security"
+ ],
+ "traced_nodes": 5222,
+ "small_changes": [],
+ "uncaused_changes": []
+ },
+ "with_health": {
+ "traced_leaves": [
+ "income_tax_before_refundable_credits",
+ "social_security"
+ ],
+ "traced_nodes": 5222,
+ "small_changes": [],
+ "uncaused_changes": []
+ }
+ },
+ "situations": {
+ "baseline": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 8916.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ },
+ "reform": {
+ "people": {
+ "worker": {
+ "age": {
+ "2026": 68
+ },
+ "social_security_retirement": {
+ "2026": 10968.0
+ },
+ "social_security_disability": {
+ "2026": 0.0
+ },
+ "social_security_survivors": {
+ "2026": 0.0
+ },
+ "social_security_dependents": {
+ "2026": 0.0
+ },
+ "employment_income": {
+ "2026": 0.0
+ },
+ "taxable_private_pension_income": {
+ "2026": 31200.0
+ },
+ "interest_income": {
+ "2026": 0.0
+ },
+ "bank_account_assets": {
+ "2026": 1500.0
+ },
+ "pre_subsidy_rent": {
+ "2026": 9600.0
+ },
+ "medicare_quarters_of_coverage": {
+ "2026": 88
+ }
+ }
+ },
+ "tax_units": {
+ "household_c_fl_tax_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "spm_units": {
+ "household_c_fl_spm_unit_1": {
+ "members": [
+ "worker"
+ ],
+ "has_heating_cooling_expense": {
+ "2026": true
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ "2026": false
+ }
+ }
+ },
+ "marital_units": {
+ "household_c_fl_marital_unit_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "families": {
+ "household_c_fl_family_1": {
+ "members": [
+ "worker"
+ ]
+ }
+ },
+ "households": {
+ "household_c_fl": {
+ "members": [
+ "worker"
+ ],
+ "state_fips": {
+ "2026": 12
+ },
+ "living_arrangements_allow_for_food_preparation": {
+ "2026": true
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "medicaid_valuation": {
+ "text": "Medicaid is valued as policyengine-us 2.18.0 values it for one household: the state's 2023 Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment, an average over enrollees of all ages, not uprated to 2026. It is not a same-year average and not specific to aged enrollees, so it could overstate or understate what covering an aged enrollee costs. It values coverage at average program cost; it is not a cash loss. The years do not match: with the release's 2023 enrollment entry, the spending's own year, it would be $7,034 in Florida (not $9,200), $8,751 in California (not $9,236) and $7,223 in Montana (not $10,799).",
+ "per_enrollee": {
+ "CA": {
+ "spending": 124063730563.0,
+ "spending_entry": "2023-01-01",
+ "enrollment": 13431928.0,
+ "enrollment_entry": "2024-10-01",
+ "release": 9236.479719292718,
+ "same_year_enrollment": 14176618.0,
+ "same_year": 8751.29248478022
+ },
+ "MT": {
+ "spending": 2363497647.0,
+ "spending_entry": "2023-01-01",
+ "enrollment": 218858.0,
+ "enrollment_entry": "2024-10-01",
+ "release": 10799.228938398413,
+ "same_year_enrollment": 327239.0,
+ "same_year": 7222.54268898267
+ },
+ "FL": {
+ "spending": 34641023901.0,
+ "spending_entry": "2023-01-01",
+ "enrollment": 3765231.0,
+ "enrollment_entry": "2024-10-01",
+ "release": 9200.238684160415,
+ "same_year_enrollment": 4924826.0,
+ "same_year": 7033.958946163783
+ }
+ },
+ "files": [
+ "policyengine_us/parameters/calibration/gov/hhs/medicaid/totals/spending.yaml",
+ "policyengine_us/parameters/calibration/gov/hhs/medicaid/totals/enrollment.yaml"
+ ]
+ },
+ "medicaid_explanations": {
+ "B-FL": "In Florida, the increase raises her countable income for the optional aged Medicaid pathway from $13,476 to $15,528, past the limit of 88% of the poverty guideline ($14,045), so Medicaid ends (PolicyEngine-US 2.18.0, is_optional_senior_or_disabled_income_eligible.py:23-32; parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/income/limit/individual.yaml:44-45). She is QMB-eligible in every month of both runs, and PolicyEngine-US counts the Medicare Savings Program only once full Medicaid ends (msp_cost.py:28), so that benefit appears in the reform."
+ },
+ "float32_guard": {
+ "comparisons": 18,
+ "changed_leaves_traced": 44,
+ "traced_nodes": 65907,
+ "small_changes": [],
+ "uncaused_changes": 0,
+ "summary": "Every changed leaf of the 18 comparisons (nine household-state pairs, with and without health coverage; 44 leaves) was traced through PolicyEngine-US's own calculation (65,907 traced variable-periods). No variable changed by a cent or more (from a cent less one float32 step at its size, never under half a cent) while every variable it read held within float noise (a cent, or four float32 steps at the read's size where that is more), the signature of a float32 step at a bracket edge. No leaf changed by a nonzero amount under $2."
+ },
+ "invariants_checked": [
+ "every household-state pair, with and without health coverage: the leaf changes sum exactly (in cents) to the net change, and the categories to the same total",
+ "the Social Security component equals the Microcosm benefit change",
+ "when Social Security rises, SSI, SNAP, the Commodity Supplemental Food Program, state benefits and federal and state refundable tax credits never rise, and federal and state income taxes never fall",
+ "PolicyEngine-US's own household_net_income change is within one cent of the definition's sum",
+ "float32 guard: every changed leaf traced through PolicyEngine-US's calculation; no variable changed by a cent or more (allowing one float32 step of rounding) while every variable it read held within float noise",
+ "Medicaid at cost, wherever she is enrolled (the memo and the with-health medicaid_cost leaf), equals the state's spending over enrollment that the valuation text describes"
+ ],
+ "caveats": [
+ "Illustrative households, not survey data: one worker, chosen so the minimum binds, in three living situations.",
+ "The bridge passes Microcosm's Social Security amounts to PolicyEngine-US as inputs; it does not run over the projected population and applies no behavioral response.",
+ "The reform is a minimum benefit set at 73% of the aged poverty threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option 2): exercise 4's headline option, the standard price-indexed minimum with its printed 12.81 percent uniform cut for new entitlees. The worker is on the minimum, so the cut does not reach her; a worker above the minimum would lose 12.81 percent.",
+ "The baseline is current-law scheduled benefits. Against option 1 (reduced current law, the Report's own benchmark) her gain would be larger; that PIA is in the JSON as a memo, not run through PolicyEngine-US.",
+ "Net income is PolicyEngine-US's household_net_income, which by default excludes health coverage (Medicaid at cost, Medicare Savings Programs); the with-health sensitivity and the memo lines report it. Medicaid is valued as policyengine-us 2.18.0 values it for one household: the state's 2023 Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment, an average over enrollees of all ages, not uprated to 2026. It is not a same-year average and not specific to aged enrollees, so it could overstate or understate what covering an aged enrollee costs. It values coverage at average program cost; it is not a cash loss. The years do not match: with the release's 2023 enrollment entry, the spending's own year, it would be $7,034 in Florida (not $9,200), $8,751 in California (not $9,236) and $7,223 in Montana (not $10,799).",
+ "SNAP for October-December 2026 uses USDA's fiscal-2027 maximum allotment ($306 a month for one person) and standard deduction ($217) as policyengine-us 2.18.0 encodes them (gov/usda/snap/max_allotment.yaml:33, income/deductions/standard.yaml:17). The state standard utility allowances have no fiscal-2027 entry there, so those months use the fiscal-2026 amounts (California $663, Florida $430 and Montana $799 a month; income/deductions/utility/standard/main.yaml:112, 179 and 387).",
+ "In policyengine-us 2.18.0 SNAP counts California's SSI supplement as unearned income (gov/usda/snap/income/sources/unearned_spm_unit.yaml:13), so losing the supplement lowers the income SNAP counts, and SNAP falls by less.",
+ "Take-up is PolicyEngine-US's default: full for SSI, SNAP and Medicaid (takes_up_ssi_if_eligible.py:9, takes_up_snap_if_eligible.py:9, takes_up_medicaid_if_eligible.py:9). The Commodity Supplemental Food Program has no take-up input: every eligible person gets USDA's cost per caseload slot, $651 in 2026 (commodity_supplemental_food_program.py:10-11, gov/usda/csfp/amount.yaml:11), though the program is caseload-limited and serves far fewer people than are eligible. Housing assistance is switched off because vouchers are rationed, and the household can prepare food at home.",
+ "California's 2026 aged or disabled payment standard is set to the published $1,233.94 a month (California Department of Social Services, SSI Total Monthly Payment Amounts 2026, Rev. 1/26); policyengine-us 2.18.0 has no 2026 entry and would hold $1,206.94 from 2025-01-01.",
+ "COLAs for 2023-2025 are derived from policyengine-us 2.18.0's third-quarter CPI-W averages under 42 USC 415(i)(1)(D); the derived 2022 COLA equals the committed SSA history's.",
+ "Household C's pension is $31,200, clear of every bracket and eligibility edge on her path. At a round $30,000, California AGI sat on the $30,000 edge of the use-tax table and float32 noise moved the use tax by $1; the float32 guard now refuses such a run."
+ ]
+}
diff --git a/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.md b/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.md
new file mode 100644
index 000000000..f5838db55
--- /dev/null
+++ b/docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.md
@@ -0,0 +1,221 @@
+# The minimum benefit after taxes and transfers: sample households
+
+**Illustrative household, not survey data.** Three households built around one illustrative worker, run through PolicyEngine-US for payment year 2026. Amounts are annual 2026 dollars. Taxes and costs enter as negative contributions, so each column sums to net income (PolicyEngine-US's `household_net_income`, which by default excludes health coverage). Each amount is rounded to the dollar on its own, with half-dollars rounded up in magnitude, so a total can differ by a dollar from the sum of its rounded parts.
+
+- Microcosm Dynamics commit `a915b96e293cc141331c8b02196bbbcbdc8da44d`
+- PolicyEngine-US 2.18.0 from PyPI (wheel SHA-256 `28e32bc1339e8ffc1ed676ac1c9ecd468d191685608039643915f63e1357085f`), with policyengine-core 3.32.11. All 17,551 installed `policyengine_us/` files match the wheel's RECORD.
+- Reform: a minimum benefit set at 73% of the aged poverty threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option 2), compared with current-law scheduled benefits
+- Social Security: 8,916 a year under current law, 10,968 under the reform
+- Parameter update (CA): `gov.states.ca.cdss.state_supplement.payment_standard.aged_or_disabled.amount.single` is set to 1,233.94 dollars a month for 2026, from California Department of Social Services, "SSI Total Monthly Payment Amounts 2026" (Rev. 1/26, effective 2026-01-01; https://cdss.ca.gov/Portals/13/SHD/ParaRegIndex/SSI%20Monthly%20Payment%20Amounts%202026.pdf, retrieved 2026-09-30). PolicyEngine-US 2.18.0 has no 2026 entry and would hold 1,206.94 from 2025-01-01.
+
+## Summary
+
+| Household | State | Social Security | Net income | Share of the benefit increase kept | Net income with health coverage |
+|---|---|---:|---:|---:|---:|
+| A | CA | +2,052 | 0 | 0% | 0 |
+| A | MT | +2,052 | 0 | 0% | 0 |
+| A | FL | +2,052 | 0 | 0% | 0 |
+| B | CA | +2,052 | +397 | 19% | +397 |
+| B | MT | +2,052 | +1,845 | 90% | +1,845 |
+| B | FL | +2,052 | +1,128 | 55% | −2,737 |
+| C | CA | +2,052 | +1,942 | 95% | +1,942 |
+| C | MT | +2,052 | +1,887 | 92% | +1,887 |
+| C | FL | +2,052 | +1,942 | 95% | +1,942 |
+
+Medicaid is valued as policyengine-us 2.18.0 values it for one household: the state's 2023 Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment, an average over enrollees of all ages, not uprated to 2026. It is not a same-year average and not specific to aged enrollees, so it could overstate or understate what covering an aged enrollee costs. It values coverage at average program cost; it is not a cash loss. The years do not match: with the release's 2023 enrollment entry, the spending's own year, it would be $7,034 in Florida (not $9,200), $8,751 in California (not $9,236) and $7,223 in Montana (not $10,799).
+
+## Household A
+
+The worker alone: no income besides Social Security, $1,500 in the bank (under the SSI resource limit), renting for $800 a month.
+
+### Household A, California
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 3,252 | 1,200 | −2,052 |
+| State benefits (incl. SSI supplements) | 2,879 | 2,879 | 0 |
+| SNAP | 3,228 | 3,228 | 0 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 0 | 0 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | −1 | −1 | 0 |
+| **Net income** | **18,925** | **18,925** | **0** |
+
+Memo (outside default net income): Medicaid at cost 9,236 → 9,236; Medicare Savings Program 0 → 0 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by 0.
+
+### Household A, Montana
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 3,252 | 1,200 | −2,052 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 3,600 | 3,600 | 0 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 0 | 0 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 1,150 | 1,150 | 0 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **17,569** | **17,569** | **0** |
+
+Memo (outside default net income): Medicaid at cost 10,799 → 10,799; Medicare Savings Program 0 → 0 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by 0.
+
+### Household A, Florida
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 3,252 | 1,200 | −2,052 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 3,600 | 3,600 | 0 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 0 | 0 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **16,419** | **16,419** | **0** |
+
+Memo (outside default net income): Medicaid at cost 9,200 → 9,200; Medicare Savings Program 0 → 0 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by 0.
+
+## Household B
+
+The same worker with a $4,800-a-year ($400-a-month) private pension, which puts her countable income above the federal SSI benefit rate.
+
+### Household B, California
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 1,331 | 0 | −1,331 |
+| SNAP | 3,228 | 2,904 | −324 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 4,800 | 4,800 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | −1 | −1 | 0 |
+| **Net income** | **18,925** | **19,322** | **+397** |
+
+Memo (outside default net income): Medicaid at cost 9,236 → 9,236; Medicare Savings Program 0 → 0 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by +397.
+
+### Household B, Montana
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 3,600 | 3,393 | −207 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 4,800 | 4,800 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 1,150 | 1,150 | 0 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **19,117** | **20,962** | **+1,845** |
+
+Memo (outside default net income): Medicaid at cost 0 → 0; Medicare Savings Program 5,335 → 5,335 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by +1,845.
+
+### Household B, Florida
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 2,988 | 2,064 | −924 |
+| Commodity Supplemental Food Program | 651 | 651 | 0 |
+| Market income | 4,800 | 4,800 | 0 |
+| Federal income tax before refundable credits | 0 | 0 | 0 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **17,355** | **18,483** | **+1,128** |
+
+Memo (outside default net income): Medicaid at cost 9,200 → 0; Medicare Savings Program 0 → 5,335 (QMB-eligible in every month of both runs; counted only without full Medicaid). Net income with health coverage counted changes by −2,737.
+
+## Household C
+
+The same worker with a $31,200-a-year ($2,600-a-month) private pension, enough for part of her Social Security to be taxable.
+
+### Household C, California
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 0 | 0 | 0 |
+| Commodity Supplemental Food Program | 0 | 0 | 0 |
+| Market income | 31,200 | 31,200 | 0 |
+| Federal income tax before refundable credits | −1,302 | −1,412 | −110 |
+| State income tax before refundable credits | −21 | −21 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | −3 | −3 | 0 |
+| **Net income** | **38,790** | **40,732** | **+1,942** |
+
+Memo (outside default net income): Medicaid at cost 0 → 0; Medicare Savings Program 0 → 0 (not QMB-eligible in any month of either run; counted only without full Medicaid). Net income with health coverage counted changes by +1,942.
+
+### Household C, Montana
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 0 | 0 | 0 |
+| Commodity Supplemental Food Program | 0 | 0 | 0 |
+| Market income | 31,200 | 31,200 | 0 |
+| Federal income tax before refundable credits | −1,302 | −1,412 | −110 |
+| State income tax before refundable credits | −341 | −384 | −43 |
+| State refundable tax credits | 13 | 0 | −13 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **38,486** | **40,372** | **+1,887** |
+
+Memo (outside default net income): Medicaid at cost 0 → 0; Medicare Savings Program 0 → 0 (not QMB-eligible in any month of either run; counted only without full Medicaid). Net income with health coverage counted changes by +1,887.
+
+### Household C, Florida
+
+| Component | Baseline | Reform | Change |
+|---|---:|---:|---:|
+| Social Security | 8,916 | 10,968 | +2,052 |
+| SSI (federal) | 0 | 0 | 0 |
+| State benefits (incl. SSI supplements) | 0 | 0 | 0 |
+| SNAP | 0 | 0 | 0 |
+| Commodity Supplemental Food Program | 0 | 0 | 0 |
+| Market income | 31,200 | 31,200 | 0 |
+| Federal income tax before refundable credits | −1,302 | −1,412 | −110 |
+| State income tax before refundable credits | 0 | 0 | 0 |
+| State refundable tax credits | 0 | 0 | 0 |
+| Other taxes (payroll, use, local) | 0 | 0 | 0 |
+| **Net income** | **38,814** | **40,756** | **+1,942** |
+
+Memo (outside default net income): Medicaid at cost 0 → 0; Medicare Savings Program 0 → 0 (not QMB-eligible in any month of either run; counted only without full Medicaid). Net income with health coverage counted changes by +1,942.
+
+## Health coverage
+
+- In Florida, the increase raises her countable income for the optional aged Medicaid pathway from $13,476 to $15,528, past the limit of 88% of the poverty guideline ($14,045), so Medicaid ends (PolicyEngine-US 2.18.0, is_optional_senior_or_disabled_income_eligible.py:23-32; parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/income/limit/individual.yaml:44-45). She is QMB-eligible in every month of both runs, and PolicyEngine-US counts the Medicare Savings Program only once full Medicaid ends (msp_cost.py:28), so that benefit appears in the reform.
+
+## Float32 guard
+
+Every changed leaf of the 18 comparisons (nine household-state pairs, with and without health coverage; 44 leaves) was traced through PolicyEngine-US's own calculation (65,907 traced variable-periods). No variable changed by a cent or more (from a cent less one float32 step at its size, never under half a cent) while every variable it read held within float noise (a cent, or four float32 steps at the read's size where that is more), the signature of a float32 step at a bracket edge. No leaf changed by a nonzero amount under $2.
+
+## Notes
+
+- Illustrative households, not survey data: one worker, chosen so the minimum binds, in three living situations.
+- The bridge passes Microcosm's Social Security amounts to PolicyEngine-US as inputs; it does not run over the projected population and applies no behavioral response.
+- The reform is a minimum benefit set at 73% of the aged poverty threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option 2): exercise 4's headline option, the standard price-indexed minimum with its printed 12.81 percent uniform cut for new entitlees. The worker is on the minimum, so the cut does not reach her; a worker above the minimum would lose 12.81 percent.
+- The baseline is current-law scheduled benefits. Against option 1 (reduced current law, the Report's own benchmark) her gain would be larger; that PIA is in the JSON as a memo, not run through PolicyEngine-US.
+- Net income is PolicyEngine-US's household_net_income, which by default excludes health coverage (Medicaid at cost, Medicare Savings Programs); the with-health sensitivity and the memo lines report it. Medicaid is valued as policyengine-us 2.18.0 values it for one household: the state's 2023 Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment, an average over enrollees of all ages, not uprated to 2026. It is not a same-year average and not specific to aged enrollees, so it could overstate or understate what covering an aged enrollee costs. It values coverage at average program cost; it is not a cash loss. The years do not match: with the release's 2023 enrollment entry, the spending's own year, it would be $7,034 in Florida (not $9,200), $8,751 in California (not $9,236) and $7,223 in Montana (not $10,799).
+- SNAP for October-December 2026 uses USDA's fiscal-2027 maximum allotment ($306 a month for one person) and standard deduction ($217) as policyengine-us 2.18.0 encodes them (gov/usda/snap/max_allotment.yaml:33, income/deductions/standard.yaml:17). The state standard utility allowances have no fiscal-2027 entry there, so those months use the fiscal-2026 amounts (California $663, Florida $430 and Montana $799 a month; income/deductions/utility/standard/main.yaml:112, 179 and 387).
+- In policyengine-us 2.18.0 SNAP counts California's SSI supplement as unearned income (gov/usda/snap/income/sources/unearned_spm_unit.yaml:13), so losing the supplement lowers the income SNAP counts, and SNAP falls by less.
+- Take-up is PolicyEngine-US's default: full for SSI, SNAP and Medicaid (takes_up_ssi_if_eligible.py:9, takes_up_snap_if_eligible.py:9, takes_up_medicaid_if_eligible.py:9). The Commodity Supplemental Food Program has no take-up input: every eligible person gets USDA's cost per caseload slot, $651 in 2026 (commodity_supplemental_food_program.py:10-11, gov/usda/csfp/amount.yaml:11), though the program is caseload-limited and serves far fewer people than are eligible. Housing assistance is switched off because vouchers are rationed, and the household can prepare food at home.
+- California's 2026 aged or disabled payment standard is set to the published $1,233.94 a month (California Department of Social Services, SSI Total Monthly Payment Amounts 2026, Rev. 1/26); policyengine-us 2.18.0 has no 2026 entry and would hold $1,206.94 from 2025-01-01.
+- COLAs for 2023-2025 are derived from policyengine-us 2.18.0's third-quarter CPI-W averages under 42 USC 415(i)(1)(D); the derived 2022 COLA equals the committed SSA history's.
+- Household C's pension is $31,200, clear of every bracket and eligibility edge on her path. At a round $30,000, California AGI sat on the $30,000 edge of the use-tax table and float32 noise moved the use tax by $1; the float32 guard now refuses such a run.
diff --git a/docs/design/pe_us_bridge.md b/docs/design/pe_us_bridge.md
new file mode 100644
index 000000000..1ef362831
--- /dev/null
+++ b/docs/design/pe_us_bridge.md
@@ -0,0 +1,833 @@
+# Bridge to PolicyEngine-US
+
+Microcosm's four DYNASIM3 comparisons report pre-tax benefit changes, and one
+pre-tax poverty measure with a static SSI response. This bridge shows what a
+benefit change becomes after federal and state taxes and means-tested
+transfers. It passes Microcosm's Social Security amounts to PolicyEngine-US as
+household inputs, runs the tax-benefit model in a separate interpreter, and
+splits the change in PolicyEngine-US's `household_net_income` into components
+that sum exactly to it.
+
+The first use is exercise 4's headline minimum benefit on three illustrative
+households in three states: a minimum benefit set at 73% of the aged poverty
+threshold for 22 years of work (Favreault, Mermin and Steuerle 2006, option
+2). **These are illustrative households, not survey data.**
+
+Code: `src/populace_dynamics/bridge/policyengine_us.py` (pure Python; no
+`policyengine_us` import) and
+`scripts/pe_us_minimum_benefit_sample_households.py`. Outputs:
+`docs/analysis/pe_us_bridge_20260930/`.
+
+## What the bridge does and does not do
+
+It does:
+
+- **Build situations.** `BridgePerson` and `BridgeHousehold` hold age, Social
+ Security by type, earnings, pension and interest income, liquid assets,
+ rent, and tax-unit, SPM-unit, marital-unit, family and household
+ membership. They refuse negative or non-finite amounts, a bare string
+ where a group of ids belongs, and any unit structure that does not place
+ every person in exactly one unit of each kind. `to_situation` writes a
+ PolicyEngine-US situation, with the state given as its FIPS code.
+- **Check the PolicyEngine-US source.** `inspect_installation` asks the
+ interpreter which `policyengine-us` it imports and where it came from
+ (package metadata only). `check_published_source` accepts two sources:
+ - **an install by name.** There is no `direct_url.json`. Metadata cannot
+ tell a package index from a local wheel found with `--find-links`, so
+ the runner hashes every installed `policyengine_us/` file against the
+ RECORD (`verify_record`). That catches changed, missing and added files;
+ policyengine-core loads every parameter YAML and variable module on
+ disk. The runner can also require a pinned `package_record_digest`,
+ which ties the install to one published wheel. The script pins 2.18.0's.
+ - **a git checkout imported in place.** Some branch of `origin` must
+ contain its `HEAD`, asked of `origin` now (`git ls-remote`), not of
+ local remote-tracking refs that can outlive a deleted branch. It must
+ have no modified tracked file and no untracked package file, ignored
+ ones included. `origin` must be a GitHub repository.
+
+ Everything else is refused, including an unreachable `origin` and a VCS,
+ local-directory or direct-URL archive install. `run_policyengine_us` and
+ `trace_policyengine_us` run the checks first.
+- **Run PolicyEngine-US out of process.** `run_policyengine_us` sends the
+ situations as JSON to the interpreter named by
+ `POPULACE_DYNAMICS_PE_US_PYTHON` (default
+ `~/.venvs/policyengine-us-2.18.0/bin/python`), the subprocess discipline of
+ `scripts/build_aux_benefit_examples.py`.
+ - **One simulation per case.** Some 2.18.0 formulas aggregate over a
+ whole simulation's population (Medicaid's state-average cost index,
+ `medicaid_slcsp_state_average_cost_index.py:14-29`), so households
+ batched into one simulation would not be independent.
+ - **Shared systems.** Cases with the same parameter overrides share one
+ reformed tax-benefit system, built from a throwaway simulation of the
+ first such case and passed as `tax_benefit_system` (`spm.py:830-834`).
+ Two oracle tests check that a case run with others equals the case run
+ alone: one for a case with no overrides, and one for household B in
+ California with the payment-standard override, whose shared system was
+ built from household A's simulation.
+ - **Tree and state.** The child reads the component tree of
+ `household_net_income` from the model and returns every node's
+ household value. It also returns the state PolicyEngine-US derives from
+ the FIPS code, and the bridge refuses a mismatch.
+- **Decompose.** `decompose` checks every aggregate against the signed sum of
+ its parts (within $0.50), and refuses any value that is not finite. It
+ checks the root's parts against the definition the bridge was read
+ against, and refuses drift. It then rounds each leaf to cents, so the leaf
+ changes sum exactly to the net change. PolicyEngine-US's own float32
+ `household_net_income` is kept as a check. In every run here its change
+ agrees with the definition's sum within a cent.
+- **Guard against float32 steps.** `trace_policyengine_us` reruns cases with
+ PolicyEngine-US's tracer on (`Simulation(..., trace=True)`) and returns
+ every variable the calculation read. Nodes of a branch simulation (2.18.0
+ computes federal tax in `itemizing` and `not_itemizing` branches) are
+ keyed apart from the main calculation.
+ - **What it reports.** `uncaused_changes` walks each changed component's
+ calculation. It reports any variable that changed by a cent or more
+ although no variable it read changed by more than float noise.
+ - **A cent in float32.** Stored in float32, an exact one-cent step can
+ measure a little under a cent (3.00 to 3.01 measures 0.0099999905). So
+ a variable's own change counts from a cent less one float32 step at its
+ size, and never from less than half a cent (`_counts_as_change`,
+ `policyengine_us.py:1535-1558`).
+ - **The tradeoff.** From $32,768 to $131,072 a cent is one or two
+ float32 steps, so float32 cannot tell a one-cent step from one or two
+ steps of noise. The guard counts both, and errs toward a false alarm.
+ The guarantee covers a stored value: a value computed from larger
+ operands carries their rounding, so a one-cent step in it can still
+ measure less. For example, when a near $35,760 moves by a cent,
+ x = a − 5,760 moves by $0.0078125, two float32 steps of a, and is not
+ counted.
+ - **Noise.** Float noise is a cent or four float32 steps at the read's
+ size, whichever is larger. Above $131,072 one float32 step is more than
+ a cent.
+ - **What a report means.** A genuine change always has a changed input
+ beneath it, here the Social Security amount. A reported variable is
+ therefore a step at a bracket or eligibility edge taken on float32
+ noise, or a false alarm: a read with no numeric value never counts as
+ a cause, so a variable whose only changed read is not numeric is
+ reported (`uncaused_changes`, `policyengine_us.py:1602-1604`). Either
+ way the script refuses to write.
+ - **What it cannot see.** The rule is local, so it cannot see a noise
+ step in a variable that also read a genuinely changed input.
+ - **Small changes.** `small_changes` lists the components that changed by
+ a nonzero amount under $2.
+
+It does not:
+
+- compute any Social Security amount. The caller passes them; here they come
+ from the Track M rule code and the Microcosm oracle.
+- run over the projected population. It runs one household at a time, and
+ applies no behavioral, take-up or claiming response to the reform.
+- add `policyengine-us` to this repository's dependencies, or edit
+ PolicyEngine-US.
+
+## The PolicyEngine-US release
+
+Every result here comes from **policyengine-us 2.18.0 from PyPI**, uploaded
+2026-09-30, with policyengine-core 3.32.11. Provenance:
+
+- **Wheel.** `policyengine_us-2.18.0-py3-none-any.whl`, SHA-256
+ `28e32bc1339e8ffc1ed676ac1c9ecd468d191685608039643915f63e1357085f` (PyPI's
+ digest; a downloaded copy matched it).
+- **Installed files.** All 17,551 `policyengine_us/` files in the virtual
+ environment match their RECORD hashes. The sorted RECORD lines for those
+ files have SHA-256 `9f9f0908…`, the same as the wheel's RECORD. The
+ installer adds its own RECORD lines, so the whole-file hashes differ.
+- **Refusal.** The script refuses to run on any other version or source.
+
+The earlier draft ran on a local checkout at `e4363903f3` (1.822.5). That
+revision is not on GitHub, and `check_published_source` now refuses it.
+
+**What changed in 2.x that matters here.** The bridge ran on 2.18.0 with one
+change to the runner: under `gov.hud.abolition`, `household_benefits` now
+drops `housing_assistance` (`household_benefits.py:11-17`), where 1.822.5
+dropped `spm_unit_capped_housing_subsidy`. Holding the inputs fixed, two
+changes in the release move the results:
+
+- **SNAP counts California's SSI supplement as unearned income**
+ (`parameters/gov/usda/snap/income/sources/unearned_spm_unit.yaml:13`).
+ The 1.822.5 checkout's SNAP income-source lists
+ (`parameters/gov/usda/snap/income/sources/`) name no state supplement.
+ - **Household B in California.** Its change moves (below).
+ - **Household A in California.** Its SNAP level falls from $3,607.57 to
+ $3,228, while its change stays zero.
+- **SNAP's fiscal-2027 amounts are encoded.** The maximum allotment is $306
+ for one person (`max_allotment.yaml:33`) and the standard deduction $217
+ (`income/deductions/standard.yaml:17`), both from October 2026.
+ - **Levels.** These move SNAP by under $8 a year, for example household
+ A in Florida from $3,607.57 to $3,600.
+ - **Changes.** The SNAP changes outside California are as they were
+ ($207 in Montana, $924 in Florida).
+
+Household C's results also differ from the first draft's because her pension
+moved from $30,000 to $31,200 (below).
+
+## What `household_net_income` includes and excludes
+
+All citations are to policyengine-us 2.18.0, under `policyengine_us/`.
+
+- **Definition.** `household_net_income` adds `household_market_income`,
+ `household_benefits` and `household_refundable_tax_credits`. It subtracts
+ `household_tax_before_refundable_credits` and `household_health_costs`
+ (`variables/household/income/household/household_net_income.py:10-18`).
+- **Market income.** `gov.household.market_income_sources` includes, among
+ others, wages, self-employment, pensions, interest, dividends and
+ retirement distributions
+ (`parameters/gov/household/market_income_sources.yaml`).
+- **Benefits.** `household_benefits` sums the 24 variables of
+ `gov.household.household_benefits` (`parameters/gov/household/
+ household_benefits.yaml:33-59`, in force from 2024):
+ - cash, food and income-replacement benefits: `social_security`, `ssi`,
+ `snap`, `wic`, `free_school_meals`, `reduced_price_school_meals`,
+ `tanf`, `commodity_supplemental_food_program`,
+ `unemployment_compensation`;
+ - other transfers and income: `acp`, `ebb`,
+ `high_efficiency_electric_home_rebate`,
+ `residential_efficiency_electrification_rebate`,
+ `child_support_received`, `workers_compensation`,
+ `educational_assistance`, `financial_assistance`, `survivor_benefits`,
+ `household_head_start_benefits`, `basic_income`, `trump_dividend`,
+ `housing_assistance`;
+ - the two aggregates `household_state_benefits` (state SSI supplements and
+ other state programs; `ca_state_supplement` is in the 2026 list at
+ `household_state_benefits.yaml:339`) and `household_health_benefits`.
+
+ The housing assistance drops out when `gov.hud.abolition` is set
+ (`household_benefits.py:11-17`). Every run's JSON records the full tree
+ (`net_income_tree`).
+- **Taxes.** Taxes before refundable credits are:
+ - employee payroll tax and self-employment tax;
+ - federal income tax before refundable credits;
+ - the flat tax;
+ - state income tax plus the state use tax
+ (`household_state_tax_before_refundable_credits.py:10`);
+ - local income and occupational taxes
+
+ (`household_tax_before_refundable_credits.py:11-19`). Refundable credits
+ are the federal ones plus each state's
+ (`household_refundable_tax_credits.py:10-13`). The tables show taxes
+ before refundable credits and refundable credits as separate rows, so a
+ refundable credit on a zero liability reads as a credit, not a negative
+ tax.
+- **Health coverage is excluded by default.** `household_health_benefits`
+ returns zero unless `gov.simulation.include_health_benefits_in_net_income`
+ is true (`household_health_benefits.py:17-22`). Its list includes Medicaid
+ valued at cost, Medicare Savings Programs, CHIP, premium tax credits and
+ state premium programs (`parameters/gov/household/
+ household_health_benefits.yaml`). The parameter's default is false
+ (`parameters/gov/simulation/include_health_benefits_in_net_income.yaml:3`).
+ The health-cost subtraction follows the same switch
+ (`household_health_costs.py:16-20`). The script therefore reports Medicaid
+ and Medicare Savings Program values as memo lines, and repeats every run
+ with the switch on as a sensitivity.
+- **Social Security is an input.** `social_security_retirement` has no formula
+ (`variables/gov/ssa/ss/social_security_retirement.py:4-10`), and
+ `social_security` adds its four types (`social_security.py:11-14`).
+
+## How to run
+
+```sh
+uv venv --python 3.13 ~/.venvs/policyengine-us-2.18.0
+uv pip install --python ~/.venvs/policyengine-us-2.18.0/bin/python \
+ policyengine-us==2.18.0
+cd
+POPULACE_DYNAMICS_PE_US_PYTHON=~/.venvs/policyengine-us-2.18.0/bin/python \
+OMP_NUM_THREADS=1 PYTHONPATH=src .venv/bin/python \
+ scripts/pe_us_minimum_benefit_sample_households.py
+```
+
+The run takes about six minutes on a lightly loaded machine, most of it
+building the reformed tax-benefit systems and tracing. Each policyengine-us
+child may take up to two hours before it is stopped (`--timeout`). It
+writes a JSON file of inputs and results, a Markdown table, and one chart
+per household (PNG and SVG) to
+`docs/analysis/pe_us_bridge_20260930/` (`--docs-dir`), and a second copy to
+`--out-dir`. Microcosm's oracle reads its SSA parameters (the wage index,
+bend-point factors, the full retirement age and the CPI-W) from the same
+installed release, so both sides use one version.
+
+Tests:
+
+- `tests/bridge/test_policyengine_us_bridge.py` (unit tier; Hypothesis
+ properties and example tests, listed under Invariants). It covers:
+ - the situation builder;
+ - the decomposition identity and its refusals;
+ - the source check, on temporary git repositories and invented
+ distributions;
+ - RECORD verification;
+ - the float32 guard, including the first draft's use-tax case as traced;
+ - the COLA helpers.
+- `tests/bridge/test_pe_us_sample_households_script.py` (unit tier). It
+ checks the script's own logic on invented inputs, with the runner replaced
+ by fakes:
+ - parameter updates and overrides;
+ - the release pin;
+ - the invariants;
+ - the traced-versus-untraced check;
+ - the Medicaid valuation and the Medicaid text;
+ - Montana's evidence wording;
+ - half-dollar rounding in tables and charts;
+ - the refusal of local paths.
+- `tests/bridge/test_pe_us_bridge_artifact.py` (unit tier). It checks the
+ committed JSON and Markdown:
+ - the pinned release and a clean source check;
+ - the California override;
+ - a clean float32 guard;
+ - the identity;
+ - no local path, and no pointer to a folder outside the repository;
+ - the rows and the reform's name;
+ - Montana's credit wording, the Medicaid valuation and the rounding of
+ household C in Montana.
+- `tests/bridge/test_policyengine_us_bridge_oracle.py` (oracle tier). It
+ skips unless the interpreter exists and imports `policyengine_us`.
+
+## Provenance
+
+Each output JSON records:
+
+- the Microcosm Dynamics commit, and whether `src/`, `scripts/` or `tests/`
+ were dirty;
+- the pinned release and the installed one:
+ - version, wheel hash and policyengine-core version;
+ - the source check and the RECORD check (files verified, mismatches,
+ missing files);
+ - the installer and Python version;
+- every parameter update, with its source;
+- every Microcosm parameter source, with its hash:
+ - the Census threshold capture;
+ - the quarter-of-coverage capture;
+ - the committed COLA history;
+ - the release's CPI-W file.
+
+Paths are repository-relative or release-relative. The script refuses to
+write an absolute or home path.
+
+**Payment year.** The payment year is 2026. The parameters below set the
+results shown; the list is not every parameter the model reads. They fall
+into four groups.
+
+**Dated 2026 entries in the release:**
+
+- the SSI benefit rate, $994 (`parameters/gov/ssa/ssi/amount/individual.yaml:55`);
+- the 2026 federal standard deduction, $16,100
+ (`parameters/gov/irs/deductions/standard/amount.yaml:12`), and its
+ additional amount for the aged, $2,050
+ (`parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml:10`);
+- the 2026 federal brackets; the 10 percent bracket ends at $12,400 for a
+ single filer (`parameters/gov/irs/income/bracket.yaml:80`);
+- the 2026 poverty guideline, $15,960 (`parameters/gov/hhs/fpg.yaml:17`);
+- the 2026 Medicare Part B premium, $202.90
+ (`parameters/gov/hhs/medicare/part_b/base_premium.yaml:25`);
+- the Commodity Supplemental Food Program amount, $651
+ (`parameters/gov/usda/csfp/amount.yaml:11`).
+
+**Earlier entries that stay in force.** These carry into 2026 unchanged:
+
+- the federal senior deduction, $6,000 from 2025
+ (`parameters/gov/irs/deductions/senior_deduction/amount.yaml:5`). With the
+ standard and aged amounts it takes household C's AGI of $37,067.30 to
+ taxable income of $12,917.30;
+- the SSI resource limit, $2,000
+ (`parameters/gov/ssa/ssi/eligibility/resources/limit/individual.yaml:8`);
+- Florida's aged Medicaid limit, 88 percent of the guideline, from 2018
+ (`parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/
+ income/limit/individual.yaml:44-45`);
+- the Commodity Supplemental Food Program's income limit, 150 percent of
+ the guideline, from 2025 (`gov/usda/csfp/fpg_limit.yaml:4`).
+
+**Uprated by the release.** California's income-tax brackets, standard
+deduction and exemption credit end at 2025. The release extends them to
+2026 with `gov.states.ca.cpi` (`parameters/gov/states/ca/tax/income/rates/
+single.yaml:15`, `deductions/standard/amount.yaml:4`,
+`exemptions/amount.yaml:8,14`).
+
+- **Not California's CPI in 2026.** That series is California's June CPI
+ only through 2025 (`parameters/gov/states/ca/cpi.yaml:3-9`). It is itself
+ extended with the federal IRS uprating index (`cpi.yaml:15`, `uprating:
+ gov.irs.uprating`), so the 2026 step is the federal index's.
+- **Household C's $21.** These parameters set her $21 of California income
+ tax, which the reform does not change:
+ - $394 of tax on taxable income of $25,365, which is California AGI of
+ $31,200 less the $5,835 standard deduction;
+ - less two exemption credits of $156.47, personal and aged
+ (`variables/gov/states/ca/tax/income/exemptions/ca_exemptions.py:24-31`);
+ - less the $60 renter's credit (`credits/ca_renter_credit.py:16-25`).
+
+**Not updated for 2026:**
+
+- **SNAP's state utility allowances, October to December 2026.** SNAP
+ amounts for those months are fiscal-2027 values. The release encodes
+ USDA's fiscal-2027 maximum allotment and standard deduction, but the
+ state standard utility allowances have no fiscal-2027 entry. Those months
+ use the fiscal-2026 amounts: California $663, Florida $430 and Montana
+ $799 a month (`parameters/gov/usda/snap/income/deductions/utility/
+ standard/main.yaml:112`, `:179` and `:387`).
+- **Medicaid's cost per enrollee (health sensitivity only).** For one
+ household, Medicaid at cost is the state's Medicaid spending over its
+ enrollment (`medicaid_cost_if_enrolled.py:11-22`). The single-household
+ denominator is `medicaid_slcsp_state_denominator.py:28-33`, so the
+ person's cost index cancels.
+ - **The entries in force.** In 2026 they are 2023 Medicaid spending
+ (`parameters/calibration/gov/hhs/medicaid/totals/spending.yaml`) and
+ KFF's October 2024 "Total Monthly Medicaid/CHIP Enrollment"
+ (`enrollment.yaml:10-13`, entries dated `2024-10-01`), which counts
+ CHIP enrollees too. Neither is uprated.
+ - **The figures.** $9,200 (Florida), $9,236 (California) and $10,799
+ (Montana) are 2023 spending over October 2024 Medicaid and CHIP
+ enrollment. They are not same-year averages, not 2026 amounts, and not
+ specific to an aged enrollee.
+ - **The years matter.** Enrollment fell after the 2023 entries: Florida's
+ from 4,924,826 to 3,765,231. With the release's 2023 enrollment entry,
+ the spending's own year, the figures would be $7,034, $8,751 and
+ $7,223. Florida's $9,200 is 31 percent above its same-year figure.
+ - **Direction.** An average over all enrollees could overstate or
+ understate what covering an aged enrollee costs. The script computes
+ both ratios from the release, refuses other entry dates, and checks
+ that every Medicaid memo equals the release's ratio
+ (`medicaid_per_enrollee`, `check_medicaid_memo`).
+- **California's payment standard (overridden).** The aged or disabled
+ single payment standard has no entry after 2025 (`parameters/gov/states/
+ ca/cdss/state_supplement/payment_standard/aged_or_disabled/amount/
+ single.yaml:12-13`), so 2026 would use 2025's $1,206.94 a month.
+ - **The published amount.** The California Department of Social Services
+ publishes $1,233.94 for a single aged person living independently: the
+ federal $994 plus the state's $239.94. The source is "SSI Total Monthly
+ Payment Amounts 2026" (Rev. 1/26, effective 2026-01-01;
+ ,
+ retrieved 2026-09-30, SHA-256 `75a99370…`).
+ - **How the script applies it.** It passes the amount as a 2026 parameter
+ override for the California cases (`PARAMETER_UPDATES`). It refuses to
+ run if the installed release has a different 2026 value, and drops the
+ override once the release has the same one.
+ - **Where it shows.** The JSON, the Markdown header and every chart
+ footer show the override.
+ - **Upstream.** Fixing the parameter in PolicyEngine-US is a separate
+ task.
+
+## The minimum benefit as applied here
+
+The rule and its parameters come from this repository's Track M code, called
+unchanged. No parameter value is typed into the script.
+
+**The worker.** She is ILLUSTRATIVE. Born in 1958, she had covered earnings of
+25 percent of the national average wage index in each of the 22 years
+1980-2001. She was first entitled to her retired-worker benefit in 2024, at
+66. Her history is invented, so the JSON keeps only the count of covered
+years, not the PSID observation fields (observed, imputed and unobserved
+years).
+
+**Years of coverage.** `coverage.count_coverage_years` counts 22 years with
+four quarters of coverage, with no PSID gap-year imputation.
+
+**PIA.** `rules.history_pia` computes it on the old-age basis. The history
+runs through 2023, the year before entitlement. The bend points are those of
+2020, the year she attained 62. The result is P = $613.80 (AIME $682).
+
+**The minimum.** It is M = s(Y*) × T / 12 (`rules.monthly_minimum`).
+
+- Exercise 4's headline cell is option 2 (`policy.HEADLINE_CELL`) of
+ Favreault, Mermin and Steuerle (2006). That is the standard schedule: 55
+ percent at 10 years, rising to 100 percent at 40. At Y* = Y = 22, s = 0.73.
+ The 10-year floor does not bind.
+- T is the Census weighted-average threshold for one person aged 65 and over.
+ It is taken for the threshold year, the year of attaining 62: $12,413 for
+ 2020, from the pinned capture.
+- So M = $755.12: a minimum benefit set at 73% of the aged poverty
+ threshold for 22 years of work.
+
+**Floor after the cut.** Option 2 pays max((1 − 0.1281) P, M) =
+max($535.17, $755.12) = $755.12, so she is on the minimum.
+
+**Window and policy year.** The minimum reaches only a PIA first calculated
+in the window. The window is 2004 or later in the headline row MS0, which
+moved every date back three years for the PSID snapshot. It is 2007 or later
+in the Report's own dating (MS1), and after the policy year under MS4. Her
+PIA is first calculated in 2024, so it is in the window under all three. The
+script evaluates the rule under MS0, MS1 and MS4 and refuses to continue
+unless the outcomes are identical. For a price-indexed option, the policy
+year changes nothing else.
+
+**Baseline.** The baseline is current-law scheduled benefits: P with no cut.
+Against option 1 (reduced current law, P × (1 − 0.1245) = $537.38), her gain
+would be larger. That PIA is in the JSON as a memo and is not run through
+PolicyEngine-US.
+
+**COLAs to 2026.** Track M compares M with the cut PIA only at first
+calculation. Carrying both PIAs forward with statutory COLAs is this bridge's
+assumption:
+
+- **Which COLAs.** Under 42 USC 415(i)(2)(A)(iii), the COLAs from the year of
+ eligibility (2020) onward apply. Payments in 2026 reflect those determined
+ through 2025.
+- **Rounding.** Each step is floored to a dime (415(i)(2)(A)(ii)).
+ `carry_pia_forward` refuses an empty year range, which would return the
+ PIA untruncated.
+- **Sources.** The 1979-2022 COLAs come from the committed SSA history
+ (`estimates.parameters.load_cola_history`). The 2023-2025 COLAs come from
+ the release's third-quarter CPI-W averages (`gov/ssa/uprating.yaml:3-8`,
+ marked "Actuals"), by 415(i)(1)(D). The derived 2022 COLA equals the
+ committed one, 8.7 percent.
+- **Result.** P becomes $777.80 and M becomes $957.20.
+- **Claim factor.** `rules.claim_factor` gives 0.9556, for 8 months before
+ her full retirement age of 66 and 8 months.
+- **Monthly benefits.** Rounded down to the dollar, they are $743 and $914:
+ $8,916 and $10,968 a year, a difference of $2,052.
+
+## Households and states
+
+Every household is the same worker, aged 68 in 2026, living alone. She rents
+for $800 a month and pays heating or cooling costs. She has $1,500 in the
+bank, which is under the SSI resource limit of $2,000
+(`parameters/gov/ssa/ssi/eligibility/resources/limit/individual.yaml:8`). She
+has 88 Medicare quarters of coverage. Housing-voucher take-up is switched off:
+PolicyEngine-US defaults it on for every income-eligible renter
+(`variables/gov/hud/takes_up_housing_assistance_if_eligible.py:9`). She can
+prepare food at home.
+
+| Household | Other income | Purpose |
+|---|---|---|
+| A | None | SSI-eligible at baseline and under the reform |
+| B | $4,800 private pension | SSI-ineligible: the SNAP and state-supplement path |
+| C | $31,200 private pension ($2,600 a month) | The federal and state income-tax path |
+
+**Household C's pension sits off every bracket edge.** The first draft gave
+her a round $30,000. That put California AGI, which excludes Social
+Security, exactly on the $30,000 edge of California's use-tax table
+(`variables/gov/states/ca/tax/income/ca_use_tax.py:13-20`;
+`parameters/gov/states/ca/tax/income/use_tax/main.yaml:14-23`).
+- **What went wrong.** Float32 subtraction put the reform run one float32
+ step below the edge: `ca_agi` was 30,000.0 at baseline and
+ 29,999.998046875 in the reform (35,761.3984 − 5,761.3999). The use tax
+ fell from $3 to $2.
+- **What it was.** A rounding artifact, not a mechanism. The first draft
+ described it as one; it was wrong.
+- **Where the pension sits now.** At $31,200 the checks show:
+ - **California AGI** is $1,200 above the $30,000 edge.
+ - **A finite-difference scan** of pensions from $31,000 to $31,400 (in
+ $50 steps, all three states, baseline and reform) found every component
+ linear except one. Two scans were run during review, one by the fixer
+ and one by an independent reviewer; they are not part of this
+ repository.
+ - **The exception** is Montana's baseline elderly homeowner and renter
+ credit. Its gross-income multiplier drops from 0.3 to 0.2 at $40,001
+ (`parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/
+ multiplier.yaml`). Her gross household income is the pension plus all
+ of her Social Security: $40,116 at baseline, $115 above that edge, and
+ $42,168 in the reform, $333 below the next edge at $42,501.
+ - **The federal bracket.** Her taxable income is $12,917 at baseline and
+ $13,831 in the reform, both in the 12 percent bracket that starts at
+ $12,400.
+ - **The float32 guard** reports nothing.
+
+The states were chosen for mechanism, each verified in the code:
+
+- **California (a state SSI supplement).** The supplement is the payment
+ standard less SSI less SSI countable income, floored at zero
+ (`variables/gov/states/ca/cdss/state_supplement/ca_state_supplement.py:13-17`).
+ SNAP counts it as unearned income (`unearned_spm_unit.yaml:13`).
+- **Montana (an income tax that reaches Social Security).** Montana AGI
+ starts from federal AGI, which includes taxable Social Security
+ (`mt_agi_indiv.py:13-16`). From 2024 the Montana Social Security
+ adjustment is off (`mt_agi_indiv.py:20-29`;
+ `social_security/applies.yaml:16-18`).
+ - **The elderly homeowner and renter credit.** Montana's refundable
+ credit is property tax plus countable rent (15 percent of rent) less an
+ income reduction. It is floored at zero, capped at $1,150 and
+ multiplied by a schedule of gross household income
+ (`mt_elderly_homeowner_or_renter_credit.py:26-42`;
+ `rent_equivalent_tax_rate.yaml:3`, `cap.yaml:4`,
+ `multiplier.yaml:34-37`; MCA 15-30-2337(11) and 15-30-2340(2), (5)
+ and (6)).
+ - **Net household income and the income reduction.** Net household
+ income is gross household income less a $12,600 standard exclusion
+ (`net_household_income/standard_exclusion.yaml:6`; MCA 15-30-2337(8),
+ which calls it "household income"). The income reduction is a share
+ of it, 5 percent at $12,000 and over (`net_household_income/
+ reduction_rate.yaml:67-70`; MCA 15-30-2340(4)). Despite its name,
+ `mt_elderly_homeowner_or_renter_credit_net_household_income` returns
+ the reduction, not the income
+ (`mt_elderly_homeowner_or_renter_credit_net_household_income.py:18-33`).
+ - **The multiplier's income.** MCA 15-30-2340(5) opens with "household
+ income", but its table is headed "gross household income".
+ PolicyEngine-US applies the multiplier to gross household income, as
+ the table does (`mt_elderly_homeowner_or_renter_credit.py:19-23,41`).
+ - **Gross household income** counts all Social Security
+ (`mt_elderly_homeowner_or_renter_credit_gross_household_income.py:17-23`).
+- **Florida (no income tax).** None of the 46 entries in the state
+ income-tax list is Florida's
+ (`parameters/gov/states/household/state_income_tax_before_refundable_credits.yaml`).
+ Its optional state supplement requires a facility living arrangement
+ (`fl_oss_eligible.py:58-60,71-76`), which defaults to none
+ (`fl_oss_living_arrangement.py:21-37`, `fl_oss_community_care_type.py:18`).
+ Its optional aged Medicaid pathway ends at 88 percent of the poverty
+ guideline
+ (`parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/income/limit/individual.yaml:44-45`).
+
+## Results
+
+Annual 2026 dollars; change from current law to the reform.
+
+| Household | State | Social Security | Net income | Share kept | Net income with health coverage |
+|---|---|---:|---:|---:|---:|
+| A | CA | +2,052 | 0 | 0% | 0 |
+| A | MT | +2,052 | 0 | 0% | 0 |
+| A | FL | +2,052 | 0 | 0% | 0 |
+| B | CA | +2,052 | +397 | 19% | +397 |
+| B | MT | +2,052 | +1,845 | 90% | +1,845 |
+| B | FL | +2,052 | +1,128 | 55% | −2,737 |
+| C | CA | +2,052 | +1,942 | 95% | +1,942 |
+| C | MT | +2,052 | +1,887 | 92% | +1,887 |
+| C | FL | +2,052 | +1,942 | 95% | +1,942 |
+
+Each figure is rounded to the dollar on its own, with half-dollars rounded
+up in magnitude: household C's change in Montana is $1,886.50, shown as
+$1,887.
+
+The last column values Medicaid as the release does: the state's 2023
+Medicaid spending divided by its October 2024 Medicaid and CHIP enrollment,
+an average over enrollees of all ages, not uprated to 2026 (see Payment
+year). It could overstate or understate what covering an aged enrollee
+costs, and the mismatched years matter: with 2023 enrollment, Florida's
+$9,200 would be $7,034. It values coverage at average program cost; it is
+not a cash loss.
+
+What the components show (full tables in
+`docs/analysis/pe_us_bridge_20260930/pe_us_minimum_benefit_sample_households.md`):
+
+**Household A.** Federal SSI falls by exactly the benefit increase, $3,252 to
+$1,200, in every state.
+
+- SSI is the benefit rate less countable income
+ (`variables/gov/ssa/ssi/uncapped_ssi.py:13-16`). Social Security counts as
+ unearned income (`parameters/gov/ssa/ssi/income/sources/unearned.yaml:6`),
+ and her $20 general exclusion is already used
+ (`_apply_ssi_exclusions.py:28-30`).
+- SNAP counts SSI and Social Security alike
+ (`parameters/gov/usda/snap/income/sources/unearned.yaml:6,63`), so it does
+ not move.
+- California's supplement does not move either, because SSI plus countable
+ income stays at the federal rate. Her state benefit is $2,879 in both runs
+ ($239.94 a month).
+- The minimum benefit replaces SSI dollar for dollar. Her net income, and her
+ SSI-linked Medicaid, are unchanged.
+
+**Household B.** She is already off SSI.
+
+- **California.** Her SSI countable income ($743 + $400 − $20 = $1,123 a
+ month) sits under the $1,233.94 payment standard at baseline, so she gets
+ a $110.94 supplement ($1,331 a year). The increase lifts countable income
+ past the standard and she loses all of it.
+ - **SNAP.** SNAP falls by $324. SNAP counts the supplement as income, so
+ the income SNAP sees rises by only $2,052 − $1,331 = $721, and SNAP falls
+ by 45 percent of that (see Florida).
+ - **Net.** She keeps $397, 19 percent.
+ - **The earlier +$25.** A run on the 1.822.5 checkout with the same
+ override left her $25 (1 percent). That release did not count the
+ supplement as SNAP income, and there SNAP fell by $696 rather than
+ $324: $2,052 − $1,331 − $696 = $25.
+- **Florida.** SNAP falls by $924, 45 percent of the increase.
+ - **Why 45 percent.** SNAP's contribution is 30 percent of net income
+ (`snap_expected_contribution.py:17-32`, with the rate at
+ `parameters/gov/usda/snap/expected_contribution.yaml:4`).
+ - **The shelter deduction.** For an elderly household it is uncapped and
+ shrinks by half of any income gain
+ (`snap_excess_shelter_expense_deduction.py:19-38`, with
+ `income_share_disregard.yaml:4` at 0.5), so net income rises 1.5 times
+ the increase.
+- **Florida, health coverage.** Her countable income for Florida's optional
+ aged Medicaid pathway rises from $13,476 to $15,528. That passes the limit
+ of 88 percent of the poverty guideline, $14,045, so Medicaid ends
+ (`is_optional_senior_or_disabled_income_eligible.py:23-32`;
+ `individual.yaml:44-45`).
+ - **Coverage values.** That is outside default net income. The memo shows
+ Medicaid at cost falling from $9,200 to 0.
+ - **The Medicare Savings Program.** She is QMB-eligible in every month
+ of both runs (`is_qmb_eligible`, a monthly variable, read for all 12
+ months). PolicyEngine-US counts the Medicare Savings Program
+ only for people not enrolled in full Medicaid (`msp_cost.py:28`), so its
+ $5,335 appears once Medicaid ends.
+ - **Net.** With health coverage counted, she loses $2,737. Medicaid is
+ valued at $9,200, Florida's 2023 spending over its October 2024
+ Medicaid and CHIP enrollment, all ages ($7,034 with 2023 enrollment):
+ the coverage she loses at average program cost, not a cash loss.
+- **Montana.** SNAP falls by $207.
+
+**Household C.** Taxable Social Security rises from $5,867 to $6,781 under
+IRC 86 (`tax_unit_taxable_social_security.py:13-85`).
+
+- **Federal.** Federal income tax rises $110 in every state, 12 percent of
+ the $914 of newly taxable benefits.
+- **Montana.** Montana income tax rises $43, 4.7 percent of the same $914.
+ Her refundable elderly homeowner and renter credit falls from $13 to zero
+ (`mt_refundable_credits.py:12-15`):
+ - **Countable rent** is $1,440, 15 percent of $9,600.
+ - **Net household income**, gross household income less the $12,600
+ exclusion, rises from $27,516 to $29,568. Her gross household income
+ is $40,116 at baseline and $42,168 in the reform.
+ - **The income reduction**, 5 percent of net household income, rises
+ from $1,376 to $1,478 and passes the $1,440 of countable rent.
+ - **The credit**, max($1,440 − reduction, 0) × 0.2, falls from $12.84 to
+ zero. The multiplier is 0.2 in both runs, for gross household income
+ of $40,001 to $42,500.
+- **California.** California AGI excludes Social Security, so California's
+ income tax ($21) and use tax ($3) do not move.
+- **Florida.** Only federal tax changes.
+
+## Invariants
+
+The unit tests check these. Where the input space allows, a Hypothesis
+property checks an invariant over generated inputs; elsewhere example tests
+check chosen cases, many of them parametrized.
+
+**Hypothesis properties:**
+
+- **Membership.** The situation builder places every person in exactly one
+ tax unit, SPM unit, marital unit, family and household. Amounts, ages and
+ the state round-trip, output is deterministic JSON, and negative amounts
+ are refused.
+- **Decomposition identity.** Leaf changes sum exactly, in integer cents, to
+ the net change, and the categories sum to the same total, for arbitrary
+ float values too. When the reform equals the baseline, every change is
+ zero. The result is deterministic and does not depend on the order of
+ parts. An aggregate inconsistent with its parts is refused, and so is any
+ non-finite value. Whole cents round-trip through `to_cents`.
+- **COLA carry-forward.** It yields whole dimes and is monotone in the PIA.
+ Truncation loses less than a dime a step, carrying composes over split
+ year ranges, and with no COLAs the PIA is only truncated to a dime. A
+ range whose first year follows its last is refused. CPI-W-derived COLAs
+ are nonnegative tenths of a percent.
+- **RECORD digest.** The package digest ignores installer lines (pip's
+ bytecode rows included) and line order.
+- **Float32 guard.** `float32_step` is the float32 spacing.
+ - Always reported: a jump of a cent or more over a sub-cent read, and a
+ one-cent step stored in float32, at any magnitude where float32 can
+ tell the two amounts apart.
+ - Never reported: a change through a chain of changed variables down to
+ an input, two identical runs, or a change under half a cent.
+- **Display.** Every amount the script shows is its whole-dollar value with
+ a half-dollar rounded up, compared against exact decimal arithmetic.
+
+**Example tests:**
+
+- **Membership.** Non-finite and non-numeric amounts, bad ages, bad
+ partitions, a three-person marital unit, unknown states, duplicate ids
+ and a bare string passed as a group are refused. Generators work as
+ groups.
+- **Decomposition.** A drifted root definition and a cyclic or badly
+ signed tree are refused, and so is a minimal NaN counterexample
+ (root → mid → (x, y) with mid = NaN). Each leaf has its documented
+ category, and a refundable credit on a zero liability reads as a credit,
+ not a negative tax.
+- **COLAs.** A missing COLA is refused. Float noise in a rate does not reach
+ the dime. SSA's third-quarter CPI-W averages give the published COLAs,
+ and the base carries forward past a year without an increase.
+- **Source check.**
+ - An install by name is published.
+ - A clone whose commit a branch of `origin` contains now, and whose files
+ are clean, is published.
+ - These are refused:
+ - a local commit not on `origin`;
+ - a branch deleted on `origin` whose remote-tracking ref remains;
+ - an unreachable `origin`, or one that is not on GitHub (the check
+ reads the host; it cannot tell a public repository from a private
+ one);
+ - a modified, untracked or git-ignored package file (bytecode aside);
+ - a package outside any checkout;
+ - a VCS, archive or local-directory install.
+- **RECORD.** Verification flags each changed, missing or added file.
+- **Float32 guard.** The first draft's use-tax case, as traced, is
+ reported. A
+ step above $131,072, where one float32 step exceeds a cent, is still
+ seen. A branch taken in one run only is a cause, and a branch's copy of
+ a main value reads the main node. The exact 3.00 to 3.01 float32 step is
+ reported, and so is one float32 step of noise at $70,000 (the tradeoff
+ above); a one-cent step computed from larger operands is not.
+- **The script's own checks.** On invented inputs, with the runner
+ replaced by fakes:
+ - parameter updates are applied, dropped when the release already
+ matches, or refused when it differs;
+ - overrides go to the right states;
+ - a local path is refused;
+ - the release pin refuses any other version, file or RECORD;
+ - the invariants raise (never `assert`) on a wrong-way change, including
+ a rising refundable credit, or a guard finding;
+ - the traced-versus-untraced differential refuses a mismatch;
+ - the Medicaid valuation computes both ratios, refuses other entry
+ dates, states both directions, and refuses a memo off the ratio;
+ - the Medicaid text is refused when its pattern does not hold;
+ - Montana's evidence keeps net household income and its reduction apart;
+ - half-dollars round up in magnitude (+1,886.50 shows as +1,887), and a
+ value that is not a number is refused.
+- **The committed outputs** (`test_pe_us_bridge_artifact.py`) are checked as
+ listed under How to run.
+
+The oracle tests check, against the live model:
+
+- SSI never rises when Social Security does, and falls dollar for dollar
+ while positive (household A in California and Florida);
+- SNAP does not move for household A (California and Florida) and falls for
+ household B in Florida;
+- California's supplement is constant for household A;
+- the 2026 payment-standard override reaches the supplement: $110.94 a month
+ for household B, against $83.94 under the release's held value;
+- federal and state income taxes do not fall for household C in Montana;
+- the identity holds on four live cases (A in California and Florida, B in
+ Florida, C in Montana);
+- the float32 guard flags `ca_use_tax` at a $30,000 pension and nothing at
+ $31,200, and keys the `itemizing` and `not_itemizing` branches' nodes
+ apart;
+- the interpreter's source is published and its installed files match the
+ RECORD, with none added, and a run pinned to another RECORD digest is
+ refused;
+- the live tree is the definition the bridge was read against;
+- a case run with others equals the case run alone: household A in Florida
+ with no overrides, and household B in California with the
+ payment-standard override, whose shared system was built from household
+ A's simulation;
+- all 51 FIPS codes resolve to their state.
+
+The script checks every household-state pair, with and without health
+coverage, before writing:
+
+- the identity holds;
+- the Social Security component equals the Microcosm benefit change;
+- no means-tested benefit or refundable tax credit rises, and no income tax
+ falls (`WRONG_WAY_CATEGORIES`);
+- PolicyEngine-US's own net-income change is within a cent of the definition's
+ sum;
+- wherever she is enrolled, Medicaid at cost equals the release's spending
+ over enrollment, the ratio the valuation text describes (within 50
+ cents), both in the memo and in the with-health decomposition's
+ `medicaid_cost` leaf;
+- **the float32 guard.** Every changed component is traced: 44 leaves in 18
+ comparisons, 65,907 traced variable-periods (branch simulations' nodes
+ counted apart).
+ - Each traced component, from its traced simulation, equals its
+ untraced value within a cent.
+ - The per-case runner reproduces the first draft's batched runner exactly
+ for every decomposition (all 18). Only Medicaid-at-cost memo values
+ move, by one float32 step, for example $9,236.4785 to $9,236.4795. That
+ is the cross-case aggregation per-case simulations remove.
+ - No variable changed by a cent or more while every variable it read
+ held within float noise.
+ - No component changed by a nonzero amount under $2.
+
+## Caveats
+
+- Illustrative households, not survey data. The worker was chosen so the
+ minimum binds. A worker above the minimum would lose the 12.81 percent
+ uniform cut instead.
+- Take-up follows PolicyEngine-US's defaults: full take-up of SSI, SNAP and
+ Medicaid (`takes_up_ssi_if_eligible.py:9`, `takes_up_snap_if_eligible.py:9`,
+ `takes_up_medicaid_if_eligible.py:9`).
+- **The Commodity Supplemental Food Program** (the $651 line) has no take-up
+ input. Every eligible person, aged 60 or more with income at or under 150
+ percent of the poverty guideline, gets USDA's cost per caseload slot
+ (`commodity_supplemental_food_program.py:10-11`;
+ `commodity_supplemental_food_program_eligible.py:17-18,42`;
+ `gov/usda/csfp/min_age.yaml:3`, `amount.yaml:11`, `fpg_limit.yaml:4`). The program is
+ caseload-limited and serves far fewer people than are eligible. It does
+ not change here.
+- California's payment standard for 2026 and SNAP's utility allowances for
+ fiscal 2027 are as described under Provenance.
+- The CPI-W-derived COLAs are the release's third-quarter averages. The
+ committed history covers 1979-2022 only.
diff --git a/scripts/first_estimates_birth_evidence.py b/scripts/first_estimates_birth_evidence.py
index b277432ca..415114b62 100644
--- a/scripts/first_estimates_birth_evidence.py
+++ b/scripts/first_estimates_birth_evidence.py
@@ -334,6 +334,11 @@
Path("scripts/track_u2_component_diagnostics.py"),
Path("scripts/run_track_u2_registered.py"),
Path("scripts/u2_u1_differential.py"),
+ # The opt-in PolicyEngine-US bridge passes benefit amounts to an
+ # external tax-benefit model after the fact; nothing historical imports
+ # it.
+ Path("src/populace_dynamics/bridge/__init__.py"),
+ Path("src/populace_dynamics/bridge/policyengine_us.py"),
)
POST_REVIEW_SHARED_SOURCE_BLOBS = {
Path(
diff --git a/scripts/pe_us_minimum_benefit_sample_households.py b/scripts/pe_us_minimum_benefit_sample_households.py
new file mode 100644
index 000000000..29208f8bb
--- /dev/null
+++ b/scripts/pe_us_minimum_benefit_sample_households.py
@@ -0,0 +1,2036 @@
+"""Federal and state tax-benefit effect of the exercise-4 minimum benefit.
+
+ILLUSTRATIVE HOUSEHOLDS, NOT SURVEY DATA. Nothing here is drawn from the
+PSID, the projection or any DYNASIM3 value. The script is the first use of
+:mod:`populace_dynamics.bridge.policyengine_us`:
+
+1. **Microcosm side (this repository's code).** One illustrative worker: a
+ woman born 1958 with covered earnings of 25 percent of the national
+ average wage index in each of the 22 years 1980-2001, first entitled to
+ her retired-worker benefit in 2024 (at 66). Her years of coverage come
+ from ``min_benefit_track_m.coverage.count_coverage_years``, her PIA from
+ ``min_benefit_track_m.rules.history_pia`` (the oracle's statutory AIME
+ and ``ss.benefits.pia``), and her reform PIA from
+ ``min_benefit_track_m.rules.evaluate_worker`` under exercise 4's
+ headline option (``policy.HEADLINE_CELL``: option 2 of Favreault,
+ Mermin and Steuerle (2006), the standard price-indexed minimum, with its
+ printed uniform cut). Both PIAs are carried to 2026 with statutory
+ COLAs (42 USC 415(i)) from the committed SSA history (determination
+ years through 2022) and the third-quarter CPI-W averages of the pinned
+ policyengine-us release (2023-2025), then reduced by her claim factor
+ (``rules.claim_factor``) and rounded down to the dollar.
+2. **PolicyEngine-US side.** Three households built from that worker (A:
+ no other income; B: a $4,800 private pension; C: a $31,200 private
+ pension), each in California, Montana and Florida, run for 2026 with
+ the current-law benefit and with the reform benefit as
+ ``social_security_retirement``. The bridge decomposes the change in
+ ``household_net_income`` into its components, which sum exactly to the
+ net change, and repeats the runs with health coverage counted in net
+ income (a sensitivity; PolicyEngine-US's default excludes it).
+3. **Checks.** The runs use policyengine-us 2.18.0 from PyPI, and the
+ script refuses any other source (the installed files must match the
+ wheel's RECORD). California's 2026 SSI payment standard, missing from
+ that release, is set from the California Department of Social Services'
+ published table. Every changed component is traced back through
+ PolicyEngine-US's own calculation (the float32 guard), and the script
+ refuses to write if any variable changed by a cent or more (allowing
+ one float32 step of rounding) although every variable it read held
+ within float noise (a cent, or four float32 steps at the read's size
+ where that is more).
+
+Outputs (JSON with provenance, a Markdown table, and a chart per household
+as PNG and SVG) go to ``--out-dir`` and ``--docs-dir``.
+
+Run from the repository root::
+
+ POPULACE_DYNAMICS_PE_US_PYTHON=~/.venvs/policyengine-us-2.18.0/bin/python \\
+ OMP_NUM_THREADS=1 PYTHONPATH=src .venv/bin/python \\
+ scripts/pe_us_minimum_benefit_sample_households.py
+"""
+
+from __future__ import annotations
+
+import argparse
+import hashlib
+import json
+import math
+import numbers
+import subprocess
+import sys
+import textwrap
+from pathlib import Path
+from typing import Any
+
+ROOT = Path(__file__).resolve().parents[1]
+if str(ROOT / "src") not in sys.path:
+ sys.path.insert(0, str(ROOT / "src"))
+
+import yaml # noqa: E402
+
+from populace_dynamics.bridge import policyengine_us as bridge # noqa: E402
+from populace_dynamics.estimates.parameters import ( # noqa: E402
+ load_cola_history,
+)
+from populace_dynamics.min_benefit_track_m import ( # noqa: E402
+ coverage,
+ rules,
+)
+from populace_dynamics.min_benefit_track_m.policy import ( # noqa: E402
+ HEADLINE_CELL,
+ OPTIONS,
+ TrackMPolicy,
+ policy_for_row,
+)
+from populace_dynamics.ss.params import load_ssa_parameters # noqa: E402
+
+SCHEMA_VERSION = "pe_us_bridge.minimum_benefit_sample_households.v2"
+ILLUSTRATIVE_LABEL = "Illustrative household, not survey data"
+PAYMENT_YEAR = 2026
+#: ``gov/ssa/uprating.yaml`` in policyengine-us 2.18.0 marks its entries
+#: dated 2022-01-01 through 2026-01-01 as actual third-quarter CPI-W
+#: averages (lines 3-8: "Actuals"); later entries are forecasts. A payment
+#: year after 2026 would need a forecast COLA, which this script refuses.
+LAST_ACTUAL_CPI_W_ENTRY = 2026
+DEFAULT_OUT_DIR = Path(
+ "~/microcosm-launch-evidence/dynasim-parity-20260909/"
+ "pe-us-bridge-20260930"
+).expanduser()
+DEFAULT_DOCS_DIR = ROOT / "docs" / "analysis" / "pe_us_bridge_20260930"
+OUTPUT_STEM = "pe_us_minimum_benefit_sample_households"
+
+#: The one policyengine-us release the published results use: 2.18.0 on
+#: PyPI, uploaded 2026-09-30. ``wheel_sha256`` is PyPI's digest of the
+#: wheel; ``package_record_digest`` is
+#: :func:`bridge.package_record_digest` of that wheel's RECORD over its
+#: 17,551 ``policyengine_us/`` lines, which equals the installed RECORD's
+#: (checked by downloading the wheel on 2026-09-30). The script refuses to
+#: run unless the interpreter imports this release from a package index and
+#: every installed ``policyengine_us/`` file matches its RECORD hash.
+PE_US_RELEASE: dict[str, Any] = {
+ "package": "policyengine-us",
+ "version": "2.18.0",
+ "index_url": "https://pypi.org/project/policyengine-us/2.18.0/",
+ "wheel": "policyengine_us-2.18.0-py3-none-any.whl",
+ "wheel_sha256": (
+ "28e32bc1339e8ffc1ed676ac1c9ecd468d191685608039643915f63e1357085f"
+ ),
+ "package_record_digest": (
+ "9f9f090894638e3d0e0dd5ad8ab34f36cf8cb2134ef3627407d0653381906007"
+ ),
+ "package_record_lines": 17_551,
+}
+PARAMETER_PREFIX = "policyengine_us/parameters/"
+
+#: Payment-year values the release lacks, each from a published source. An
+#: update applies only to its states, and only when the installed parameter
+#: has no entry dated in the payment year; the script refuses to run if it
+#: has one that differs. Values are monthly dollars, as the parameter is.
+PARAMETER_UPDATES: tuple[dict[str, Any], ...] = (
+ {
+ "parameter": (
+ "gov.states.ca.cdss.state_supplement.payment_standard."
+ "aged_or_disabled.amount.single"
+ ),
+ "file": (
+ PARAMETER_PREFIX + "gov/states/ca/cdss/state_supplement/"
+ "payment_standard/aged_or_disabled/amount/single.yaml"
+ ),
+ "states": ["CA"],
+ "year": PAYMENT_YEAR,
+ "value": 1_233.94,
+ "unit": "dollars a month",
+ "source": {
+ "publisher": "California Department of Social Services",
+ "title": "SSI Total Monthly Payment Amounts 2026",
+ "revision": "Rev. 1/26",
+ "effective": "2026-01-01",
+ "entry": (
+ "Single Person, Aged or with Qualifying Disability; "
+ "Independent Living - Residing in Own Household: $1,233.94"
+ ),
+ "url": (
+ "https://cdss.ca.gov/Portals/13/SHD/ParaRegIndex/"
+ "SSI%20Monthly%20Payment%20Amounts%202026.pdf"
+ ),
+ "retrieved": "2026-09-30",
+ "sha256": (
+ "75a9937075ed1891cf6a0dd481bd3f0516b1ad11331041555f1d8c04"
+ "7bb57228"
+ ),
+ },
+ },
+)
+
+#: The illustrative worker. Every number here is an input chosen for the
+#: illustration; everything derived from them is computed below.
+WORKER: dict[str, Any] = {
+ "birth_year": 1958,
+ "sex": "female",
+ "first_earnings_year": 1980,
+ "earnings_years": 22,
+ "share_of_average_wage_index": 0.25,
+ "entitlement_year": 2024,
+}
+#: Common household facts (annual dollars; assets a stock).
+MONTHLY_RENT = 800.0
+BANK_ACCOUNT_ASSETS = 1_500.0
+HOUSEHOLDS: tuple[dict[str, Any], ...] = (
+ {
+ "key": "A",
+ "label": "Household A",
+ "description": (
+ "The worker alone: no income besides Social Security, $1,500 in "
+ "the bank (under the SSI resource limit), renting for $800 a "
+ "month"
+ ),
+ "taxable_private_pension_income": 0.0,
+ "purpose": "SSI-eligible at baseline and under the reform",
+ },
+ {
+ "key": "B",
+ "label": "Household B",
+ "description": (
+ "The same worker with a $4,800-a-year ($400-a-month) private "
+ "pension, which puts her countable income above the federal SSI "
+ "benefit rate"
+ ),
+ "taxable_private_pension_income": 4_800.0,
+ "purpose": "SSI-ineligible: the SNAP and state-supplement path",
+ },
+ {
+ "key": "C",
+ "label": "Household C",
+ "description": (
+ "The same worker with a $31,200-a-year ($2,600-a-month) private "
+ "pension, enough for part of her Social Security to be taxable"
+ ),
+ # $31,200, not a round $30,000: at $30,000 California AGI sat on
+ # the $30,000 edge of the use-tax table, and float32 subtraction
+ # (35,761.3984 - 5,761.3999) put the reform run a fraction of a
+ # cent below it, a $1 step that was noise, not a mechanism. The
+ # float32 guard below would now refuse that run.
+ "taxable_private_pension_income": 31_200.0,
+ "purpose": "The federal and state income-tax path",
+ },
+)
+#: The state comparison, each state chosen for one mechanism verified in
+#: the policyengine-us 2.18.0 code (paths under ``policyengine_us/``).
+STATES: dict[str, dict[str, Any]] = {
+ "CA": {
+ "name": "California",
+ "mechanism": "A state SSI supplement that PolicyEngine-US models",
+ "evidence": [
+ "variables/gov/states/ca/cdss/state_supplement/"
+ "ca_state_supplement.py:13-17: the supplement is the payment "
+ "standard less federal SSI less SSI countable income, floored at "
+ "zero, so it is reduced dollar for dollar by countable income "
+ "above the federal benefit rate",
+ "parameters/gov/states/ca/cdss/state_supplement/payment_standard/"
+ "aged_or_disabled/amount/single.yaml:12-13: the aged or disabled "
+ "single payment standard is 1,206.94 a month from 2025-01-01, "
+ "with no later entry; this script sets the 2026 value to CDSS's "
+ "published 1,233.94 (PARAMETER_UPDATES)",
+ "parameters/gov/usda/snap/income/sources/unearned_spm_unit.yaml"
+ ":13: SNAP counts the California supplement as unearned income",
+ ],
+ },
+ "MT": {
+ "name": "Montana",
+ "mechanism": (
+ "An income tax that reaches federally taxable Social Security, "
+ "and an elderly renter credit that counts all Social Security"
+ ),
+ "evidence": [
+ "variables/gov/states/mt/tax/income/base/mt_agi_indiv.py: "
+ "Montana AGI starts from federal AGI, which includes taxable "
+ "Social Security; the separate Montana Social Security "
+ "adjustment applies only when "
+ "gov.states.mt.tax.income.social_security.applies is true",
+ "parameters/gov/states/mt/tax/income/social_security/applies.yaml"
+ ": false from 2024",
+ "variables/gov/states/mt/tax/income/credits/"
+ "mt_elderly_homeowner_or_renter/"
+ "mt_elderly_homeowner_or_renter_credit.py:26-42: a refundable "
+ "credit of property tax plus countable rent (15 percent of "
+ "rent; MCA 15-30-2337(11)) less an income reduction, floored at "
+ "zero, capped at 1,150 and multiplied by a schedule of gross "
+ "household income (MCA 15-30-2340(2), (5) and (6))",
+ "variables/gov/states/mt/tax/income/credits/"
+ "mt_elderly_homeowner_or_renter/"
+ "mt_elderly_homeowner_or_renter_credit_net_household_income.py"
+ ":18-33: despite its name, this variable is the income "
+ "reduction, not net household income. Net household income is "
+ "gross household income less a 12,600 standard exclusion (MCA "
+ "15-30-2337(8) calls it household income); the reduction is a "
+ "share of it, 5 percent at 12,000 and over (MCA 15-30-2340(4))",
+ "variables/gov/states/mt/tax/income/credits/"
+ "mt_elderly_homeowner_or_renter/"
+ "mt_elderly_homeowner_or_renter_credit_gross_household_income.py"
+ ": gross household income adds the untaxed part of Social "
+ "Security to federal AGI, so all of it counts",
+ ],
+ },
+ "FL": {
+ "name": "Florida",
+ "mechanism": (
+ "No state income tax, and no state supplement for a person in "
+ "her own home"
+ ),
+ "evidence": [
+ "parameters/gov/states/household/"
+ "state_income_tax_before_refundable_credits.yaml: no entry for "
+ "Florida",
+ "variables/gov/states/fl/dcf/oss/fl_oss_eligible.py: Florida's "
+ "optional state supplement requires a facility living "
+ "arrangement, mapped from fl_oss_community_care_type, whose "
+ "default is none",
+ "parameters/gov/hhs/medicaid/eligibility/categories/"
+ "senior_or_disabled/income/limit/individual.yaml:44-45: Florida's "
+ "optional aged Medicaid pathway ends at 88 percent of the "
+ "poverty guideline (reported as a memo; health coverage is "
+ "outside default net income)",
+ ],
+ },
+}
+MEDICAID_LIMIT_FILE = (
+ PARAMETER_PREFIX + "gov/hhs/medicaid/eligibility/categories/"
+ "senior_or_disabled/income/limit/individual.yaml"
+)
+#: QMB eligibility is monthly (``is_qmb_eligible.py:8``), so every month
+#: is read.
+QMB_MONTHS = tuple(
+ f"is_qmb_eligible@{PAYMENT_YEAR}-{month:02d}" for month in range(1, 13)
+)
+MEMO_VARIABLES = (
+ "medicaid_cost",
+ "msp_cost",
+ "is_medicaid_eligible",
+ "medicaid_optional_senior_or_disabled_countable_income",
+ "medicaid_optional_senior_or_disabled_income_limit",
+ *QMB_MONTHS,
+ "commodity_supplemental_food_program_eligible",
+ "taxable_social_security",
+)
+#: How the with-health figures value Medicaid, as policyengine-us 2.18.0
+#: computes it for one household: the state's Medicaid spending over its
+#: enrollment (``medicaid_cost_if_enrolled.py:11-22``; the single-household
+#: denominator is ``medicaid_slcsp_state_denominator.py:28-33``, so the
+#: person's cost index cancels), from the calibration totals. In 2026 the
+#: entries in force are 2023 spending and KFF's October 2024 "Total Monthly
+#: Medicaid/CHIP Enrollment" (``enrollment.yaml:10-13``), so the ratio is
+#: not a same-year average. :func:`medicaid_per_enrollee` refuses other
+#: entry dates, so this text cannot go stale.
+MEDICAID_VALUATION = (
+ "Medicaid is valued as policyengine-us 2.18.0 values it for one "
+ "household: the state's 2023 Medicaid spending divided by its October "
+ "2024 Medicaid and CHIP enrollment, an average over enrollees of all "
+ "ages, not uprated to 2026. It is not a same-year average and not "
+ "specific to aged enrollees, so it could overstate or understate what "
+ "covering an aged enrollee costs. It values coverage at average "
+ "program cost; it is not a cash loss."
+)
+MEDICAID_TOTALS = "calibration/gov/hhs/medicaid/totals/"
+#: The calibration entries :data:`MEDICAID_VALUATION` describes, and the
+#: enrollment entry of the spending's own year.
+MEDICAID_SPENDING_ENTRY = "2023-01-01"
+MEDICAID_ENROLLMENT_ENTRY = "2024-10-01"
+MEDICAID_SAME_YEAR_ENROLLMENT_ENTRY = "2023-01-01"
+HEALTH_OVERRIDE = {
+ "gov.simulation.include_health_benefits_in_net_income": True
+}
+#: Seconds each policyengine-us child (the run, then the traced rerun) may
+#: take before it is stopped. The bridge's own default is 1,800; on a
+#: heavily loaded machine the 36-case run takes longer, and a timeout only
+#: guards against a hung child, so the script allows more.
+RUN_TIMEOUT_SECONDS = 7_200.0
+VARIANTS = ("default", "with_health")
+SCENARIOS = ("baseline", "reform")
+
+
+# ---------------------------------------------------------------------------
+# Provenance helpers
+# ---------------------------------------------------------------------------
+def _git(path: Path, *args: str) -> str:
+ return subprocess.run(
+ ["git", *args],
+ cwd=path,
+ capture_output=True,
+ text=True,
+ check=True,
+ ).stdout.strip()
+
+
+def _sha256(path: Path) -> str:
+ return hashlib.sha256(path.read_bytes()).hexdigest()
+
+
+def _repo_relative(path: str | Path) -> str:
+ """``path`` relative to the repository root (refused if outside it)."""
+
+ return str(Path(path).resolve().relative_to(ROOT))
+
+
+def _in_force(values: dict[Any, Any], on: str) -> tuple[str, float]:
+ """The entry in force on ``on`` (ISO date) of a ``{date: value}`` map."""
+
+ dated = sorted((str(key), value) for key, value in values.items())
+ in_force = [(key, value) for key, value in dated if key <= on]
+ if not in_force:
+ raise ValueError(f"no value in force on {on}: {dated}")
+ key, value = in_force[-1]
+ return key, float(value)
+
+
+def _dated_value(values: dict[Any, Any], on: str) -> float:
+ """The value in force on ``on`` (ISO date) of a ``{date: value}`` map."""
+
+ return _in_force(values, on)[1]
+
+
+def _parameter(root: Path, relative: str, *keys: Any, on: str) -> float:
+ """A value of a policyengine-us parameter file, in force on ``on``."""
+
+ node = yaml.safe_load((root / PARAMETER_PREFIX / relative).read_text())
+ for key in keys:
+ node = node[key]
+ return _dated_value(node.get("values", node), on)
+
+
+def _yaml_year_values(path: Path) -> dict[int, float]:
+ document = yaml.safe_load(path.read_text())
+ return {
+ int(str(key)[:4]): float(value)
+ for key, value in document["values"].items()
+ }
+
+
+def pinned_release(
+ python: str | None,
+) -> tuple[bridge.PolicyEngineUSInstallation, dict[str, Any]]:
+ """The interpreter's policyengine-us, checked against the pinned release.
+
+ Refuses (``ValueError``) unless it is a published index install of
+ :data:`PE_US_RELEASE`'s version whose installed ``policyengine_us/``
+ files all match the RECORD and whose RECORD matches the wheel's.
+ """
+
+ installation = bridge.inspect_installation(python)
+ source = bridge.check_published_source(installation)
+ problems = []
+ if source["kind"] != "index":
+ problems.append(f"installed from {source['kind']}, not PyPI")
+ if installation.version != PE_US_RELEASE["version"]:
+ problems.append(
+ f"version {installation.version}, not {PE_US_RELEASE['version']}"
+ )
+ record = bridge.verify_record(installation)
+ if record["mismatched"] or record["missing"] or record["extra"]:
+ problems.append(
+ f"{len(record['mismatched'])} installed files differ from the "
+ f"RECORD, {len(record['missing'])} are missing and "
+ f"{len(record['extra'])} are not in it"
+ )
+ if record["package_record_digest"] != (
+ PE_US_RELEASE["package_record_digest"]
+ ):
+ problems.append("the RECORD is not the pinned wheel's")
+ if problems:
+ raise ValueError(
+ "the policyengine-us interpreter is not the pinned release ("
+ + "; ".join(problems)
+ + "); install it with `uv venv ~/.venvs/policyengine-us-2.18.0 && "
+ "uv pip install --python ~/.venvs/policyengine-us-2.18.0 "
+ "policyengine-us==2.18.0` and set "
+ f"{bridge.PE_US_PYTHON_ENV}"
+ )
+ return installation, {
+ "release": dict(PE_US_RELEASE),
+ "installed": {
+ "version": installation.version,
+ "policyengine_core_version": installation.core_version,
+ "python_version": installation.python_version,
+ "installer": installation.installer,
+ "record_sha256": installation.record_sha256,
+ },
+ "source_check": source,
+ "record_check": {
+ key: (len(value) if isinstance(value, list) else value)
+ for key, value in record.items()
+ },
+ "interpreter": (
+ f"{bridge.PE_US_PYTHON_ENV}: a uv virtual environment holding "
+ "policyengine-us==2.18.0 from PyPI"
+ ),
+ }
+
+
+def parameter_updates(parameter_root: Path) -> list[dict[str, Any]]:
+ """The :data:`PARAMETER_UPDATES` to apply, each with the value it replaces.
+
+ Refuses an update whose parameter already has an entry dated in its
+ year with a different value; drops one whose entry already matches.
+ """
+
+ applied = []
+ for update in PARAMETER_UPDATES:
+ path = parameter_root / update["file"]
+ document = yaml.safe_load(path.read_text())
+ entries = sorted(
+ (str(key), float(value))
+ for key, value in document["values"].items()
+ )
+ year = str(update["year"])
+ dated = [(key, value) for key, value in entries if key[:4] == year]
+ if dated:
+ if all(math.isclose(v, update["value"]) for _, v in dated):
+ continue
+ raise ValueError(
+ f"{update['parameter']} has {dated} for {year}, not the "
+ f"published {update['value']}"
+ )
+ held = [(key, value) for key, value in entries if key[:4] < year]
+ held_date, held_value = held[-1]
+ applied.append(
+ {
+ **update,
+ "file_sha256": _sha256(path),
+ "installed_value": held_value,
+ "installed_value_dated": held_date,
+ }
+ )
+ return applied
+
+
+def _overrides_for(
+ state: str, variant: str, updates: list[dict[str, Any]]
+) -> dict[str, Any]:
+ overrides: dict[str, Any] = {
+ update["parameter"]: update["value"]
+ for update in updates
+ if state in update["states"]
+ }
+ if variant == "with_health":
+ overrides.update(HEALTH_OVERRIDE)
+ return overrides
+
+
+def medicaid_per_enrollee(parameter_root: Path) -> dict[str, dict[str, Any]]:
+ """Each state's Medicaid spending per enrollee, two ways.
+
+ ``release`` is the ratio policyengine-us computes in the payment year
+ (the spending and enrollment entries in force); ``same_year`` divides
+ the same spending by the enrollment entry of the spending's own year.
+ Refuses entries other than the ones :data:`MEDICAID_VALUATION`
+ describes.
+ """
+
+ documents = {
+ name: yaml.safe_load(
+ (
+ parameter_root / PARAMETER_PREFIX / MEDICAID_TOTALS / name
+ ).read_text()
+ )
+ for name in ("spending.yaml", "enrollment.yaml")
+ }
+ on = f"{PAYMENT_YEAR}-01-01"
+ out = {}
+ for state in STATES:
+ spending_date, spending = _in_force(
+ documents["spending.yaml"][state], on
+ )
+ enrolled = documents["enrollment.yaml"][state]
+ enrollment_date, enrollment = _in_force(enrolled, on)
+ if (spending_date, enrollment_date) != (
+ MEDICAID_SPENDING_ENTRY,
+ MEDICAID_ENROLLMENT_ENTRY,
+ ):
+ raise ValueError(
+ f"{state}: Medicaid spending {spending_date} and enrollment "
+ f"{enrollment_date} are in force, not the entries "
+ "MEDICAID_VALUATION describes"
+ )
+ same_year = {str(key): float(value) for key, value in enrolled.items()}
+ out[state] = {
+ "spending": spending,
+ "spending_entry": spending_date,
+ "enrollment": enrollment,
+ "enrollment_entry": enrollment_date,
+ "release": spending / enrollment,
+ "same_year_enrollment": same_year[
+ MEDICAID_SAME_YEAR_ENROLLMENT_ENTRY
+ ],
+ "same_year": spending
+ / same_year[MEDICAID_SAME_YEAR_ENROLLMENT_ENTRY],
+ }
+ return out
+
+
+def _medicaid_state_order() -> list[str]:
+ # Florida first: it is the one case where the valuation moves a result.
+ return ["FL", *[state for state in STATES if state != "FL"]]
+
+
+def medicaid_valuation_text(per_enrollee: dict[str, dict[str, Any]]) -> str:
+ """:data:`MEDICAID_VALUATION`, then each state's same-year ratio."""
+
+ parts = [
+ f"${_money(bridge.to_cents(per_enrollee[state]['same_year']))} in "
+ f"{STATES[state]['name']} (not "
+ f"${_money(bridge.to_cents(per_enrollee[state]['release']))})"
+ for state in _medicaid_state_order()
+ ]
+ return (
+ MEDICAID_VALUATION
+ + " The years do not match: with the release's 2023 enrollment "
+ "entry, the spending's own year, it would be "
+ + ", ".join(parts[:-1])
+ + " and "
+ + parts[-1]
+ + "."
+ )
+
+
+def check_medicaid_memo(
+ rows: list[dict[str, Any]], per_enrollee: dict[str, dict[str, Any]]
+) -> None:
+ """Medicaid at cost in each run is the ratio the text describes.
+
+ A differential check: wherever she is enrolled, the value
+ PolicyEngine-US computed (float32) must equal the release's spending
+ over enrollment within 50 cents. It checks the memo of the default
+ runs and the ``medicaid_cost`` leaf of the with-health decomposition,
+ the figure the with-health column counts.
+ """
+
+ for row in rows:
+ expected = per_enrollee[row["state"]]["release"]
+ where = f"{row['household']}-{row['state']}"
+ costs = [
+ (f"{scenario} memo", row["memo"][scenario]["medicaid_cost"])
+ for scenario in SCENARIOS
+ ]
+ for component in row["with_health"].components:
+ if component.variable == "medicaid_cost":
+ costs += [
+ ("with-health baseline", component.baseline_cents / 100),
+ ("with-health reform", component.reform_cents / 100),
+ ]
+ for label, cost in costs:
+ _require(
+ cost == 0 or abs(cost - expected) < 0.5,
+ f"{where} {label}: Medicaid at cost is {cost}, not spending "
+ f"over enrollment ({expected})",
+ )
+
+
+def _no_absolute_paths(value: Any, where: str = "document") -> None:
+ """Refuse an absolute or home path anywhere in the output."""
+
+ if isinstance(value, dict):
+ for key, item in value.items():
+ _no_absolute_paths(item, f"{where}.{key}")
+ elif isinstance(value, (list, tuple)):
+ for index, item in enumerate(value):
+ _no_absolute_paths(item, f"{where}[{index}]")
+ elif isinstance(value, str):
+ if value.startswith(("/", "~")) or "/Users/" in value:
+ raise ValueError(f"{where} holds a local path: {value}")
+
+
+# ---------------------------------------------------------------------------
+# The Microcosm side: the worker's PIA, the minimum and the COLAs
+# ---------------------------------------------------------------------------
+def cola_rates(
+ payment_year: int, parameter_root: Path
+) -> tuple[dict[int, float], dict[str, Any]]:
+ """Determination-year COLAs through ``payment_year - 1``.
+
+ 1979-2022 from the committed SSA history
+ (``estimates.parameters.load_cola_history``); 2023 onward derived from
+ the release's third-quarter CPI-W averages by
+ :func:`bridge.cola_rates_from_cpi_w`. The two sources overlap in 2022
+ and must agree there.
+ """
+
+ if payment_year > LAST_ACTUAL_CPI_W_ENTRY:
+ raise ValueError(
+ f"payment year {payment_year} needs a COLA determined after the "
+ "last actual CPI-W average in this release"
+ )
+ history = load_cola_history()
+ relative = PARAMETER_PREFIX + "gov/ssa/uprating.yaml"
+ uprating_path = parameter_root / relative
+ cpi_w = {
+ year: value
+ for year, value in _yaml_year_values(uprating_path).items()
+ if year <= LAST_ACTUAL_CPI_W_ENTRY
+ }
+ derived = bridge.cola_rates_from_cpi_w(cpi_w)
+ overlap = sorted(set(derived) & set(history))
+ if not overlap:
+ raise ValueError("the CPI-W series does not overlap the history")
+ for year in overlap:
+ if not math.isclose(derived[year], history[year], abs_tol=1e-12):
+ raise ValueError(
+ f"COLA {year}: CPI-W gives {derived[year]}, the committed "
+ f"history {history[year]}"
+ )
+ rates = {int(year): float(history[year]) for year in history}
+ for year, rate in derived.items():
+ rates.setdefault(year, rate)
+ missing = [year for year in range(1979, payment_year) if year not in rates]
+ if missing:
+ raise ValueError(f"no COLA for determination years {missing}")
+ history_provenance = dict(history.provenance)
+ if "path" in history_provenance:
+ history_provenance["path"] = _repo_relative(history_provenance["path"])
+ provenance = {
+ "history": history_provenance,
+ "history_years": [min(history), max(history)],
+ "cpi_w_path": relative,
+ "cpi_w_path_is_relative_to": "the installed policyengine-us release",
+ "cpi_w_sha256": _sha256(uprating_path),
+ "cpi_w_third_quarter_averages": {
+ str(year): value for year, value in sorted(cpi_w.items())
+ },
+ "derived_rates": {
+ str(year): rate for year, rate in sorted(derived.items())
+ },
+ "overlap_checked": overlap,
+ "rule": (
+ "42 USC 415(i)(1)(D): the percentage by which the third-quarter "
+ "CPI-W exceeds that of the last cost-of-living computation "
+ "quarter, rounded to the nearest one-tenth of 1 percent"
+ ),
+ }
+ return rates, provenance
+
+
+def worker_benefits(payment_year: int, parameter_root: Path) -> dict[str, Any]:
+ """The worker's PIAs under current law and option 2, carried forward."""
+
+ params = load_ssa_parameters(parameter_root)
+ qc = coverage.load_qc_amounts()
+ thresholds = rules.load_aged_thresholds()
+ rates, cola_provenance = cola_rates(payment_year, parameter_root)
+
+ birth = WORKER["birth_year"]
+ first = WORKER["first_earnings_year"]
+ share = WORKER["share_of_average_wage_index"]
+ history = {
+ year: round(share * params.nawi[year], 2)
+ for year in range(first, first + WORKER["earnings_years"])
+ }
+ policy = TrackMPolicy()
+ years = rules.record_years(
+ basis=rules.BASIS_OLD_AGE,
+ birth_year=birth,
+ window_year=WORKER["entitlement_year"],
+ )
+ count = coverage.count_coverage_years(
+ history,
+ birth_year=birth,
+ through_year=years.last_year,
+ qc=qc,
+ nawi=params.nawi,
+ policy=policy,
+ gap_years=(),
+ )
+ pia_record = rules.history_pia(
+ history,
+ birth_year=birth,
+ params=params,
+ basis=rules.BASIS_OLD_AGE,
+ window_year=years.window_year,
+ policy=policy,
+ )
+ inputs = rules.WorkerInputs(
+ pia=pia_record.pia,
+ work_years=count.years,
+ first_pia_year=years.window_year,
+ threshold_year=years.threshold_year,
+ birth_year=birth,
+ )
+ option_number = HEADLINE_CELL[0]
+ option = OPTIONS[option_number]
+ outcomes = {}
+ for row in ("MS0", "MS1", "MS4"):
+ outcomes[row] = rules.evaluate_worker(
+ inputs,
+ option_number,
+ thresholds=thresholds,
+ nawi=params.nawi,
+ policy=policy_for_row(row),
+ )
+ headline = outcomes["MS0"]
+ for row, outcome in outcomes.items():
+ if outcome != headline:
+ raise AssertionError(
+ f"{row} changes this worker's outcome: the window and "
+ "policy year should not matter for a 2024 entitlement"
+ )
+ if not headline.on_minimum:
+ raise AssertionError(
+ f"the illustrative worker is not on the minimum: {headline}"
+ )
+ option_1 = rules.evaluate_worker(
+ inputs, 1, thresholds=thresholds, nawi=params.nawi, policy=policy
+ )
+ claim = rules.claim_factor(birth, years.window_year, params)
+ last_determination = payment_year - 1
+
+ def carried(pia: float) -> dict[str, Any]:
+ amount, steps = bridge.carry_pia_forward(
+ pia,
+ rates,
+ first_determination_year=years.threshold_year,
+ last_determination_year=last_determination,
+ )
+ monthly = math.floor(amount * claim + 1e-9)
+ return {
+ "pia_at_eligibility": pia,
+ "cola_steps": list(steps),
+ "pia_payment_year": amount,
+ "monthly_benefit": monthly,
+ "annual_benefit": 12 * monthly,
+ }
+
+ current_law = carried(pia_record.pia)
+ reform = carried(headline.option_pia)
+ reduced_current_law = carried(option_1.option_pia)
+ counted = count.as_dict()
+ return {
+ "label": ILLUSTRATIVE_LABEL,
+ "inputs": dict(WORKER),
+ "earnings_history": {
+ str(year): value for year, value in sorted(history.items())
+ },
+ "earnings_rule": (
+ f"{share:.0%} of the national average wage index "
+ "(ss.params.load_ssa_parameters().nawi) in each year, rounded "
+ "to the cent"
+ ),
+ "record_years": years.as_dict(),
+ # The count's PSID bookkeeping (observed, imputed and unobserved
+ # years, the flag) describes survey coverage; this history is
+ # invented and complete by construction, so only the count is kept.
+ "years_of_coverage": {
+ "years": counted["years"],
+ "counted_years": counted["counted_years"],
+ "through_year": counted["through_year"],
+ "note": (
+ "Invented history: covered earnings in every year "
+ f"{first}-{first + WORKER['earnings_years'] - 1} and none "
+ "after, by construction. Each counted year's earnings reach "
+ "four quarters of coverage (coverage.count_coverage_years); "
+ "the PSID observation fields do not apply."
+ ),
+ },
+ "pia_record": pia_record.as_dict(),
+ "threshold": {
+ "year": years.threshold_year,
+ "annual": thresholds.for_year(years.threshold_year),
+ "source": dict(thresholds.source),
+ },
+ "option": option.as_dict(),
+ "schedule_share": option.schedule.share(headline.work_years_star),
+ "outcome": headline.as_dict(),
+ "outcome_rows_checked": sorted(outcomes),
+ "option_1_outcome": option_1.as_dict(),
+ "claim_factor": claim,
+ "months_from_full_retirement_age": (
+ 12 * (years.window_year - birth) - params.fra_months(birth)
+ ),
+ "current_law": current_law,
+ "reform": reform,
+ "reduced_current_law_memo": reduced_current_law,
+ "cola": cola_provenance,
+ "rounding": (
+ "PIA: ss.benefits.pia floors to a dime; each COLA step floors to "
+ "a dime (42 USC 415(i)(2)(A)(ii)); the monthly benefit is the "
+ "PIA times the claim factor rounded down to the dollar (the "
+ "415(g) reading recorded in min_benefit_track_m/policy.py's "
+ "MINIMUM_ROUNDING_DIME note)"
+ ),
+ "parameters": {
+ "ssa": (
+ "policyengine_us/parameters/gov/ssa in the pinned "
+ "policyengine-us release (see provenance.policyengine_us)"
+ ),
+ "quarter_of_coverage": dict(qc.source),
+ },
+ }
+
+
+def reform_name(worker: dict[str, Any], *, short: bool = False) -> str:
+ """The reform in words, from the worker's computed schedule share."""
+
+ share = worker["schedule_share"]
+ years = worker["years_of_coverage"]["years"]
+ number = worker["option"]["number"]
+ text = (
+ f"a minimum benefit set at {share:.0%} of the aged poverty threshold "
+ f"for {years} years of work"
+ )
+ if short:
+ return text
+ return f"{text} (Favreault, Mermin and Steuerle 2006, option {number})"
+
+
+# ---------------------------------------------------------------------------
+# The PolicyEngine-US side
+# ---------------------------------------------------------------------------
+def household(
+ spec: dict[str, Any], state: str, social_security: float, quarters: int
+) -> bridge.BridgeHousehold:
+ person = bridge.BridgePerson(
+ person_id="worker",
+ age=PAYMENT_YEAR - WORKER["birth_year"],
+ social_security_retirement=social_security,
+ taxable_private_pension_income=spec["taxable_private_pension_income"],
+ bank_account_assets=BANK_ACCOUNT_ASSETS,
+ pre_subsidy_rent=12 * MONTHLY_RENT,
+ medicare_quarters_of_coverage=quarters,
+ )
+ return bridge.BridgeHousehold(
+ household_id=f"household_{spec['key'].lower()}_{state.lower()}",
+ state=state,
+ people=(person,),
+ tax_units=(("worker",),),
+ spm_units=(("worker",),),
+ marital_units=(("worker",),),
+ has_heating_cooling_expense=True,
+ takes_up_housing_assistance=False,
+ food_preparation_allowed=True,
+ )
+
+
+def _case_id(key: str, state: str, scenario: str, variant: str) -> str:
+ return f"{key}_{state}_{scenario}_{variant}"
+
+
+def run_households(
+ worker: dict[str, Any],
+ python: str | None,
+ updates: list[dict[str, Any]],
+ *,
+ timeout: float = RUN_TIMEOUT_SECONDS,
+) -> tuple[list[dict[str, Any]], bridge.RunResult]:
+ baseline_ss = float(worker["current_law"]["annual_benefit"])
+ reform_ss = float(worker["reform"]["annual_benefit"])
+ quarters = 4 * int(worker["years_of_coverage"]["years"])
+ cases: list[bridge.RunCase] = []
+ plan: list[dict[str, Any]] = []
+ for spec in HOUSEHOLDS:
+ for state in STATES:
+ entry = {
+ "spec": spec,
+ "state": state,
+ "cases": {},
+ "situations": {},
+ "overrides": {
+ variant: _overrides_for(state, variant, updates)
+ for variant in VARIANTS
+ },
+ }
+ for scenario, amount in zip(
+ SCENARIOS, (baseline_ss, reform_ss), strict=True
+ ):
+ built = household(spec, state, amount, quarters)
+ situation = bridge.to_situation(built, PAYMENT_YEAR)
+ entry["situations"][scenario] = situation
+ for variant in VARIANTS:
+ case_id = _case_id(spec["key"], state, scenario, variant)
+ cases.append(
+ bridge.RunCase(
+ case_id=case_id,
+ situation=situation,
+ expected_state=state,
+ memo=MEMO_VARIABLES,
+ parameter_overrides=entry["overrides"][variant],
+ )
+ )
+ entry["cases"][(scenario, variant)] = case_id
+ plan.append(entry)
+ result = bridge.run_policyengine_us(
+ cases,
+ year=PAYMENT_YEAR,
+ python=python,
+ expected_package_record_digest=PE_US_RELEASE["package_record_digest"],
+ timeout=timeout,
+ )
+ rows = []
+ for entry in plan:
+ runs = {key: result.runs[cid] for key, cid in entry["cases"].items()}
+ decompositions = {
+ variant: bridge.decompose(
+ runs[("baseline", variant)].tree,
+ runs[("baseline", variant)].values,
+ runs[("reform", variant)].values,
+ reform_tree=runs[("reform", variant)].tree,
+ )
+ for variant in VARIANTS
+ }
+ spec = entry["spec"]
+ rows.append(
+ {
+ "household": spec["key"],
+ "label": spec["label"],
+ "state": entry["state"],
+ "baseline_social_security_annual": baseline_ss,
+ "reform_social_security_annual": reform_ss,
+ "decomposition": decompositions["default"],
+ "with_health": decompositions["with_health"],
+ "memo": {
+ scenario: dict(runs[(scenario, "default")].memo)
+ for scenario in SCENARIOS
+ },
+ "overrides": entry["overrides"],
+ "situations": entry["situations"],
+ "tree": runs[("baseline", "default")].tree,
+ "run_values": {
+ variant: {
+ scenario: runs[(scenario, variant)].values
+ for scenario in SCENARIOS
+ }
+ for variant in VARIANTS
+ },
+ }
+ )
+ return rows, result
+
+
+def float32_guard(
+ rows: list[dict[str, Any]],
+ python: str | None,
+ *,
+ timeout: float = RUN_TIMEOUT_SECONDS,
+) -> None:
+ """Trace every changed leaf of every comparison; record what it finds.
+
+ Each comparison (household, state, with and without health coverage)
+ is rerun with PolicyEngine-US's tracer on, and
+ :func:`bridge.uncaused_changes` walks each changed leaf's calculation.
+ Adds ``row["float32_guard"][variant]``: the traced leaves, the leaves
+ that changed by less than $2 (:func:`bridge.small_changes`), and every
+ variable that changed by a cent or more although each variable it read
+ held within float noise (:func:`bridge.uncaused_changes`).
+ """
+
+ cases = []
+ overrides = {}
+ plan = []
+ for row in rows:
+ for variant, decomposition in (
+ ("default", row["decomposition"]),
+ ("with_health", row["with_health"]),
+ ):
+ changed = sorted(
+ {
+ c.variable
+ for c in decomposition.components
+ if c.change_cents
+ }
+ )
+ for scenario in SCENARIOS:
+ case_id = _case_id(
+ row["household"], row["state"], scenario, variant
+ )
+ cases.append((case_id, row["situations"][scenario], changed))
+ overrides[case_id] = row["overrides"][variant]
+ plan.append((row, variant, decomposition, changed))
+ traces = bridge.trace_policyengine_us(
+ cases,
+ year=PAYMENT_YEAR,
+ python=python,
+ parameter_overrides=overrides,
+ expected_package_record_digest=PE_US_RELEASE["package_record_digest"],
+ timeout=timeout,
+ )
+ for row, variant, decomposition, changed in plan:
+ baseline = traces[
+ _case_id(row["household"], row["state"], "baseline", variant)
+ ]
+ reform = traces[
+ _case_id(row["household"], row["state"], "reform", variant)
+ ]
+ # A differential check: each traced leaf, from its traced
+ # simulation, equals its value in the untraced run (one person, so
+ # every unit is one row).
+ for scenario, nodes in zip(SCENARIOS, (baseline, reform), strict=True):
+ untraced = row["run_values"][variant][scenario]
+ for name in changed:
+ traced = math.fsum(nodes[f"{name}@{PAYMENT_YEAR}"].value)
+ if abs(traced - untraced[name]) >= bridge.CENT_TOLERANCE:
+ raise InvariantError(
+ f"{row['household']}-{row['state']} {scenario} "
+ f"{variant}: {name} is {traced} traced and "
+ f"{untraced[name]} untraced"
+ )
+ uncaused = bridge.uncaused_changes(
+ baseline, reform, [f"{name}@{PAYMENT_YEAR}" for name in changed]
+ )
+ row.setdefault("float32_guard", {})[variant] = {
+ "traced_leaves": changed,
+ "traced_nodes": len(set(baseline) | set(reform)),
+ "small_changes": [
+ {"variable": c.variable, "change": c.change_cents / 100}
+ for c in bridge.small_changes(decomposition)
+ ],
+ "uncaused_changes": uncaused,
+ }
+
+
+class InvariantError(AssertionError):
+ """A property of the run failed; nothing is written."""
+
+
+def _require(condition: bool, message: object) -> None:
+ # An explicit raise, not ``assert``: ``python -O`` strips asserts, and
+ # these checks gate what is written.
+ if not condition:
+ raise InvariantError(message)
+
+
+#: Categories whose contribution to net income must not rise when Social
+#: Security rises: the means-tested benefits, the income taxes (a
+#: contribution rises when the tax falls) and the refundable credits.
+WRONG_WAY_CATEGORIES = (
+ "ssi",
+ "snap",
+ "csfp",
+ "state_benefits",
+ "federal_income_tax",
+ "state_income_tax",
+ "federal_refundable_credits",
+ "state_refundable_credits",
+)
+
+
+def check_invariants(rows: list[dict[str, Any]]) -> list[str]:
+ """Properties of this run, checked before anything is written."""
+
+ checks = []
+ for row in rows:
+ tag = f"{row['household']}-{row['state']}"
+ for variant in VARIANTS:
+ d: bridge.Decomposition = (
+ row["decomposition"]
+ if variant == "default"
+ else row["with_health"]
+ )
+ where = f"{tag} ({variant})"
+ total = sum(c.change_cents for c in d.components)
+ _require(total == d.net_change_cents, f"{where}: identity")
+ categories = d.by_category()
+ _require(
+ sum(v["change"] for v in categories.values()) == total,
+ f"{where}: categories",
+ )
+ ss = categories["social_security"]["change"]
+ expected = bridge.to_cents(
+ row["reform_social_security_annual"]
+ - row["baseline_social_security_annual"]
+ )
+ _require(ss == expected, (where, ss, expected))
+ # Each is a contribution to net income: a benefit or credit
+ # that rose, or a tax that fell, would raise it.
+ for name in WRONG_WAY_CATEGORIES:
+ _require(
+ categories[name]["change"] <= 0,
+ f"{where}: {name} rose with Social Security",
+ )
+ _require(
+ abs(d.reported_gap_cents) <= 1,
+ f"{where}: household_net_income is off its definition by "
+ f"{d.reported_gap_cents} cents",
+ )
+ guard = row["float32_guard"][variant]
+ _require(
+ not guard["uncaused_changes"],
+ f"{where}: float32 guard: {guard['uncaused_changes']}",
+ )
+ checks.append(
+ "every household-state pair, with and without health coverage: the "
+ "leaf changes sum exactly (in cents) to the net change, and the "
+ "categories to the same total"
+ )
+ checks.append(
+ "the Social Security component equals the Microcosm benefit change"
+ )
+ checks.append(
+ "when Social Security rises, SSI, SNAP, the Commodity Supplemental "
+ "Food Program, state benefits and federal and state refundable tax "
+ "credits never rise, and federal and state income taxes never fall"
+ )
+ checks.append(
+ "PolicyEngine-US's own household_net_income change is within one "
+ "cent of the definition's sum"
+ )
+ checks.append(
+ "float32 guard: every changed leaf traced through PolicyEngine-US's "
+ "calculation; no variable changed by a cent or more (allowing one "
+ "float32 step of rounding) while every variable it read held within "
+ "float noise"
+ )
+ return checks
+
+
+# ---------------------------------------------------------------------------
+# Writing
+# ---------------------------------------------------------------------------
+def _money(cents: int, *, signed: bool = False) -> str:
+ """Whole dollars from integer cents; a half-dollar rounds up.
+
+ Rounding is on the magnitude, in integer arithmetic, so +1,886.50
+ shows as +1,887 and −1,886.50 as −1,887 (Python's ``format`` would
+ round half to even, to 1,886). An amount that rounds to zero shows as
+ ``0``, unsigned.
+ """
+
+ if (
+ isinstance(cents, bool)
+ or not isinstance(cents, numbers.Real)
+ or not float(cents).is_integer()
+ ):
+ raise ValueError(f"cents must be a whole number, not {cents!r}")
+ cents = int(cents)
+ dollars = (abs(cents) + 50) // 100
+ text = f"{dollars:,}"
+ if dollars == 0:
+ return "0"
+ if cents < 0:
+ return f"−{text}"
+ return f"+{text}" if signed else text
+
+
+def table_categories(rows: list[dict[str, Any]]) -> list[str]:
+ """Categories nonzero, at baseline or reform, in any of the nine cases.
+
+ Every table shows the same rows, zeros included, so a program or tax
+ that one state has and another lacks reads as a visible zero.
+ """
+
+ present: set[str] = set()
+ for row in rows:
+ categories = row["decomposition"].by_category()
+ present.update(
+ name
+ for name, entry in categories.items()
+ if entry["baseline"] or entry["reform"]
+ )
+ return [name for name in bridge.CATEGORY_ORDER if name in present]
+
+
+def chart_categories(rows: list[dict[str, Any]]) -> list[str]:
+ """Categories that change in any of the nine cases, for every chart.
+
+ Every chart shows the same rows, zeros included; a category that
+ changes in none of the nine cases is on no chart.
+ """
+
+ changed: set[str] = set()
+ for row in rows:
+ categories = row["decomposition"].by_category()
+ changed.update(
+ name for name, entry in categories.items() if entry["change"]
+ )
+ return [name for name in bridge.CATEGORY_ORDER if name in changed]
+
+
+#: Shorter row labels for the charts (the tables keep the full labels).
+CHART_LABELS = {
+ **bridge.CATEGORY_LABELS,
+ "state_benefits": "State benefits",
+ "csfp": "Commodity food program",
+ "federal_income_tax": "Federal income tax",
+ "federal_refundable_credits": "Federal refundable credits",
+ "state_income_tax": "State income tax",
+ "state_refundable_credits": "State refundable credits",
+ "other_taxes": "Other taxes",
+ "health_net": "Health, net",
+}
+HEALTH_ROW_LABEL = "Net, with health coverage*"
+
+
+def _qmb_months(memo: dict[str, float]) -> int:
+ """Months of the payment year in which she is QMB-eligible."""
+
+ return round(sum(memo[name] for name in QMB_MONTHS))
+
+
+def medicaid_explanations(
+ rows: list[dict[str, Any]], limits: dict[str, float]
+) -> dict[tuple[str, str], str]:
+ """Why net income with health coverage differs, per affected case.
+
+ Only one mechanism is described: Medicaid eligibility ending as the
+ optional senior pathway's countable income passes its limit. Any other
+ difference is refused, so no case gets a text that does not fit it.
+ """
+
+ out = {}
+ for row in rows:
+ if row["with_health"].net_change_cents == (
+ row["decomposition"].net_change_cents
+ ):
+ continue
+ before, after = row["memo"]["baseline"], row["memo"]["reform"]
+ income_key = "medicaid_optional_senior_or_disabled_countable_income"
+ limit_key = "medicaid_optional_senior_or_disabled_income_limit"
+ pattern = (
+ before["is_medicaid_eligible"] == 1
+ and after["is_medicaid_eligible"] == 0
+ and before[income_key] <= before[limit_key]
+ and after[income_key] > after[limit_key]
+ )
+ if not pattern:
+ raise InvariantError(
+ f"{row['household']}-{row['state']}: net income with health "
+ "coverage changes for a reason the text does not describe"
+ )
+ share = limits[row["state"]]
+ msp_appears = before["msp_cost"] == 0 and after["msp_cost"] > 0
+ qmb_text = (
+ " She is QMB-eligible in every month of both runs, and "
+ "PolicyEngine-US counts the Medicare Savings Program only once "
+ "full Medicaid ends (msp_cost.py:28), so that benefit appears in "
+ "the reform."
+ if _qmb_months(before) == 12
+ and _qmb_months(after) == 12
+ and msp_appears
+ else ""
+ )
+ out[(row["household"], row["state"])] = (
+ f"In {STATES[row['state']]['name']}, the increase raises her "
+ "countable income for the optional aged Medicaid pathway from "
+ f"${_money(bridge.to_cents(before[income_key]))} to "
+ f"${_money(bridge.to_cents(after[income_key]))}, past the limit "
+ f"of {share:.0%} of the poverty guideline "
+ f"(${_money(bridge.to_cents(after[limit_key]))}), so Medicaid "
+ "ends (PolicyEngine-US 2.18.0, "
+ "is_optional_senior_or_disabled_income_eligible.py:23-32; "
+ f"{MEDICAID_LIMIT_FILE.removeprefix('policyengine_us/')}:44-45)."
+ + qmb_text
+ )
+ return out
+
+
+def markdown(rows: list[dict[str, Any]], document: dict[str, Any]) -> str:
+ provenance = document["provenance"]
+ release = provenance["policyengine_us"]["release"]
+ installed = provenance["policyengine_us"]["installed"]
+ record = provenance["policyengine_us"]["record_check"]
+ lines = [
+ "# The minimum benefit after taxes and transfers: sample households",
+ "",
+ f"**{ILLUSTRATIVE_LABEL}.** Three households built around one "
+ "illustrative worker, run through PolicyEngine-US for payment year "
+ f"{PAYMENT_YEAR}. Amounts are annual 2026 dollars. Taxes and costs "
+ "enter as negative contributions, so each column sums to net income "
+ "(PolicyEngine-US's `household_net_income`, which by default "
+ "excludes health coverage). Each amount is rounded to the dollar on "
+ "its own, with half-dollars rounded up in magnitude, so a total can "
+ "differ by a dollar from the sum of its rounded parts.",
+ "",
+ f"- Microcosm Dynamics commit `{provenance['microcosm_dynamics']['commit']}`",
+ f"- PolicyEngine-US {release['version']} from PyPI (wheel SHA-256 "
+ f"`{release['wheel_sha256']}`), with policyengine-core "
+ f"{installed['policyengine_core_version']}. All "
+ f"{record['files_checked']:,} installed `policyengine_us/` files "
+ "match the wheel's RECORD.",
+ f"- Reform: {document['reform']}, compared with current-law "
+ "scheduled benefits",
+ f"- Social Security: {_money(bridge.to_cents(rows[0]['baseline_social_security_annual']))} "
+ f"a year under current law, "
+ f"{_money(bridge.to_cents(rows[0]['reform_social_security_annual']))} "
+ "under the reform",
+ ]
+ for update in provenance["policyengine_us"]["parameter_updates"]:
+ source = update["source"]
+ lines.append(
+ f"- Parameter update ({', '.join(update['states'])}): "
+ f"`{update['parameter']}` is set to {update['value']:,.2f} "
+ f"{update['unit']} for {update['year']}, from {source['publisher']}, "
+ f"\"{source['title']}\" ({source['revision']}, effective "
+ f"{source['effective']}; {source['url']}, retrieved "
+ f"{source['retrieved']}). PolicyEngine-US {release['version']} "
+ f"has no {update['year']} entry and would hold "
+ f"{update['installed_value']:,.2f} from "
+ f"{update['installed_value_dated']}."
+ )
+ lines += [
+ "",
+ "## Summary",
+ "",
+ "| Household | State | Social Security | Net income | Share of the "
+ "benefit increase kept | Net income with health coverage |",
+ "|---|---|---:|---:|---:|---:|",
+ ]
+ for row in rows:
+ d = row["decomposition"]
+ ss = d.by_category()["social_security"]["change"]
+ share = d.net_change_cents / ss if ss else float("nan")
+ lines.append(
+ f"| {row['household']} | {row['state']} | "
+ f"{_money(ss, signed=True)} | "
+ f"{_money(d.net_change_cents, signed=True)} | {share:.0%} | "
+ f"{_money(row['with_health'].net_change_cents, signed=True)} |"
+ )
+ lines += ["", document["medicaid_valuation"]["text"], ""]
+ names = table_categories(rows)
+ for spec in HOUSEHOLDS:
+ key = spec["key"]
+ lines += [f"## {spec['label']}", "", spec["description"] + ".", ""]
+ for row in rows:
+ if row["household"] != key:
+ continue
+ d = row["decomposition"]
+ categories = d.by_category()
+ lines += [
+ f"### {spec['label']}, {STATES[row['state']]['name']}",
+ "",
+ "| Component | Baseline | Reform | Change |",
+ "|---|---:|---:|---:|",
+ ]
+ for name in names:
+ entry = categories[name]
+ lines.append(
+ f"| {bridge.CATEGORY_LABELS[name]} | "
+ f"{_money(entry['baseline'])} | "
+ f"{_money(entry['reform'])} | "
+ f"{_money(entry['change'], signed=True)} |"
+ )
+ lines.append(
+ f"| **Net income** | **{_money(d.baseline_net_cents)}** | "
+ f"**{_money(d.reform_net_cents)}** | "
+ f"**{_money(d.net_change_cents, signed=True)}** |"
+ )
+ health = row["with_health"]
+ memo_b = row["memo"]["baseline"]
+ memo_r = row["memo"]["reform"]
+ months = (_qmb_months(memo_b), _qmb_months(memo_r))
+ qmb_text = {
+ (12, 12): "QMB-eligible in every month of both runs",
+ (0, 0): "not QMB-eligible in any month of either run",
+ }.get(
+ months,
+ f"QMB-eligible {months[0]} months at baseline and "
+ f"{months[1]} in the reform",
+ )
+ lines += [
+ "",
+ "Memo (outside default net income): Medicaid at cost "
+ f"{_money(bridge.to_cents(memo_b['medicaid_cost']))} → "
+ f"{_money(bridge.to_cents(memo_r['medicaid_cost']))}; "
+ "Medicare Savings Program "
+ f"{_money(bridge.to_cents(memo_b['msp_cost']))} → "
+ f"{_money(bridge.to_cents(memo_r['msp_cost']))} "
+ f"({qmb_text}; counted only without full Medicaid). "
+ "Net income with health coverage counted changes by "
+ f"{_money(health.net_change_cents, signed=True)}.",
+ "",
+ ]
+ explanations = document["medicaid_explanations"]
+ if explanations:
+ lines += ["## Health coverage", ""]
+ lines += [f"- {text}" for text in explanations.values()]
+ lines.append("")
+ guard = document["float32_guard"]
+ lines += [
+ "## Float32 guard",
+ "",
+ guard["summary"],
+ "",
+ "## Notes",
+ "",
+ *[f"- {note}" for note in document["caveats"]],
+ "",
+ ]
+ return "\n".join(lines)
+
+
+def _serialize_row(row: dict[str, Any]) -> dict[str, Any]:
+ return {
+ "household": row["household"],
+ "label": row["label"],
+ "state": row["state"],
+ "baseline_social_security_annual": row[
+ "baseline_social_security_annual"
+ ],
+ "reform_social_security_annual": row["reform_social_security_annual"],
+ "parameter_overrides": row["overrides"],
+ "decomposition": row["decomposition"].as_dict(),
+ "with_health_benefits_in_net_income": {
+ "net_change": row["with_health"].net_change_cents / 100,
+ "baseline_net_income": row["with_health"].baseline_net_cents / 100,
+ "reform_net_income": row["with_health"].reform_net_cents / 100,
+ "categories": {
+ name: {k: v / 100 for k, v in entry.items()}
+ for name, entry in row["with_health"].by_category().items()
+ },
+ },
+ "memo": row["memo"],
+ "float32_guard": row["float32_guard"],
+ "situations": row["situations"],
+ }
+
+
+# ---------------------------------------------------------------------------
+# Charts
+# ---------------------------------------------------------------------------
+#: Colors from the dataviz skill's reference palette (diverging poles and
+#: text inks, light surface).
+POSITIVE = "#2a78d6"
+NEGATIVE = "#e34948"
+NET = "#52514e"
+INK = "#0b0b0b"
+MUTED = "#52514e"
+GRID = "#e4e3df"
+SURFACE = "#fcfcfb"
+
+
+def chart_footnotes(
+ spec: dict[str, Any], document: dict[str, Any]
+) -> list[str]:
+ """The footnote paragraphs under a household's chart."""
+
+ provenance = document["provenance"]
+ pe = provenance["policyengine_us"]
+ florida = document["medicaid_valuation"]["per_enrollee"]["FL"]
+ notes = [
+ "Blue raises net income and red lowers it; the gray bars are net "
+ "changes. Each amount is rounded to the dollar on its own. *Health "
+ "coverage counted: Medicaid valued at the state's 2023 Medicaid "
+ "spending over its October 2024 Medicaid and CHIP enrollment, all "
+ "ages. That could overstate or understate an aged enrollee's cost; "
+ "with 2023 enrollment, Florida's "
+ f"${_money(bridge.to_cents(florida['release']))} would be "
+ f"${_money(bridge.to_cents(florida['same_year']))}. It is coverage "
+ "at average program cost, not a cash loss.",
+ ]
+ for (key, _state), text in document["medicaid_explanations"].items():
+ if key == spec["key"]:
+ notes.append(text)
+ for update in pe["parameter_updates"]:
+ notes.append(
+ f"{STATES[update['states'][0]]['name']}: the 2026 SSI payment "
+ f"standard is set to the published ${update['value']:,.2f} a "
+ f"month ({update['source']['publisher']}, "
+ f"{update['source']['title']}, {update['source']['revision']}); "
+ f"PolicyEngine-US {pe['release']['version']} holds "
+ f"${update['installed_value']:,.2f} from "
+ f"{update['installed_value_dated'][:4]}."
+ )
+ notes.append(
+ f"PolicyEngine-US {pe['release']['version']} (PyPI); Microcosm "
+ f"Dynamics {provenance['microcosm_dynamics']['commit'][:10]}."
+ )
+ return notes
+
+
+def draw_chart(
+ rows: list[dict[str, Any]],
+ spec: dict[str, Any],
+ document: dict[str, Any],
+ stem: Path,
+) -> list[Path]:
+ import matplotlib
+
+ matplotlib.use("Agg")
+ import matplotlib.pyplot as plt
+
+ matplotlib.rcParams["svg.hashsalt"] = "pe-us-bridge"
+ matplotlib.rcParams["font.family"] = "DejaVu Sans"
+ # Dollar signs are text, not mathtext delimiters.
+ matplotlib.rcParams["text.parse_math"] = False
+ matplotlib.rcParams["hatch.color"] = SURFACE
+ key = spec["key"]
+ own = [row for row in rows if row["household"] == key]
+ names = chart_categories(rows)
+ labels = (
+ [CHART_LABELS[name] for name in names]
+ + ["Net income"]
+ + [HEALTH_ROW_LABEL]
+ )
+ changes_by_state = {}
+ for row in own:
+ categories = row["decomposition"].by_category()
+ changes_by_state[row["state"]] = (
+ [categories[name]["change"] / 100 for name in names]
+ + [row["decomposition"].net_change_cents / 100]
+ + [row["with_health"].net_change_cents / 100]
+ )
+ extent = max(
+ max(abs(v) for v in values) for values in changes_by_state.values()
+ )
+ running_extent = 0.0
+ for values in changes_by_state.values():
+ cumulative = 0.0
+ for value in values[:-2]:
+ cumulative += value
+ running_extent = max(running_extent, abs(cumulative))
+ limit = max(extent, running_extent) * 1.5 or 1.0
+
+ width = 4.1 * len(own)
+ footer = [
+ textwrap.fill(note, width=int(width * 16.5))
+ for note in chart_footnotes(spec, document)
+ ]
+ footer_lines = sum(note.count("\n") + 1 for note in footer)
+ footer_height = 0.145 * footer_lines + 0.15
+ plot_height = 0.5 * len(labels) + 0.6
+ top_height = 1.35
+ height = top_height + plot_height + footer_height + 0.45
+ fig, axes = plt.subplots(
+ 1,
+ len(own),
+ figsize=(width, height),
+ sharey=True,
+ facecolor=SURFACE,
+ )
+ axes = list(axes) if len(own) > 1 else [axes]
+ y_positions = list(range(len(labels)))[::-1]
+ net_index = len(labels) - 2
+ health_index = len(labels) - 1
+ for ax, row in zip(axes, own, strict=True):
+ ax.set_facecolor(SURFACE)
+ values = changes_by_state[row["state"]]
+ cumulative = 0.0
+ for index, (value, y) in enumerate(
+ zip(values, y_positions, strict=True)
+ ):
+ is_total = index >= net_index
+ start = 0.0 if is_total else cumulative
+ color = NET if is_total else (POSITIVE if value >= 0 else NEGATIVE)
+ if value != 0:
+ # A thin bar keeps an outline in its own color, so a change
+ # of a few dollars stays visible next to one of thousands.
+ thin = abs(value) < 0.02 * limit
+ ax.barh(
+ y,
+ value,
+ left=start,
+ height=0.6,
+ color=color,
+ alpha=0.55 if index == health_index else 1.0,
+ hatch="///" if index == health_index else None,
+ edgecolor=color if thin else SURFACE,
+ linewidth=1.5,
+ zorder=3,
+ )
+ else:
+ ax.plot(
+ [start, start],
+ [y - 0.3, y + 0.3],
+ color=MUTED,
+ linewidth=1.2,
+ zorder=3,
+ )
+ end = start + value
+ text = _money(round(value * 100), signed=True)
+ offset = limit * 0.03
+ ax.text(
+ end + offset if value >= 0 else end - offset,
+ y,
+ text,
+ va="center",
+ ha="left" if value >= 0 else "right",
+ fontsize=9,
+ color=INK,
+ fontweight="bold" if is_total else "normal",
+ bbox={"facecolor": SURFACE, "edgecolor": "none", "pad": 1.0},
+ zorder=4,
+ )
+ marked = (row["household"], row["state"]) in (
+ document["medicaid_explanations"]
+ )
+ if index == health_index and marked:
+ ax.text(
+ offset,
+ y,
+ "Medicaid ends\n(valued at cost)",
+ va="center",
+ ha="left",
+ fontsize=7.5,
+ style="italic",
+ color=INK,
+ linespacing=1.1,
+ zorder=4,
+ )
+ if not is_total:
+ cumulative = end
+ ax.axvline(0, color=MUTED, linewidth=0.8, zorder=2)
+ ax.set_xlim(-limit, limit)
+ ax.set_title(
+ STATES[row["state"]]["name"],
+ loc="left",
+ fontsize=11,
+ color=INK,
+ fontweight="bold",
+ )
+ ax.grid(axis="x", color=GRID, linewidth=0.6, zorder=0)
+ ax.tick_params(axis="x", labelsize=8, colors=MUTED)
+ ax.tick_params(axis="y", length=0, labelsize=9.5, colors=INK)
+ ax.xaxis.set_major_formatter(
+ matplotlib.ticker.FuncFormatter(
+ lambda v, _: (
+ "$0"
+ if v == 0
+ else (
+ f"−${abs(v) / 1000:,.0f}k"
+ if v < 0
+ else f"${v / 1000:,.0f}k"
+ )
+ )
+ )
+ )
+ ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(1000))
+ for side in ("top", "right", "left"):
+ ax.spines[side].set_visible(False)
+ ax.spines["bottom"].set_color(GRID)
+ ax.axhline(y_positions[net_index] + 0.5, color=GRID, linewidth=0.8)
+ axes[0].set_yticks(y_positions)
+ axes[0].set_yticklabels(labels)
+
+ def from_top(inches: float) -> float:
+ return 1 - inches / height
+
+ fig.text(
+ 0.01,
+ from_top(0.18),
+ f"{spec['label']}: change in 2026 net income from the minimum benefit",
+ ha="left",
+ va="top",
+ fontsize=13.5,
+ fontweight="bold",
+ color=INK,
+ )
+ subtitle = textwrap.fill(
+ f"{ILLUSTRATIVE_LABEL}. {spec['description']}. Reform: "
+ f"{document['reform']}, against current-law benefits; annual "
+ "dollars.",
+ width=int(width * 13.4),
+ )
+ fig.text(
+ 0.01,
+ from_top(0.52),
+ subtitle,
+ ha="left",
+ va="top",
+ fontsize=9,
+ color=MUTED,
+ linespacing=1.4,
+ )
+ fig.text(
+ 0.01,
+ (footer_height + 0.05) / height,
+ "\n".join(footer),
+ ha="left",
+ va="top",
+ fontsize=7.5,
+ color=MUTED,
+ linespacing=1.35,
+ )
+ fig.subplots_adjust(
+ left=0.16,
+ right=0.98,
+ top=from_top(top_height),
+ bottom=(footer_height + 0.45) / height,
+ wspace=0.12,
+ )
+ paths = []
+ for suffix, kwargs in (
+ (".png", {"dpi": 200, "metadata": {"Software": None}}),
+ (".svg", {"metadata": {"Date": None, "Creator": None}}),
+ ):
+ path = stem.with_suffix(suffix)
+ fig.savefig(path, facecolor=SURFACE, **kwargs)
+ paths.append(path)
+ plt.close(fig)
+ return paths
+
+
+# ---------------------------------------------------------------------------
+# Main
+# ---------------------------------------------------------------------------
+def _guard_summary(rows: list[dict[str, Any]]) -> dict[str, Any]:
+ comparisons = 0
+ leaves = 0
+ nodes = 0
+ small = []
+ for row in rows:
+ for variant in VARIANTS:
+ guard = row["float32_guard"][variant]
+ comparisons += 1
+ leaves += len(guard["traced_leaves"])
+ nodes += guard["traced_nodes"]
+ small += [
+ f"{row['household']}-{row['state']} ({variant}): "
+ f"{item['variable']} {item['change']:+.2f}"
+ for item in guard["small_changes"]
+ ]
+ small_text = (
+ "No leaf changed by a nonzero amount under $2."
+ if not small
+ else "Leaves that changed by a nonzero amount under $2, each traced "
+ "to a changed input: " + "; ".join(small) + "."
+ )
+ return {
+ "comparisons": comparisons,
+ "changed_leaves_traced": leaves,
+ "traced_nodes": nodes,
+ "small_changes": small,
+ "uncaused_changes": 0,
+ "summary": (
+ f"Every changed leaf of the {comparisons} comparisons (nine "
+ "household-state pairs, with and without health coverage; "
+ f"{leaves} leaves) was traced through PolicyEngine-US's own "
+ f"calculation ({nodes:,} traced variable-periods). No variable "
+ "changed by a cent or more (from a cent less one float32 step at "
+ "its size, never under half a cent) while every variable it read "
+ "held within float noise (a cent, or four float32 steps at the "
+ "read's size where that is more), the signature of a float32 step "
+ "at a bracket edge. " + small_text
+ ),
+ }
+
+
+def _release_caveats(root: Path) -> list[str]:
+ """Caveats whose amounts are read from the pinned release."""
+
+ fy27 = f"{PAYMENT_YEAR}-10-01"
+ snap = "gov/usda/snap/"
+ allotment = _parameter(
+ root, snap + "max_allotment.yaml", "main", "CONTIGUOUS_US", 1, on=fy27
+ )
+ deduction = _parameter(
+ root,
+ snap + "income/deductions/standard.yaml",
+ "CONTIGUOUS_US",
+ 1,
+ on=fy27,
+ )
+ utility = {
+ state: _parameter(
+ root,
+ snap + "income/deductions/utility/standard/main.yaml",
+ state,
+ on=fy27,
+ )
+ for state in ("CA", "FL", "MT")
+ }
+ csfp = _parameter(
+ root, "gov/usda/csfp/amount.yaml", on=f"{PAYMENT_YEAR}-01-01"
+ )
+ version = PE_US_RELEASE["version"]
+ return [
+ "SNAP for October-December 2026 uses USDA's fiscal-2027 maximum "
+ f"allotment (${allotment:,.0f} a month for one person) and standard "
+ f"deduction (${deduction:,.0f}) as policyengine-us {version} encodes "
+ "them (gov/usda/snap/max_allotment.yaml:33, "
+ "income/deductions/standard.yaml:17). The state standard utility "
+ "allowances have no fiscal-2027 entry there, so those months use "
+ f"the fiscal-2026 amounts (California ${utility['CA']:,.0f}, "
+ f"Florida ${utility['FL']:,.0f} and Montana "
+ f"${utility['MT']:,.0f} a month; "
+ "income/deductions/utility/standard/main.yaml:112, 179 and 387).",
+ f"In policyengine-us {version} SNAP counts California's SSI "
+ "supplement as unearned income "
+ "(gov/usda/snap/income/sources/unearned_spm_unit.yaml:13), so "
+ "losing the supplement lowers the income SNAP counts, and SNAP "
+ "falls by less.",
+ "Take-up is PolicyEngine-US's default: full for SSI, SNAP and "
+ "Medicaid (takes_up_ssi_if_eligible.py:9, "
+ "takes_up_snap_if_eligible.py:9, takes_up_medicaid_if_eligible.py:9"
+ "). The Commodity Supplemental Food Program has no take-up input: "
+ "every eligible person gets USDA's cost per caseload slot, "
+ f"${csfp:,.0f} in {PAYMENT_YEAR} "
+ "(commodity_supplemental_food_program.py:10-11, "
+ "gov/usda/csfp/amount.yaml:11), though the program is "
+ "caseload-limited and serves far fewer people than are eligible. "
+ "Housing assistance is switched off because vouchers are rationed, "
+ "and the household can prepare food at home.",
+ ]
+
+
+def build(
+ python: str | None,
+ *,
+ timeout: float = RUN_TIMEOUT_SECONDS,
+) -> tuple[dict[str, Any], list[dict[str, Any]], dict[tuple[str, str], str]]:
+ installation, pe_provenance = pinned_release(python)
+ parameter_root = Path(installation.location)
+ updates = parameter_updates(parameter_root)
+ worker = worker_benefits(PAYMENT_YEAR, parameter_root)
+ rows, result = run_households(worker, python, updates, timeout=timeout)
+ if result.installation != installation:
+ raise ValueError("policyengine-us changed during the run")
+ float32_guard(rows, python, timeout=timeout)
+ checks = check_invariants(rows)
+ per_enrollee = medicaid_per_enrollee(parameter_root)
+ check_medicaid_memo(rows, per_enrollee)
+ checks.append(
+ "Medicaid at cost, wherever she is enrolled (the memo and the "
+ "with-health medicaid_cost leaf), equals the state's spending over "
+ "enrollment that the valuation text describes"
+ )
+ valuation = medicaid_valuation_text(per_enrollee)
+ limits_document = yaml.safe_load(
+ (parameter_root / MEDICAID_LIMIT_FILE).read_text()
+ )
+ limits = {
+ state: _dated_value(limits_document[state], f"{PAYMENT_YEAR}-01-01")
+ for state in STATES
+ }
+ explanations = medicaid_explanations(rows, limits)
+ code_paths = ["src", "scripts", "tests"]
+ pe_provenance["parameter_updates"] = [dict(update) for update in updates]
+ provenance = {
+ "microcosm_dynamics": {
+ "repository": "PolicyEngine/microcosm-dynamics",
+ "commit": _git(ROOT, "rev-parse", "HEAD"),
+ "code_dirty": bool(
+ _git(ROOT, "status", "--porcelain", "--", *code_paths)
+ ),
+ "script": "scripts/pe_us_minimum_benefit_sample_households.py",
+ "bridge": "src/populace_dynamics/bridge/policyengine_us.py",
+ },
+ "policyengine_us": pe_provenance,
+ "payment_year": PAYMENT_YEAR,
+ }
+ caveats = [
+ "Illustrative households, not survey data: one worker, chosen so "
+ "the minimum binds, in three living situations.",
+ "The bridge passes Microcosm's Social Security amounts to "
+ "PolicyEngine-US as inputs; it does not run over the projected "
+ "population and applies no behavioral response.",
+ f"The reform is {reform_name(worker)}: exercise 4's headline "
+ "option, the standard price-indexed minimum with its printed 12.81 "
+ "percent uniform cut for new entitlees. The worker is on the "
+ "minimum, so the cut does not reach her; a worker above the minimum "
+ "would lose 12.81 percent.",
+ "The baseline is current-law scheduled benefits. Against option 1 "
+ "(reduced current law, the Report's own benchmark) her gain would "
+ "be larger; that PIA is in the JSON as a memo, not run through "
+ "PolicyEngine-US.",
+ "Net income is PolicyEngine-US's household_net_income, which by "
+ "default excludes health coverage (Medicaid at cost, Medicare "
+ "Savings Programs); the with-health sensitivity and the memo lines "
+ "report it. " + valuation,
+ *_release_caveats(parameter_root),
+ *[
+ f"{STATES[update['states'][0]]['name']}'s {update['year']} aged "
+ "or disabled payment standard is set to the published "
+ f"${update['value']:,.2f} a month ({update['source']['publisher']}"
+ f", {update['source']['title']}, {update['source']['revision']})"
+ f"; policyengine-us {PE_US_RELEASE['version']} has no "
+ f"{update['year']} entry and would hold "
+ f"${update['installed_value']:,.2f} from "
+ f"{update['installed_value_dated']}."
+ for update in updates
+ ],
+ "COLAs for 2023-2025 are derived from policyengine-us 2.18.0's "
+ "third-quarter CPI-W averages under 42 USC 415(i)(1)(D); the "
+ "derived 2022 COLA equals the committed SSA history's.",
+ "Household C's pension is $31,200, clear of every bracket and "
+ "eligibility edge on her path. At a round $30,000, California AGI "
+ "sat on the $30,000 edge of the use-tax table and float32 noise "
+ "moved the use tax by $1; the float32 guard now refuses such a run.",
+ ]
+ reform = reform_name(worker)
+ guard = _guard_summary(rows)
+ document = {
+ "schema_version": SCHEMA_VERSION,
+ "label": ILLUSTRATIVE_LABEL,
+ "reform": reform,
+ "provenance": provenance,
+ "worker": worker,
+ "households": [dict(spec) for spec in HOUSEHOLDS],
+ "states": STATES,
+ "common_inputs": {
+ "monthly_rent": MONTHLY_RENT,
+ "bank_account_assets": BANK_ACCOUNT_ASSETS,
+ "has_heating_cooling_expense": True,
+ "takes_up_housing_assistance_if_eligible": False,
+ "living_arrangements_allow_for_food_preparation": True,
+ "medicare_quarters_of_coverage": 4
+ * worker["years_of_coverage"]["years"],
+ },
+ "net_income_tree": {
+ name: [list(part) for part in parts]
+ for name, parts in rows[0]["tree"].children.items()
+ },
+ "table_rows": table_categories(rows),
+ "chart_rows": chart_categories(rows),
+ "results": [_serialize_row(row) for row in rows],
+ "medicaid_valuation": {
+ "text": valuation,
+ "per_enrollee": per_enrollee,
+ "files": [
+ PARAMETER_PREFIX + MEDICAID_TOTALS + name
+ for name in ("spending.yaml", "enrollment.yaml")
+ ],
+ },
+ "medicaid_explanations": {
+ f"{key}-{state}": text
+ for (key, state), text in explanations.items()
+ },
+ "float32_guard": guard,
+ "invariants_checked": checks,
+ "caveats": caveats,
+ }
+ _no_absolute_paths(document)
+ return document, rows, explanations
+
+
+def write(
+ document: dict[str, Any],
+ rows: list[dict[str, Any]],
+ explanations: dict[tuple[str, str], str],
+ directory: Path,
+) -> list[Path]:
+ directory.mkdir(parents=True, exist_ok=True)
+ written = []
+ json_path = directory / f"{OUTPUT_STEM}.json"
+ json_path.write_text(
+ json.dumps(document, indent=2, sort_keys=False) + "\n"
+ )
+ written.append(json_path)
+ view = dict(document, medicaid_explanations=explanations)
+ md_path = directory / f"{OUTPUT_STEM}.md"
+ md_path.write_text(markdown(rows, view))
+ written.append(md_path)
+ for spec in HOUSEHOLDS:
+ stem = directory / f"household_{spec['key'].lower()}_waterfall"
+ written += draw_chart(rows, spec, view, stem)
+ return written
+
+
+def main(argv: list[str] | None = None) -> int:
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
+ parser.add_argument("--out-dir", type=Path, default=DEFAULT_OUT_DIR)
+ parser.add_argument("--docs-dir", type=Path, default=DEFAULT_DOCS_DIR)
+ parser.add_argument(
+ "--pe-us-python",
+ default=None,
+ help=f"interpreter with policyengine-us (else ${bridge.PE_US_PYTHON_ENV})",
+ )
+ parser.add_argument(
+ "--timeout",
+ type=float,
+ default=RUN_TIMEOUT_SECONDS,
+ help="seconds each policyengine-us child may take",
+ )
+ args = parser.parse_args(argv)
+ document, rows, explanations = build(
+ args.pe_us_python, timeout=args.timeout
+ )
+ for directory in (args.out_dir, args.docs_dir):
+ for path in write(document, rows, explanations, directory):
+ print(path)
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/src/populace_dynamics/bridge/__init__.py b/src/populace_dynamics/bridge/__init__.py
new file mode 100644
index 000000000..9afec1770
--- /dev/null
+++ b/src/populace_dynamics/bridge/__init__.py
@@ -0,0 +1,8 @@
+"""Bridges from Microcosm Dynamics to external tax-benefit models.
+
+:mod:`populace_dynamics.bridge.policyengine_us` passes Microcosm benefit
+amounts to PolicyEngine-US as household inputs and decomposes the change
+in PolicyEngine-US's ``household_net_income``. The package is opt-in: the
+historical projection never imports it, and it adds no dependency on
+``policyengine-us`` (that model runs in a separate interpreter).
+"""
diff --git a/src/populace_dynamics/bridge/policyengine_us.py b/src/populace_dynamics/bridge/policyengine_us.py
new file mode 100644
index 000000000..f675d5c7a
--- /dev/null
+++ b/src/populace_dynamics/bridge/policyengine_us.py
@@ -0,0 +1,2126 @@
+"""Bridge Microcosm benefit amounts to PolicyEngine-US's tax-benefit model.
+
+Microcosm computes Social Security amounts (a PIA from an earnings history,
+a reform PIA, statutory COLAs). PolicyEngine-US computes what those
+amounts become after federal and state taxes and means-tested transfers.
+This module joins the two without importing ``policyengine_us``:
+
+1. **Household to situation.** :class:`BridgePerson` and
+ :class:`BridgeHousehold` describe people (age, Social Security by type,
+ earnings, pension and other income, liquid assets, rent) and their
+ tax-unit, SPM-unit, marital-unit, family and household membership.
+ :func:`to_situation` writes a PolicyEngine-US situation dictionary.
+ ``social_security_retirement`` and the other three Social Security
+ variables have no formula in PolicyEngine-US (``variables/gov/ssa/ss/
+ social_security_retirement.py:4-10`` in policyengine-us 2.18.0), so the
+ amounts Microcosm computes enter as inputs and PolicyEngine-US adds them
+ in ``social_security`` (``social_security.py:11-14``).
+2. **Runner.** :func:`run_policyengine_us` sends situations as JSON to a
+ separate interpreter that has ``policyengine-us`` installed (located by
+ ``POPULACE_DYNAMICS_PE_US_PYTHON``, the subprocess discipline of
+ ``scripts/build_aux_benefit_examples.py``). The child reads the
+ component tree of ``household_net_income`` from the model itself and
+ returns every node's household value.
+3. **Source check.** Before running, :func:`inspect_installation` asks the
+ interpreter which ``policyengine-us`` it imports and where it came from,
+ and :func:`check_published_source` refuses a source that is not
+ published: a git checkout whose revision no ``origin`` branch contains,
+ or whose tracked files are modified, and any VCS or archive install.
+ :func:`verify_record` checks the installed files against the
+ distribution's RECORD.
+4. **Decomposition.** :func:`decompose` turns a baseline and a reform run
+ into leaf components whose changes sum exactly (in integer cents) to
+ the change in ``household_net_income`` as its definition computes it,
+ after checking every aggregate against the sum of its parts.
+5. **Float32 guard.** :func:`trace_policyengine_us` reruns cases with
+ PolicyEngine-US's tracer on, and :func:`uncaused_changes` finds every
+ variable that changes between two runs although no variable it read
+ changed by more than float noise (a cent, or four float32 steps at the
+ read's size where that is more): a step at a bracket edge taken on
+ float32 noise, not a mechanism.
+
+``household_net_income`` in policyengine-us 2.18.0
+(``variables/household/income/household/household_net_income.py:10-18``)
+adds ``household_market_income``, ``household_benefits`` and
+``household_refundable_tax_credits`` and subtracts
+``household_tax_before_refundable_credits`` and ``household_health_costs``.
+``household_benefits`` includes ``household_health_benefits`` (Medicaid at
+cost, Medicare Savings Programs, CHIP, premium tax credits), but that
+variable returns zero unless ``gov.simulation.
+include_health_benefits_in_net_income`` is true
+(``household_health_benefits.py:17-22``), and the parameter's default is
+false (``parameters/gov/simulation/
+include_health_benefits_in_net_income.yaml``). So by default net income
+excludes health coverage; a caller can switch it on through
+``RunCase.parameter_overrides``.
+
+What this module does not do: compute any Social Security amount (callers
+pass them), run over a projected population, or edit PolicyEngine-US.
+"""
+
+from __future__ import annotations
+
+import base64
+import csv
+import hashlib
+import io
+import json
+import math
+import os
+import re
+import subprocess
+from collections.abc import Iterable, Mapping, Sequence
+from dataclasses import dataclass, field
+from decimal import ROUND_FLOOR, ROUND_HALF_EVEN, ROUND_HALF_UP, Decimal
+from pathlib import Path
+from typing import Any
+
+__all__ = [
+ "AMOUNT_FIELDS",
+ "AggregateMismatchError",
+ "BridgeHousehold",
+ "BridgePerson",
+ "CATEGORY_LABELS",
+ "CATEGORY_ORDER",
+ "CENT_TOLERANCE",
+ "Component",
+ "ComponentTree",
+ "DEFAULT_EXPAND",
+ "DEFAULT_PE_US_PYTHON",
+ "Decomposition",
+ "DefinitionDriftError",
+ "NET_INCOME_DEFINITION",
+ "PE_US_PYTHON_ENV",
+ "PolicyEngineRun",
+ "PolicyEngineUSInstallation",
+ "PolicyEngineUSUnavailable",
+ "ROOT_VARIABLE",
+ "RunCase",
+ "RunResult",
+ "FLOAT32_NOISE_STEPS",
+ "SMALL_CHANGE_CENTS",
+ "STATE_FIPS",
+ "TraceNode",
+ "UnpublishedSourceError",
+ "carry_pia_forward",
+ "category_for",
+ "check_published_source",
+ "cola_rates_from_cpi_w",
+ "decompose",
+ "float32_step",
+ "floor_to_dime",
+ "inspect_installation",
+ "package_record_digest",
+ "person_inputs_from_situation",
+ "resolve_pe_us_python",
+ "run_policyengine_us",
+ "small_changes",
+ "to_cents",
+ "to_situation",
+ "trace_policyengine_us",
+ "uncaused_changes",
+ "verify_record",
+]
+
+#: The environment variable naming the interpreter that has
+#: ``policyengine-us`` installed (as ``scripts/build_aux_benefit_examples.py``
+#: uses it).
+PE_US_PYTHON_ENV = "POPULACE_DYNAMICS_PE_US_PYTHON"
+#: The fallback when the environment variable is unset: a virtual
+#: environment holding the policyengine-us 2.18.0 release from PyPI
+#: (``uv venv`` then ``uv pip install policyengine-us==2.18.0``).
+DEFAULT_PE_US_PYTHON = Path("~/.venvs/policyengine-us-2.18.0/bin/python")
+
+#: The variable the decomposition explains.
+ROOT_VARIABLE = "household_net_income"
+#: ``household_net_income``'s definition in policyengine-us 2.18.0
+#: (``household_net_income.py:10-18``): the added and subtracted parts, in
+#: order. :func:`decompose` refuses a run whose tree differs (a tripwire for
+#: a PolicyEngine-US change the bridge has not been checked against).
+NET_INCOME_DEFINITION: tuple[tuple[str, int], ...] = (
+ ("household_market_income", 1),
+ ("household_benefits", 1),
+ ("household_refundable_tax_credits", 1),
+ ("household_tax_before_refundable_credits", -1),
+ ("household_health_costs", -1),
+)
+#: The aggregates the runner expands into their parts. Every other variable
+#: is a leaf (``social_security``, ``ssi``, ``snap``, each state benefit,
+#: each tax). ``household_benefits``, ``household_health_benefits`` and
+#: ``household_health_costs`` are formulas; the runner mirrors their lists
+#: (see :data:`_RUNNER_SOURCE`), and :func:`decompose` checks each against
+#: the sum of its parts.
+DEFAULT_EXPAND: tuple[str, ...] = (
+ "household_net_income",
+ "household_market_income",
+ "household_benefits",
+ "household_refundable_tax_credits",
+ "household_tax_before_refundable_credits",
+ "household_health_costs",
+ "household_state_benefits",
+ "household_health_benefits",
+ "household_refundable_state_tax_credits",
+ "household_state_tax_before_refundable_credits",
+)
+
+#: Census state FIPS codes (50 states and DC). The runner returns the state
+#: PolicyEngine-US derives from the code (``state_name``'s formula maps
+#: ``state_fips``; ``input/geography.py:26-87``), and :func:`run_policyengine_us`
+#: refuses a case whose derived state differs from the one requested.
+STATE_FIPS: dict[str, int] = {
+ "AL": 1,
+ "AK": 2,
+ "AZ": 4,
+ "AR": 5,
+ "CA": 6,
+ "CO": 8,
+ "CT": 9,
+ "DE": 10,
+ "DC": 11,
+ "FL": 12,
+ "GA": 13,
+ "HI": 15,
+ "ID": 16,
+ "IL": 17,
+ "IN": 18,
+ "IA": 19,
+ "KS": 20,
+ "KY": 21,
+ "LA": 22,
+ "ME": 23,
+ "MD": 24,
+ "MA": 25,
+ "MI": 26,
+ "MN": 27,
+ "MS": 28,
+ "MO": 29,
+ "MT": 30,
+ "NE": 31,
+ "NV": 32,
+ "NH": 33,
+ "NJ": 34,
+ "NM": 35,
+ "NY": 36,
+ "NC": 37,
+ "ND": 38,
+ "OH": 39,
+ "OK": 40,
+ "OR": 41,
+ "PA": 42,
+ "RI": 44,
+ "SC": 45,
+ "SD": 46,
+ "TN": 47,
+ "TX": 48,
+ "UT": 49,
+ "VT": 50,
+ "VA": 51,
+ "WA": 53,
+ "WV": 54,
+ "WI": 55,
+ "WY": 56,
+}
+
+#: Person amounts the bridge passes, each a PolicyEngine-US input variable
+#: of the same name (annual dollars, or dollars for the asset stock).
+AMOUNT_FIELDS: tuple[str, ...] = (
+ "social_security_retirement",
+ "social_security_disability",
+ "social_security_survivors",
+ "social_security_dependents",
+ "employment_income",
+ "taxable_private_pension_income",
+ "interest_income",
+ "bank_account_assets",
+ "pre_subsidy_rent",
+)
+_MAX_AGE = 125
+
+
+class PolicyEngineUSUnavailable(RuntimeError):
+ """The interpreter with ``policyengine-us`` cannot be found or run."""
+
+
+class AggregateMismatchError(ValueError):
+ """An aggregate differs from the signed sum of its parts."""
+
+
+class DefinitionDriftError(ValueError):
+ """``household_net_income``'s definition is not the one the bridge read."""
+
+
+class UnpublishedSourceError(RuntimeError):
+ """The interpreter imports a ``policyengine-us`` that is not published."""
+
+
+# ---------------------------------------------------------------------------
+# Households and situations
+# ---------------------------------------------------------------------------
+def _check_amount(label: str, value: object) -> float:
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
+ raise TypeError(f"{label} must be a number, not {value!r}")
+ amount = float(value)
+ if not math.isfinite(amount):
+ raise ValueError(f"{label} must be finite, not {value!r}")
+ if amount < 0:
+ raise ValueError(f"{label} must be nonnegative, not {value!r}")
+ return amount
+
+
+@dataclass(frozen=True)
+class BridgePerson:
+ """One person: age and the annual amounts PolicyEngine-US takes as input.
+
+ Every amount is nonnegative and finite. ``medicare_quarters_of_coverage``
+ is PolicyEngine-US's input of that name (it defaults to 40 there,
+ ``medicare_quarters_of_coverage.py:16``); ``None`` leaves the default.
+ """
+
+ person_id: str
+ age: int
+ social_security_retirement: float = 0.0
+ social_security_disability: float = 0.0
+ social_security_survivors: float = 0.0
+ social_security_dependents: float = 0.0
+ employment_income: float = 0.0
+ taxable_private_pension_income: float = 0.0
+ interest_income: float = 0.0
+ bank_account_assets: float = 0.0
+ pre_subsidy_rent: float = 0.0
+ medicare_quarters_of_coverage: int | None = None
+
+ def __post_init__(self) -> None:
+ if not isinstance(self.person_id, str) or not self.person_id:
+ raise ValueError("person_id must be a nonempty string")
+ if isinstance(self.age, bool) or not isinstance(self.age, int):
+ raise TypeError(f"age must be an integer, not {self.age!r}")
+ if not 0 <= self.age <= _MAX_AGE:
+ raise ValueError(f"age must be 0-{_MAX_AGE}, not {self.age}")
+ for name in AMOUNT_FIELDS:
+ object.__setattr__(
+ self, name, _check_amount(name, getattr(self, name))
+ )
+ quarters = self.medicare_quarters_of_coverage
+ if quarters is not None:
+ if isinstance(quarters, bool) or not isinstance(quarters, int):
+ raise TypeError("medicare_quarters_of_coverage must be int")
+ if quarters < 0:
+ raise ValueError("medicare_quarters_of_coverage must be >= 0")
+
+ def amounts(self) -> dict[str, float]:
+ return {name: getattr(self, name) for name in AMOUNT_FIELDS}
+
+
+def _groups(value: Iterable[Iterable[str]], label: str) -> tuple:
+ # A bare string is iterable, so ("ab",) would otherwise become the group
+ # ("a", "b"); a group of people must be a sequence of ids.
+ if isinstance(value, str):
+ raise TypeError(f"{label}: expected groups of ids, not {value!r}")
+ # Read the input once: a generator would be empty the second time.
+ value = tuple(value)
+ for group in value:
+ if isinstance(group, str):
+ raise TypeError(
+ f"{label}: a group must be a sequence of ids, not {group!r}"
+ )
+ groups = tuple(tuple(group) for group in value)
+ for group in groups:
+ if not group:
+ raise ValueError(f"{label}: a group must have a member")
+ for member in group:
+ if not isinstance(member, str):
+ raise TypeError(f"{label}: member {member!r} is not a str")
+ return groups
+
+
+@dataclass(frozen=True)
+class BridgeHousehold:
+ """A household and the units PolicyEngine-US needs.
+
+ ``tax_units``, ``spm_units`` and ``marital_units`` each partition the
+ people: every person is in exactly one group of each kind (a marital
+ unit has one or two people). ``families`` defaults to one family of
+ everyone. ``state`` is a two-letter code in :data:`STATE_FIPS`.
+
+ Three flags set PolicyEngine-US inputs whose defaults would otherwise
+ decide a result:
+
+ * ``has_heating_cooling_expense`` (SPM unit): SNAP's standard utility
+ allowance turns on it (``snap_utility_allowance_type.py:30,40-52`` in
+ policyengine-us 2.18.0). In 2.18.0 the variable has a formula
+ (``has_heating_cooling_expense.py:21-32``); the situation sets it as
+ an input, which takes the formula's place.
+ * ``takes_up_housing_assistance`` (SPM unit,
+ ``takes_up_housing_assistance_if_eligible``): its default is true
+ (``takes_up_housing_assistance_if_eligible.py:9``), which would give
+ every income-eligible renter a housing voucher.
+ * ``food_preparation_allowed`` (household,
+ ``living_arrangements_allow_for_food_preparation``): its default is
+ false, and California's supplement adds a food allowance when it is
+ false (``ca_state_supplement_food_allowance_eligible.py:12-21``).
+ """
+
+ household_id: str
+ state: str
+ people: tuple[BridgePerson, ...]
+ tax_units: tuple[tuple[str, ...], ...]
+ spm_units: tuple[tuple[str, ...], ...]
+ marital_units: tuple[tuple[str, ...], ...]
+ families: tuple[tuple[str, ...], ...] | None = None
+ has_heating_cooling_expense: bool = False
+ takes_up_housing_assistance: bool = False
+ food_preparation_allowed: bool = True
+
+ def __post_init__(self) -> None:
+ if not isinstance(self.household_id, str) or not self.household_id:
+ raise ValueError("household_id must be a nonempty string")
+ if self.state not in STATE_FIPS:
+ raise ValueError(
+ f"state must be a key of STATE_FIPS, not {self.state!r}"
+ )
+ people = tuple(self.people)
+ if not people:
+ raise ValueError("a household needs at least one person")
+ for person in people:
+ if not isinstance(person, BridgePerson):
+ raise TypeError("people must be BridgePerson instances")
+ ids = [person.person_id for person in people]
+ if len(set(ids)) != len(ids):
+ raise ValueError(f"person ids must be unique: {ids}")
+ object.__setattr__(self, "people", people)
+ families = (tuple(ids),) if self.families is None else self.families
+ for label in ("tax_units", "spm_units", "marital_units"):
+ object.__setattr__(
+ self, label, _groups(getattr(self, label), label)
+ )
+ object.__setattr__(self, "families", _groups(families, "families"))
+ for label in ("tax_units", "spm_units", "marital_units", "families"):
+ members = [m for group in getattr(self, label) for m in group]
+ if sorted(members) != sorted(ids):
+ raise ValueError(
+ f"{label} must place every person in exactly one group: "
+ f"people {sorted(ids)}, members {sorted(members)}"
+ )
+ if any(len(group) > 2 for group in self.marital_units):
+ raise ValueError("a marital unit has at most two people")
+ for flag in (
+ "has_heating_cooling_expense",
+ "takes_up_housing_assistance",
+ "food_preparation_allowed",
+ ):
+ if not isinstance(getattr(self, flag), bool):
+ raise TypeError(f"{flag} must be a bool")
+
+
+def _entity_name(household_id: str, kind: str, index: int) -> str:
+ return f"{household_id}_{kind}_{index + 1}"
+
+
+def to_situation(household: BridgeHousehold, year: int) -> dict[str, Any]:
+ """The PolicyEngine-US situation for ``household`` in ``year``.
+
+ Every person carries ``age`` and every :data:`AMOUNT_FIELDS` amount
+ (zeros included, so the inputs round-trip); the household carries
+ ``state_fips`` (PolicyEngine-US derives ``state_name`` and
+ ``state_code`` from it). Deterministic: the same household and year
+ give the same dictionary.
+ """
+
+ if isinstance(year, bool) or not isinstance(year, int):
+ raise TypeError(f"year must be an integer, not {year!r}")
+ period = str(year)
+ people: dict[str, Any] = {}
+ for person in household.people:
+ entry: dict[str, Any] = {"age": {period: person.age}}
+ for name, value in person.amounts().items():
+ entry[name] = {period: value}
+ if person.medicare_quarters_of_coverage is not None:
+ entry["medicare_quarters_of_coverage"] = {
+ period: person.medicare_quarters_of_coverage
+ }
+ people[person.person_id] = entry
+ hid = household.household_id
+ situation: dict[str, Any] = {"people": people}
+ situation["tax_units"] = {
+ _entity_name(hid, "tax_unit", i): {"members": list(group)}
+ for i, group in enumerate(household.tax_units)
+ }
+ situation["spm_units"] = {
+ _entity_name(hid, "spm_unit", i): {
+ "members": list(group),
+ "has_heating_cooling_expense": {
+ period: household.has_heating_cooling_expense
+ },
+ "takes_up_housing_assistance_if_eligible": {
+ period: household.takes_up_housing_assistance
+ },
+ }
+ for i, group in enumerate(household.spm_units)
+ }
+ situation["marital_units"] = {
+ _entity_name(hid, "marital_unit", i): {"members": list(group)}
+ for i, group in enumerate(household.marital_units)
+ }
+ situation["families"] = {
+ _entity_name(hid, "family", i): {"members": list(group)}
+ for i, group in enumerate(household.families or ())
+ }
+ situation["households"] = {
+ hid: {
+ "members": [person.person_id for person in household.people],
+ "state_fips": {period: STATE_FIPS[household.state]},
+ "living_arrangements_allow_for_food_preparation": {
+ period: household.food_preparation_allowed
+ },
+ }
+ }
+ return situation
+
+
+def person_inputs_from_situation(
+ situation: Mapping[str, Any], year: int
+) -> dict[str, dict[str, float]]:
+ """Read back each person's :data:`AMOUNT_FIELDS` from a situation."""
+
+ period = str(year)
+ return {
+ person_id: {
+ name: float(entry[name][period])
+ for name in AMOUNT_FIELDS
+ if name in entry
+ }
+ for person_id, entry in situation["people"].items()
+ }
+
+
+# ---------------------------------------------------------------------------
+# The runner (a separate interpreter with policyengine-us)
+# ---------------------------------------------------------------------------
+#: Shared by every child script: which ``policyengine-us`` the interpreter
+#: imports and where it came from, read with ``importlib.metadata`` and
+#: ``importlib.util.find_spec`` (neither imports the package).
+_INSTALLATION_SOURCE = r"""
+import hashlib
+import importlib.metadata as metadata
+import importlib.util
+import json
+import platform
+import sys
+from pathlib import Path
+
+
+def installation():
+ spec = importlib.util.find_spec("policyengine_us")
+ out = {
+ "python_version": platform.python_version(),
+ "package_dir": (
+ str(Path(spec.origin).resolve().parent)
+ if spec is not None and spec.origin
+ else None
+ ),
+ "version": None,
+ "core_version": None,
+ "location": None,
+ "record_path": None,
+ "record_sha256": None,
+ "direct_url": None,
+ "installer": None,
+ }
+ try:
+ out["core_version"] = metadata.version("policyengine-core")
+ except metadata.PackageNotFoundError:
+ pass
+ try:
+ dist = metadata.distribution("policyengine-us")
+ except metadata.PackageNotFoundError:
+ return out
+ out["version"] = dist.version
+ out["location"] = str(Path(dist.locate_file("")).resolve())
+ for entry in dist.files or ():
+ if entry.name == "RECORD" and entry.parent.name.endswith(".dist-info"):
+ record = Path(dist.locate_file(entry)).resolve()
+ out["record_path"] = str(record)
+ out["record_sha256"] = hashlib.sha256(
+ record.read_bytes()
+ ).hexdigest()
+ direct_url = dist.read_text("direct_url.json")
+ if direct_url:
+ out["direct_url"] = json.loads(direct_url)
+ installer = dist.read_text("INSTALLER")
+ if installer:
+ out["installer"] = installer.strip()
+ return out
+"""
+
+#: :func:`inspect_installation`'s child: the installation, nothing else.
+_INSPECT_SOURCE = (
+ _INSTALLATION_SOURCE + "\njson.dump(installation(), sys.stdout)\n"
+)
+
+#: Shared by the runner and the tracer: one simulation per case. Cases
+#: with the same parameter overrides share one tax-benefit system: the
+#: reform is built once (from a throwaway simulation of the first such
+#: case), and every case, the first included, passes that system as
+#: ``tax_benefit_system`` with no reform, which policyengine-us shares
+#: rather than rebuilds (``spm.py:830-834`` in 2.18.0). So no case gets a
+#: ``baseline`` branch that another lacks, and no case shares a simulation
+#: with another: some 2.18.0 formulas aggregate over a whole simulation's
+#: population (Medicaid's state-average cost index,
+#: ``medicaid_slcsp_state_average_cost_index.py:14-29``), so households
+#: batched into one simulation would not be independent.
+_SIMULATION_SOURCE = r"""
+from policyengine_us import Simulation
+from policyengine_core.reforms import Reform
+
+job = json.load(sys.stdin)
+year = int(job["year"])
+period = str(year)
+_systems = {}
+
+
+def reform_for(overrides):
+ return Reform.from_dict(
+ {
+ path: {f"{year}-01-01.{year}-12-31": value}
+ for path, value in overrides.items()
+ },
+ country_id="us",
+ )
+
+
+def simulation_for(case, trace=False):
+ situation = case["situation"]
+ if len(situation.get("households", {})) != 1:
+ raise ValueError(f"{case['case_id']}: needs exactly one household")
+ overrides = case.get("parameter_overrides") or {}
+ if not overrides:
+ return Simulation(situation=situation, trace=trace)
+ key = json.dumps(overrides, sort_keys=True)
+ if key not in _systems:
+ _systems[key] = Simulation(
+ situation=situation, reform=reform_for(overrides)
+ ).tax_benefit_system
+ return Simulation(
+ situation=situation, tax_benefit_system=_systems[key], trace=trace
+ )
+"""
+
+#: Executed by the policyengine-us interpreter. It reads a job on stdin and
+#: writes results on stdout. For each case it builds the component tree of
+#: the root from the model: a variable's ``adds``/``subtracts`` (a list, or
+#: a parameter path to a list), except three formula variables whose lists
+#: it mirrors from their source in policyengine-us 2.18.0:
+#:
+#: * ``household_benefits`` (``household_benefits.py:11-17``):
+#: ``gov.household.household_benefits``, less ``housing_assistance`` when
+#: ``gov.hud.abolition``;
+#: * ``household_health_benefits`` (``household_health_benefits.py:17-22``)
+#: and ``household_health_costs`` (``household_health_costs.py:16-20``):
+#: their ``gov.household`` lists when
+#: ``gov.simulation.include_health_benefits_in_net_income``, else none.
+#:
+#: A variable to expand with neither ``adds`` nor a mirrored list is an
+#: error. :func:`decompose` checks every aggregate's value against its
+#: parts, so a wrong mirror fails loudly. Each case runs in its own
+#: simulation (see :data:`_SIMULATION_SOURCE`) and must hold exactly one
+#: household. A memo name may carry ``@period`` (for example
+#: ``is_qmb_eligible@2026-01``) to read a monthly variable.
+_RUNNER_SOURCE = _INSTALLATION_SOURCE + _SIMULATION_SOURCE + r"""
+import policyengine_us
+
+expand = set(job["expand"])
+root = job["root"]
+
+
+def parameter_list(params, path):
+ node = params
+ for part in path.split("."):
+ node = getattr(node, part)
+ return [str(item) for item in node]
+
+
+def children_of(system, params, name):
+ if name == "household_benefits":
+ items = parameter_list(params, "gov.household.household_benefits")
+ if bool(params.gov.hud.abolition):
+ items = [i for i in items if i != "housing_assistance"]
+ return [[item, 1] for item in items]
+ if name in ("household_health_benefits", "household_health_costs"):
+ if not bool(params.gov.simulation.include_health_benefits_in_net_income):
+ return []
+ items = parameter_list(params, "gov.household." + name)
+ return [[item, 1] for item in items]
+ variable = system.variables[name]
+ out = []
+ for attribute, sign in (("adds", 1), ("subtracts", -1)):
+ parts = getattr(variable, attribute)
+ if parts is None:
+ continue
+ if isinstance(parts, str):
+ parts = parameter_list(params, parts)
+ out.extend([str(part), sign] for part in parts)
+ if not out:
+ raise ValueError(f"{name} has no adds or subtracts to expand")
+ return out
+
+
+def build_tree(system, params):
+ tree = {}
+ pending = [root]
+ while pending:
+ name = pending.pop()
+ if name in tree:
+ continue
+ tree[name] = children_of(system, params, name)
+ for child, _ in tree[name]:
+ if child in expand and child not in tree:
+ pending.append(child)
+ return tree
+
+
+def memo_period(name):
+ return name.split("@", 1) if "@" in name else (name, period)
+
+
+def household_value(sim, name, at):
+ values = sim.calculate(name, at, map_to="household")
+ if len(values) != 1:
+ raise ValueError(f"{name}: expected one household, got {len(values)}")
+ return float(values[0])
+
+
+results = []
+for case in job["cases"]:
+ sim = simulation_for(case)
+ system = sim.tax_benefit_system
+ params = system.parameters(f"{year}-01-01")
+ tree = build_tree(system, params)
+ names = set(tree)
+ for parts in tree.values():
+ names.update(child for child, _ in parts)
+ values = {name: household_value(sim, name, period) for name in sorted(names)}
+ memo = {}
+ for name in case.get("memo", []):
+ variable, at = memo_period(name)
+ memo[name] = household_value(sim, variable, at)
+ states = sim.calculate("state_code", period).decode_to_str()
+ results.append(
+ {
+ "case_id": case["case_id"],
+ "state_code": [str(state) for state in states],
+ "tree": tree,
+ "values": values,
+ "memo": memo,
+ }
+ )
+json.dump(
+ {
+ "policyengine_us_version": metadata.version("policyengine-us"),
+ "policyengine_core_version": metadata.version("policyengine-core"),
+ "policyengine_us_package": policyengine_us.__file__,
+ "installation": installation(),
+ "cases": results,
+ },
+ sys.stdout,
+)
+"""
+
+#: :func:`trace_policyengine_us`'s child. Each case runs in its own
+#: simulation (see :data:`_SIMULATION_SOURCE`) with PolicyEngine-US's
+#: tracer on (``Simulation(..., trace=True)``); the child calculates the
+#: requested variables for the year and returns every traced node once,
+#: keyed ``name@period``: its value (as floats, over the variable's own
+#: entity), the nodes it read (``children``) and whether the variable is
+#: an input (no formula, ``adds`` or ``subtracts``). A node calculated in
+#: a branch simulation (policyengine-core's ``get_branch``; 2.18.0 computes
+#: federal income tax through ``itemizing`` and ``not_itemizing`` branches)
+#: is keyed ``branch:name@period``, so a branch's calculation never stands
+#: in for the main one. A node calculated again later is traced again
+#: without children (its value is cached), so the entry with children is
+#: kept.
+_TRACE_SOURCE = _INSTALLATION_SOURCE + _SIMULATION_SOURCE + r"""
+import numpy as np
+
+
+def as_floats(value):
+ if value is None:
+ return None
+ try:
+ return [float(v) for v in np.asarray(value, dtype=float).ravel()]
+ except (TypeError, ValueError):
+ return None
+
+
+def is_input(system, name):
+ variable = system.variables.get(name)
+ if variable is None:
+ return None
+ return not (variable.formulas or variable.adds or variable.subtracts)
+
+
+def node_key(node):
+ key = f"{node.name}@{node.period}"
+ branch = getattr(node, "branch_name", "default") or "default"
+ return key if branch == "default" else f"{branch}:{key}"
+
+
+out = []
+for case in job["cases"]:
+ sim = simulation_for(case, trace=True)
+ system = sim.tax_benefit_system
+ for name in case["variables"]:
+ sim.calculate(name, period)
+ nodes = {}
+
+ def walk(node):
+ key = node_key(node)
+ children = [node_key(c) for c in node.children]
+ seen = nodes.get(key)
+ if seen is None or (children and not seen["children"]):
+ nodes[key] = {
+ "value": as_floats(node.value),
+ "children": children,
+ "input": is_input(system, node.name),
+ }
+ for child in node.children:
+ walk(child)
+
+ for tree in sim.tracer.trees:
+ walk(tree)
+ out.append({"case_id": case["case_id"], "nodes": nodes})
+json.dump({"installation": installation(), "cases": out}, sys.stdout)
+"""
+
+
+@dataclass(frozen=True)
+class RunCase:
+ """One PolicyEngine-US simulation: a situation and its options.
+
+ ``expected_state`` is the two-letter state the situation should resolve
+ to; ``memo`` names further variables to report at the household level
+ (outside the decomposition; ``name@period`` reads another period);
+ ``parameter_overrides`` sets parameters for the whole year (for example
+ ``{"gov.simulation.include_health_benefits_in_net_income": True}``).
+ """
+
+ case_id: str
+ situation: Mapping[str, Any]
+ expected_state: str | None = None
+ memo: tuple[str, ...] = ()
+ parameter_overrides: Mapping[str, Any] = field(default_factory=dict)
+
+
+@dataclass(frozen=True)
+class PolicyEngineRun:
+ """One case's result: the component tree and every node's value."""
+
+ case_id: str
+ state_code: str
+ tree: ComponentTree
+ values: Mapping[str, float]
+ memo: Mapping[str, float]
+
+
+@dataclass(frozen=True)
+class PolicyEngineUSInstallation:
+ """Which ``policyengine-us`` an interpreter imports, and its source.
+
+ ``package_dir`` is the directory ``import policyengine_us`` would load;
+ ``location`` is the installed distribution's root (``site-packages``),
+ with its RECORD file and that file's SHA-256. ``direct_url`` is the
+ distribution's ``direct_url.json`` (PEP 610): absent when the package
+ was installed by name (from a package index, or a local wheel found
+ with ``--find-links``), present for a VCS, local-directory (editable
+ included) or direct-URL archive install.
+ """
+
+ python: str
+ python_version: str
+ package_dir: str | None
+ version: str | None
+ core_version: str | None
+ location: str | None
+ record_path: str | None
+ record_sha256: str | None
+ direct_url: Mapping[str, Any] | None
+ installer: str | None
+
+ @classmethod
+ def from_payload(
+ cls, python: str | os.PathLike, payload: Mapping[str, Any]
+ ) -> PolicyEngineUSInstallation:
+ return cls(
+ python=str(python),
+ python_version=str(payload.get("python_version") or ""),
+ package_dir=payload.get("package_dir"),
+ version=payload.get("version"),
+ core_version=payload.get("core_version"),
+ location=payload.get("location"),
+ record_path=payload.get("record_path"),
+ record_sha256=payload.get("record_sha256"),
+ direct_url=payload.get("direct_url"),
+ installer=payload.get("installer"),
+ )
+
+ @property
+ def imports_the_distribution(self) -> bool:
+ """Whether ``import policyengine_us`` loads the installed files."""
+
+ if not (self.package_dir and self.location):
+ return False
+ return (
+ Path(self.package_dir) == Path(self.location) / "policyengine_us"
+ )
+
+ @property
+ def source_kind(self) -> str:
+ """``index``, ``vcs``, ``archive``, ``directory`` or ``path``.
+
+ ``index`` means installed by name, with no ``direct_url.json``: from
+ a package index, or from a local wheel found with ``--find-links``,
+ which metadata cannot tell apart. A pinned
+ :func:`package_record_digest` ties such an install to a published
+ wheel. ``path`` means the imported package is not the installed
+ distribution's files (an editable install or ``PYTHONPATH``).
+ """
+
+ if not self.imports_the_distribution:
+ return "path"
+ if self.direct_url is None:
+ return "index"
+ if "vcs_info" in self.direct_url:
+ return "vcs"
+ if "archive_info" in self.direct_url:
+ return "archive"
+ return "directory"
+
+
+@dataclass(frozen=True)
+class RunResult:
+ """Every case of one runner call and the versions that computed them."""
+
+ runs: Mapping[str, PolicyEngineRun]
+ policyengine_us_version: str
+ policyengine_core_version: str
+ policyengine_us_package: str
+ python: str
+ installation: PolicyEngineUSInstallation | None = None
+ source: Mapping[str, Any] | None = None
+
+
+def resolve_pe_us_python(python: str | os.PathLike | None = None) -> Path:
+ """The interpreter to run: ``python``, else the environment, else default.
+
+ Returns the path whether or not it exists; callers check.
+ """
+
+ if python:
+ return Path(python).expanduser()
+ env = os.environ.get(PE_US_PYTHON_ENV)
+ if env:
+ return Path(env).expanduser()
+ return DEFAULT_PE_US_PYTHON.expanduser()
+
+
+def _run_child(
+ interpreter: Path, source: str, stdin: str, timeout: float
+) -> dict[str, Any]:
+ env = dict(os.environ)
+ env.setdefault("OMP_NUM_THREADS", "1")
+ try:
+ proc = subprocess.run(
+ [str(interpreter), "-c", source],
+ input=stdin,
+ capture_output=True,
+ text=True,
+ timeout=timeout,
+ env=env,
+ )
+ except (OSError, subprocess.SubprocessError) as error:
+ raise PolicyEngineUSUnavailable(
+ f"policyengine-us runner could not start ({interpreter}): {error}"
+ ) from error
+ if proc.returncode != 0:
+ raise PolicyEngineUSUnavailable(
+ f"policyengine-us runner failed ({interpreter}):\n"
+ f"{proc.stderr[-4000:]}"
+ )
+ return json.loads(proc.stdout)
+
+
+def _interpreter(python: str | os.PathLike | None) -> Path:
+ interpreter = resolve_pe_us_python(python)
+ if not interpreter.is_file():
+ raise PolicyEngineUSUnavailable(
+ f"no policyengine-us interpreter at {interpreter}; set "
+ f"{PE_US_PYTHON_ENV}"
+ )
+ return interpreter
+
+
+def inspect_installation(
+ python: str | os.PathLike | None = None, *, timeout: float = 300.0
+) -> PolicyEngineUSInstallation:
+ """Ask the interpreter which ``policyengine-us`` it imports.
+
+ Reads package metadata only (no simulation, no ``policyengine_us``
+ import). Raises :class:`PolicyEngineUSUnavailable` when the
+ interpreter is missing or fails.
+ """
+
+ interpreter = _interpreter(python)
+ payload = _run_child(interpreter, _INSPECT_SOURCE, "", timeout)
+ return PolicyEngineUSInstallation.from_payload(interpreter, payload)
+
+
+#: Git never prompts for credentials here: an unreachable or private
+#: origin fails fast instead of hanging.
+_GIT_ENV = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}
+
+
+def _git(directory: Path, *args: str, timeout: float = 120.0) -> str:
+ return subprocess.run(
+ ["git", "-C", str(directory), *args],
+ capture_output=True,
+ text=True,
+ check=True,
+ timeout=timeout,
+ env=_GIT_ENV,
+ ).stdout.strip()
+
+
+#: Untracked files under the package that cannot change a calculation:
+#: bytecode caches and Finder metadata. Any other untracked file,
+#: git-ignored or not, marks the checkout modified (policyengine-core loads
+#: every parameter YAML and variable module it finds on disk).
+_INERT_UNTRACKED = (".pyc", ".pyo", ".DS_Store")
+#: GitHub remote URLs, for ``public_origin``. A URL cannot say whether
+#: the repository is public or private, so this checks the host only.
+_GITHUB_ORIGIN = re.compile(
+ r"^(?:https://|ssh://git@|git@)github\.com[/:][^/]+/[^/]+?(?:\.git)?/?$"
+)
+
+
+def _checkout_state(package_dir: Path) -> dict[str, Any]:
+ """The git state of the checkout holding ``package_dir``.
+
+ Reachability is checked against ``origin`` as it is now (``git
+ ls-remote``), not against local remote-tracking refs, which can outlive
+ a deleted branch. A remote head counts only when its commit is also in
+ the local object store (``merge-base --is-ancestor`` needs it); fetch
+ first if the checkout is behind.
+ """
+
+ try:
+ top = Path(_git(package_dir, "rev-parse", "--show-toplevel"))
+ except (OSError, subprocess.SubprocessError):
+ return {"git": False}
+ revision = _git(top, "rev-parse", "HEAD")
+ tracked_changes = _git(
+ top, "status", "--porcelain", "--untracked-files=no"
+ )
+ untracked = [
+ path
+ for path in _git(
+ top, "ls-files", "--others", "--", str(package_dir)
+ ).splitlines()
+ if path
+ and "__pycache__/" not in path
+ and not path.endswith(_INERT_UNTRACKED)
+ ]
+ try:
+ origin_url = _git(top, "remote", "get-url", "origin")
+ except subprocess.SubprocessError:
+ origin_url = None
+ heads: list[str] = []
+ origin_reachable = False
+ if origin_url is not None:
+ try:
+ listing = _git(top, "ls-remote", "--heads", "origin")
+ origin_reachable = True
+ except subprocess.SubprocessError:
+ listing = ""
+ for line in listing.splitlines():
+ sha, _, ref = line.partition("\t")
+ ancestor = subprocess.run(
+ ["git", "-C", str(top), "merge-base", "--is-ancestor"]
+ + [revision, sha],
+ capture_output=True,
+ timeout=120,
+ env=_GIT_ENV,
+ )
+ if ancestor.returncode == 0:
+ heads.append(ref)
+ return {
+ "git": True,
+ "revision": revision,
+ "dirty": bool(tracked_changes or untracked),
+ "untracked_package_files": untracked[:20],
+ "origin_url": origin_url,
+ "origin_reachable": origin_reachable,
+ "origin_heads_containing": heads,
+ }
+
+
+def check_published_source(
+ installation: PolicyEngineUSInstallation,
+ *,
+ require: bool = True,
+ public_origin: bool = True,
+) -> dict[str, Any]:
+ """Describe where the imported ``policyengine-us`` came from.
+
+ Two sources pass:
+
+ * ``index``: installed by name, with no ``direct_url.json``. Metadata
+ cannot tell a package index from a local wheel found with
+ ``--find-links``, so a caller that must know the release pins
+ :func:`package_record_digest` (``run_policyengine_us``'s
+ ``expected_package_record_digest``) and checks the files with
+ :func:`verify_record`.
+ * ``path``: a git checkout imported in place (an editable install or
+ ``PYTHONPATH``) whose ``HEAD`` a head of ``origin`` contains, as
+ ``git ls-remote`` reports it now, and with no modified tracked file
+ or untracked package file (ignored ones included). With
+ ``public_origin`` (the default), ``origin`` must be a GitHub
+ repository.
+
+ Anything else -- a revision no ``origin`` head contains, an
+ unreachable ``origin``, a modified checkout, a package directory
+ outside any git checkout, or a VCS, local-directory or direct-URL
+ archive install -- raises :class:`UnpublishedSourceError` when
+ ``require`` is true, and is returned with ``published`` false
+ otherwise.
+ """
+
+ kind = installation.source_kind
+ record: dict[str, Any] = {
+ "kind": kind,
+ "version": installation.version,
+ "core_version": installation.core_version,
+ "record_sha256": installation.record_sha256,
+ }
+ reasons: list[str] = []
+ if kind == "index":
+ if not installation.record_path:
+ reasons.append("the distribution has no RECORD file")
+ elif kind == "path":
+ if not installation.package_dir:
+ reasons.append("policyengine_us is not importable")
+ else:
+ state = _checkout_state(Path(installation.package_dir))
+ record["checkout"] = state
+ if not state["git"]:
+ reasons.append(
+ f"{installation.package_dir} is not in a git checkout"
+ )
+ else:
+ if state["origin_url"] is None:
+ reasons.append("the checkout has no origin remote")
+ elif public_origin and not _GITHUB_ORIGIN.match(
+ state["origin_url"]
+ ):
+ reasons.append(
+ f"origin ({state['origin_url']}) is not a GitHub "
+ "repository"
+ )
+ if state["origin_url"] is not None and not (
+ state["origin_reachable"]
+ ):
+ reasons.append(
+ "origin could not be reached to confirm the "
+ "revision is published"
+ )
+ elif not state["origin_heads_containing"]:
+ reasons.append(
+ f"revision {state['revision']} is not reachable "
+ "from any branch of origin"
+ )
+ if state["dirty"]:
+ reasons.append("the checkout has uncommitted changes")
+ else:
+ record["direct_url"] = dict(installation.direct_url or {})
+ reasons.append(
+ f"a {kind} install cannot be checked against a published "
+ "release here; install a release from PyPI"
+ )
+ record["published"] = not reasons
+ record["reasons"] = reasons
+ if reasons and require:
+ raise UnpublishedSourceError(
+ f"policyengine-us at {installation.package_dir} is not a "
+ f"published source ({kind}): " + "; ".join(reasons)
+ )
+ return record
+
+
+def _is_bytecode(path: str) -> bool:
+ return "/__pycache__/" in f"/{path}" or path.endswith((".pyc", ".pyo"))
+
+
+def package_record_digest(record_text: str, prefix: str) -> tuple[str, int]:
+ """SHA-256 of a RECORD's sorted lines under ``prefix``, and their count.
+
+ The installer appends its own lines (``INSTALLER``, ``REQUESTED``,
+ console scripts, and with pip the bytecode it compiles) and may reorder
+ the file, so the whole-file hash of an installed RECORD differs from
+ the wheel's. The sorted package lines, bytecode excluded, are the same
+ in both, so this digest ties installed files to a wheel.
+ """
+
+ lines = sorted(
+ line
+ for line in record_text.splitlines()
+ if line.startswith(prefix)
+ and not _is_bytecode(next(csv.reader([line]))[0])
+ )
+ digest = hashlib.sha256(("\n".join(lines) + "\n").encode()).hexdigest()
+ return digest, len(lines)
+
+
+def verify_record(
+ installation: PolicyEngineUSInstallation,
+ *,
+ prefix: str = "policyengine_us/",
+) -> dict[str, Any]:
+ """Check the installed files under ``prefix`` against the RECORD.
+
+ Hashes every file the RECORD lists with a hash (``mismatched`` when the
+ SHA-256 differs, ``missing`` when absent) and walks the installed
+ directory for files the RECORD does not list (``extra``;
+ policyengine-core loads every parameter YAML and variable module on
+ disk, so an added file changes the model). Bytecode is neither hashed
+ nor counted as extra. Also returns :func:`package_record_digest` of
+ the RECORD. The RECORD hash is urlsafe base64 without padding (the
+ wheel format, PEP 376/427).
+ """
+
+ if not (installation.record_path and installation.location):
+ raise ValueError("the installation has no RECORD to verify")
+ root = Path(installation.location)
+ text = Path(installation.record_path).read_text()
+ checked = 0
+ listed: set[str] = set()
+ mismatched: list[str] = []
+ missing: list[str] = []
+ for row in csv.reader(io.StringIO(text)):
+ if not row or not row[0].startswith(prefix):
+ continue
+ path, digest = row[0], row[1] if len(row) > 1 else ""
+ listed.add(path)
+ if not digest:
+ continue
+ algorithm, _, expected = digest.partition("=")
+ target = root / path
+ if not target.is_file():
+ missing.append(path)
+ continue
+ actual = hashlib.new(algorithm, target.read_bytes()).digest()
+ encoded = base64.urlsafe_b64encode(actual).rstrip(b"=").decode()
+ checked += 1
+ if encoded != expected:
+ mismatched.append(path)
+ extra = sorted(
+ relative
+ for file in (root / prefix).rglob("*")
+ if file.is_file()
+ and not _is_bytecode(relative := file.relative_to(root).as_posix())
+ and relative not in listed
+ )
+ digest, lines = package_record_digest(text, prefix)
+ return {
+ "prefix": prefix,
+ "files_checked": checked,
+ "mismatched": mismatched,
+ "missing": missing,
+ "extra": extra,
+ "package_record_digest": digest,
+ "package_record_lines": lines,
+ }
+
+
+def _checked_installation(
+ interpreter: Path,
+ *,
+ require_published: bool,
+ verify_files: bool,
+ expected_package_record_digest: str | None,
+) -> tuple[PolicyEngineUSInstallation, dict[str, Any]]:
+ """Inspect, check the source, and (for an index install) the files."""
+
+ installation = inspect_installation(interpreter)
+ source = check_published_source(installation, require=require_published)
+ problems = []
+ if installation.source_kind == "index" and (
+ verify_files or expected_package_record_digest
+ ):
+ record = verify_record(installation)
+ source = {
+ **source,
+ "record_check": {
+ key: len(value) if isinstance(value, list) else value
+ for key, value in record.items()
+ },
+ }
+ if record["mismatched"] or record["missing"] or record["extra"]:
+ problems.append(
+ f"{len(record['mismatched'])} installed files differ from "
+ f"the RECORD, {len(record['missing'])} are missing and "
+ f"{len(record['extra'])} are not in it"
+ )
+ if expected_package_record_digest and (
+ record["package_record_digest"] != expected_package_record_digest
+ ):
+ problems.append("the RECORD is not the expected release's")
+ elif expected_package_record_digest:
+ problems.append(
+ f"a {installation.source_kind} install has no RECORD to compare "
+ "with the expected release"
+ )
+ if problems:
+ raise UnpublishedSourceError(
+ f"policyengine-us at {installation.package_dir}: "
+ + "; ".join(problems)
+ )
+ return installation, source
+
+
+def run_policyengine_us(
+ cases: Sequence[RunCase],
+ *,
+ year: int,
+ python: str | os.PathLike | None = None,
+ expand: Sequence[str] = DEFAULT_EXPAND,
+ root: str = ROOT_VARIABLE,
+ timeout: float = 1800.0,
+ require_published: bool = True,
+ verify_files: bool = True,
+ expected_package_record_digest: str | None = None,
+) -> RunResult:
+ """Run ``cases`` in the policyengine-us interpreter and parse the results.
+
+ Each case runs in its own simulation (cases with the same parameter
+ overrides share a tax-benefit system). Before running, the imported
+ ``policyengine-us`` must be a published source
+ (:func:`check_published_source`, when ``require_published``), an index
+ install's files must match its RECORD (:func:`verify_record`, when
+ ``verify_files``), and its RECORD must have
+ ``expected_package_record_digest`` when one is given.
+
+ Raises :class:`PolicyEngineUSUnavailable` when the interpreter is
+ missing or the child fails, :class:`UnpublishedSourceError` when a
+ check above fails, and ``ValueError`` when a case resolves to a state
+ other than its ``expected_state`` or a case id repeats.
+ """
+
+ if isinstance(year, bool) or not isinstance(year, int):
+ raise TypeError(f"year must be an integer, not {year!r}")
+ ids = [case.case_id for case in cases]
+ if len(set(ids)) != len(ids):
+ raise ValueError(f"case ids must be unique: {ids}")
+ interpreter = _interpreter(python)
+ installation, source = _checked_installation(
+ interpreter,
+ require_published=require_published,
+ verify_files=verify_files,
+ expected_package_record_digest=expected_package_record_digest,
+ )
+ job = {
+ "year": year,
+ "root": root,
+ "expand": list(expand),
+ "cases": [
+ {
+ "case_id": case.case_id,
+ "situation": case.situation,
+ "memo": list(case.memo),
+ "parameter_overrides": dict(case.parameter_overrides),
+ }
+ for case in cases
+ ],
+ }
+ payload = _run_child(interpreter, _RUNNER_SOURCE, json.dumps(job), timeout)
+ ran = PolicyEngineUSInstallation.from_payload(
+ interpreter, payload["installation"]
+ )
+ if ran != installation:
+ raise PolicyEngineUSUnavailable(
+ "policyengine-us changed between the source check and the run"
+ )
+ runs: dict[str, PolicyEngineRun] = {}
+ by_id = {case.case_id: case for case in cases}
+ for raw in payload["cases"]:
+ case = by_id[raw["case_id"]]
+ states = sorted(set(raw["state_code"]))
+ if len(states) != 1:
+ raise ValueError(f"{case.case_id}: several states {states}")
+ state = states[0]
+ if case.expected_state is not None and state != case.expected_state:
+ raise ValueError(
+ f"{case.case_id}: situation resolved to {state}, not "
+ f"{case.expected_state} (check STATE_FIPS)"
+ )
+ tree = ComponentTree(
+ root=root,
+ children={
+ name: tuple((str(c), int(s)) for c, s in parts)
+ for name, parts in raw["tree"].items()
+ },
+ )
+ runs[case.case_id] = PolicyEngineRun(
+ case_id=case.case_id,
+ state_code=state,
+ tree=tree,
+ values={k: float(v) for k, v in raw["values"].items()},
+ memo={k: float(v) for k, v in raw["memo"].items()},
+ )
+ if set(runs) != set(ids):
+ raise PolicyEngineUSUnavailable("the runner returned other cases")
+ return RunResult(
+ runs=runs,
+ policyengine_us_version=str(payload["policyengine_us_version"]),
+ policyengine_core_version=str(payload["policyengine_core_version"]),
+ policyengine_us_package=str(payload["policyengine_us_package"]),
+ python=str(interpreter),
+ installation=installation,
+ source=source,
+ )
+
+
+# ---------------------------------------------------------------------------
+# The float32 guard (traced runs)
+# ---------------------------------------------------------------------------
+#: A change smaller than a cent is float noise, not a change.
+CENT_TOLERANCE = 0.01
+#: Float32 carries 24 significant bits, so a value near ``x`` moves in
+#: steps of 2 ** (exponent(x) - 24). A read that changed by no more than
+#: this many steps is treated as noise even when the steps exceed a cent
+#: (above $131,072 one step is $0.015625).
+FLOAT32_NOISE_STEPS = 4
+#: Leaf changes below this many cents (and above zero) are small enough to
+#: be a float32 step at a bracket edge and are always traced.
+SMALL_CHANGE_CENTS = 200
+
+
+def float32_step(value: float) -> float:
+ """The spacing of float32 values at ``value`` (0 at 0 and non-finite)."""
+
+ if value == 0 or not math.isfinite(value):
+ return 0.0
+ _, exponent = math.frexp(value)
+ return math.ldexp(1.0, exponent - 24)
+
+
+@dataclass(frozen=True)
+class TraceNode:
+ """One traced variable at one period in one case.
+
+ ``value`` is the variable's value over its own entity (``None`` when
+ it is not numeric); ``children`` are the ``name@period`` keys it read;
+ ``input`` is true for a variable with no formula, ``adds`` or
+ ``subtracts`` (``None`` for a name that is not a variable).
+ """
+
+ value: tuple[float, ...] | None
+ children: tuple[str, ...]
+ input: bool | None
+
+
+def trace_policyengine_us(
+ cases: Sequence[tuple[str, Mapping[str, Any], Sequence[str]]],
+ *,
+ year: int,
+ python: str | os.PathLike | None = None,
+ parameter_overrides: Mapping[str, Mapping[str, Any]] | None = None,
+ timeout: float = 1800.0,
+ require_published: bool = True,
+ verify_files: bool = True,
+ expected_package_record_digest: str | None = None,
+) -> dict[str, dict[str, TraceNode]]:
+ """Trace ``(case_id, situation, variables)`` cases, one simulation each.
+
+ Returns ``{case_id: {"name@period": TraceNode}}`` (branch simulations'
+ nodes are keyed ``branch:name@period``). ``parameter_overrides`` maps
+ a case id to its overrides (as :class:`RunCase`). The imported
+ ``policyengine-us`` is checked as in :func:`run_policyengine_us`, and
+ must not change between the check and the trace.
+ """
+
+ if isinstance(year, bool) or not isinstance(year, int):
+ raise TypeError(f"year must be an integer, not {year!r}")
+ ids = [case_id for case_id, _, _ in cases]
+ if len(set(ids)) != len(ids):
+ raise ValueError(f"case ids must be unique: {ids}")
+ interpreter = _interpreter(python)
+ installation, _ = _checked_installation(
+ interpreter,
+ require_published=require_published,
+ verify_files=verify_files,
+ expected_package_record_digest=expected_package_record_digest,
+ )
+ overrides = parameter_overrides or {}
+ job = {
+ "year": year,
+ "cases": [
+ {
+ "case_id": case_id,
+ "situation": situation,
+ "variables": list(variables),
+ "parameter_overrides": dict(overrides.get(case_id, {})),
+ }
+ for case_id, situation, variables in cases
+ ],
+ }
+ payload = _run_child(interpreter, _TRACE_SOURCE, json.dumps(job), timeout)
+ traced = PolicyEngineUSInstallation.from_payload(
+ interpreter, payload["installation"]
+ )
+ if traced != installation:
+ raise PolicyEngineUSUnavailable(
+ "policyengine-us changed between the source check and the trace"
+ )
+ out: dict[str, dict[str, TraceNode]] = {}
+ for case in payload["cases"]:
+ out[case["case_id"]] = {
+ key: TraceNode(
+ value=(
+ None
+ if node["value"] is None
+ else tuple(float(v) for v in node["value"])
+ ),
+ children=tuple(node["children"]),
+ input=node["input"],
+ )
+ for key, node in case["nodes"].items()
+ }
+ if set(out) != set(ids):
+ raise PolicyEngineUSUnavailable("the tracer returned other cases")
+ return out
+
+
+def _node_change(
+ baseline: TraceNode | None, reform: TraceNode | None
+) -> float | None:
+ """The largest absolute change of a node, or ``None`` if not comparable.
+
+ A node traced in one run only, or with values of different lengths, is
+ a change of infinite size (its structure changed).
+ """
+
+ if baseline is None or reform is None:
+ return math.inf
+ if baseline.value is None or reform.value is None:
+ return None
+ if len(baseline.value) != len(reform.value):
+ return math.inf
+ worst = 0.0
+ for a, b in zip(baseline.value, reform.value, strict=True):
+ if not (math.isfinite(a) and math.isfinite(b)):
+ if not (a == b or (math.isnan(a) and math.isnan(b))):
+ return math.inf
+ continue
+ worst = max(worst, abs(b - a))
+ return worst
+
+
+def _noise(
+ baseline: TraceNode | None, reform: TraceNode | None, tolerance: float
+) -> float:
+ """The largest change of a read that still counts as float noise."""
+
+ magnitude = _magnitude(baseline, reform)
+ return max(tolerance, FLOAT32_NOISE_STEPS * float32_step(magnitude))
+
+
+def _counts_as_change(
+ baseline: TraceNode, reform: TraceNode, change: float, tolerance: float
+) -> bool:
+ """Whether a node's ``change`` is ``tolerance`` or more before rounding.
+
+ Values are stored in float32, so a change of exactly ``tolerance``
+ can measure up to one float32 step less (3.00 to 3.01 measures
+ 0.0099999905): each stored end is off by at most half a step. The
+ threshold is therefore ``tolerance`` less one float32 step at the
+ node's magnitude, and never less than half of ``tolerance``, so a zero
+ change or float64 noise on a large value never counts.
+
+ Two limits. From $32,768 to $131,072 a cent is one or two float32
+ steps, so one or two steps of noise count too: the guard errs toward a
+ false alarm. And the bound covers a stored value, not one computed
+ from larger operands, which carries their larger rounding: x = a -
+ 5,760 with ``a`` near $35,760 moves by $0.0078125 when ``a`` moves by a
+ cent, and that does not count.
+ """
+
+ if change == 0:
+ return False
+ slack = float32_step(_magnitude(baseline, reform))
+ return change >= tolerance - min(slack, tolerance / 2)
+
+
+def _magnitude(*nodes: TraceNode | None) -> float:
+ """The largest finite absolute value among the nodes' values."""
+
+ return max(
+ (
+ abs(v)
+ for node in nodes
+ if node is not None and node.value is not None
+ for v in node.value
+ if math.isfinite(v)
+ ),
+ default=0.0,
+ )
+
+
+def _values(node: TraceNode | None) -> list[float] | None:
+ return None if node is None or node.value is None else list(node.value)
+
+
+def uncaused_changes(
+ baseline: Mapping[str, TraceNode],
+ reform: Mapping[str, TraceNode],
+ roots: Iterable[str],
+ *,
+ tolerance: float = CENT_TOLERANCE,
+) -> list[dict[str, Any]]:
+ """Variables that change although every variable they read did not.
+
+ Walks the traced nodes reachable from ``roots`` (``name@period``
+ keys) in both runs. A node is reported when it was traced in both
+ runs, is not an input, its value changes by at least ``tolerance``
+ before float32 rounding (:func:`_counts_as_change`, so an exact
+ one-cent step stored in float32 counts), and no node it read changed
+ by more than float noise: ``tolerance`` or
+ :data:`FLOAT32_NOISE_STEPS` float32 steps at the read's magnitude,
+ whichever is larger (so the guard still sees noise above $131,072,
+ where one float32 step exceeds a cent). A node it read in one run only
+ counts as changed: the calculation took another branch, which a
+ changed value upstream decided. A node traced in one run only is not
+ compared for the same reason. A branch simulation's node traced
+ without reads (``branch:name@period``) holds a value copied from the
+ parent simulation and reads the main node ``name@period``. A read
+ with no numeric value never counts as a cause, so a node whose only
+ changed read is not numeric is reported (a false alarm, not a miss).
+
+ A genuine change always has a changed input below it (here, the Social
+ Security amount), so a reported node is a step taken on noise:
+ typically a bracket or eligibility edge that float32 arithmetic
+ crossed. The rule is local, so it cannot see a noise step in a
+ variable that also read a genuinely changed input. Returns one record
+ per reported node, sorted by key.
+ """
+
+ found: dict[str, dict[str, Any]] = {}
+ visited: set[str] = set()
+ pending = list(roots)
+ while pending:
+ key = pending.pop()
+ if key in visited:
+ continue
+ visited.add(key)
+ before, after = baseline.get(key), reform.get(key)
+ children = tuple(
+ dict.fromkeys(
+ (before.children if before else ())
+ + (after.children if after else ())
+ )
+ )
+ if not children and ":" in key:
+ # A branch starts as a clone of its parent's calculated values
+ # (policyengine-core ``get_branch``, ``simulation.py:1499``), so
+ # a branch node traced without reads was copied, not computed:
+ # it reads the main calculation's node of the same name.
+ main = key.split(":", 1)[1]
+ if main in baseline or main in reform:
+ children = (main,)
+ pending.extend(children)
+ if before is None or after is None or before.input:
+ continue
+ change = _node_change(before, after)
+ if change is None or not _counts_as_change(
+ before, after, change, tolerance
+ ):
+ continue
+ child_changes = {
+ child: _node_change(baseline.get(child), reform.get(child))
+ for child in children
+ }
+ caused = any(
+ value is not None
+ and value
+ > _noise(baseline.get(child), reform.get(child), tolerance)
+ for child, value in child_changes.items()
+ )
+ if not caused:
+ found[key] = {
+ "variable": key,
+ "baseline": _values(before),
+ "reform": _values(after),
+ "change": change,
+ "reads": dict(sorted(child_changes.items())),
+ }
+ return [found[key] for key in sorted(found)]
+
+
+def small_changes(
+ decomposition: Decomposition, *, limit_cents: int = SMALL_CHANGE_CENTS
+) -> list[Component]:
+ """Leaves whose change is nonzero and under ``limit_cents``."""
+
+ return [
+ component
+ for component in decomposition.components
+ if 0 < abs(component.change_cents) < limit_cents
+ ]
+
+
+# ---------------------------------------------------------------------------
+# The decomposition
+# ---------------------------------------------------------------------------
+@dataclass(frozen=True)
+class ComponentTree:
+ """``children[name]`` lists ``(part, sign)`` for each expanded aggregate.
+
+ A name absent from ``children``, or present with no parts, is a leaf.
+ """
+
+ root: str
+ children: Mapping[str, tuple[tuple[str, int], ...]]
+
+ def __post_init__(self) -> None:
+ if self.root not in self.children:
+ raise ValueError(f"the tree does not expand its root {self.root}")
+ for name, parts in self.children.items():
+ for part, sign in parts:
+ if sign not in (1, -1):
+ raise ValueError(f"{name}: sign {sign} of {part}")
+
+ def visit(name: str, stack: tuple[str, ...]) -> None:
+ if name in stack:
+ raise ValueError(f"cycle through {name}")
+ for part, _ in self.children.get(name, ()):
+ visit(part, (*stack, name))
+
+ visit(self.root, ())
+
+ def is_leaf(self, name: str) -> bool:
+ return not self.children.get(name)
+
+ def leaves(self) -> tuple[tuple[str, tuple[str, ...], int], ...]:
+ """Each leaf with its ancestors (root first) and cumulative sign."""
+
+ out: list[tuple[str, tuple[str, ...], int]] = []
+
+ def walk(name: str, path: tuple[str, ...], sign: int) -> None:
+ if self.is_leaf(name):
+ out.append((name, path, sign))
+ return
+ for part, part_sign in self.children[name]:
+ walk(part, (*path, name), sign * part_sign)
+
+ walk(self.root, (), 1)
+ return tuple(out)
+
+ def aggregates(self) -> tuple[str, ...]:
+ """The names with parts, parents before children."""
+
+ order: list[str] = []
+
+ def walk(name: str) -> None:
+ if self.is_leaf(name) or name in order:
+ return
+ order.append(name)
+ for part, _ in self.children[name]:
+ walk(part)
+
+ walk(self.root)
+ return tuple(order)
+
+
+def to_cents(value: float) -> int:
+ """Dollars to integer cents, half to even, exactly from the float."""
+
+ amount = float(value)
+ if not math.isfinite(amount):
+ raise ValueError(f"amount must be finite, not {value!r}")
+ cents = (Decimal(amount) * 100).to_integral_value(rounding=ROUND_HALF_EVEN)
+ return int(cents)
+
+
+#: Display categories, in table order, and their labels. Taxes before
+#: refundable credits and refundable credits are separate rows, so a
+#: refundable credit on a zero liability never reads as a negative tax.
+CATEGORY_ORDER: tuple[str, ...] = (
+ "social_security",
+ "ssi",
+ "state_benefits",
+ "snap",
+ "csfp",
+ "other_benefits",
+ "health_net",
+ "market_income",
+ "federal_income_tax",
+ "federal_refundable_credits",
+ "state_income_tax",
+ "state_refundable_credits",
+ "other_taxes",
+)
+CATEGORY_LABELS: dict[str, str] = {
+ "social_security": "Social Security",
+ "ssi": "SSI (federal)",
+ "state_benefits": "State benefits (incl. SSI supplements)",
+ "snap": "SNAP",
+ "csfp": "Commodity Supplemental Food Program",
+ "other_benefits": "Other benefits",
+ "health_net": "Health benefits less health costs",
+ "market_income": "Market income",
+ "federal_income_tax": "Federal income tax before refundable credits",
+ "federal_refundable_credits": "Federal refundable tax credits",
+ "state_income_tax": "State income tax before refundable credits",
+ "state_refundable_credits": "State refundable tax credits",
+ "other_taxes": "Other taxes (payroll, use, local)",
+}
+
+
+def category_for(variable: str, path: Sequence[str]) -> str:
+ """The display category of a leaf reached through ``path``."""
+
+ ancestors = set(path)
+ if variable == "social_security":
+ return "social_security"
+ if variable == "ssi":
+ return "ssi"
+ if variable == "snap":
+ return "snap"
+ if variable == "commodity_supplemental_food_program":
+ return "csfp"
+ if variable == "income_tax_before_refundable_credits":
+ return "federal_income_tax"
+ if variable == "income_tax_refundable_credits":
+ return "federal_refundable_credits"
+ if "household_state_benefits" in ancestors:
+ return "state_benefits"
+ if ancestors & {"household_health_benefits", "household_health_costs"}:
+ return "health_net"
+ if "household_market_income" in ancestors:
+ return "market_income"
+ if variable == "state_income_tax_before_refundable_credits":
+ return "state_income_tax"
+ if "household_refundable_state_tax_credits" in ancestors:
+ return "state_refundable_credits"
+ if "household_tax_before_refundable_credits" in ancestors:
+ return "other_taxes"
+ return "other_benefits"
+
+
+@dataclass(frozen=True)
+class Component:
+ """A leaf's contribution to net income in each run, in cents.
+
+ ``sign`` is +1 for an income and -1 for a tax or cost; ``baseline`` and
+ ``reform`` are the signed contributions, so a tax enters negative.
+ """
+
+ variable: str
+ path: tuple[str, ...]
+ sign: int
+ category: str
+ baseline_cents: int
+ reform_cents: int
+
+ @property
+ def change_cents(self) -> int:
+ return self.reform_cents - self.baseline_cents
+
+ def as_dict(self) -> dict[str, Any]:
+ return {
+ "variable": self.variable,
+ "path": list(self.path),
+ "sign": self.sign,
+ "category": self.category,
+ "baseline": self.baseline_cents / 100,
+ "reform": self.reform_cents / 100,
+ "change": self.change_cents / 100,
+ }
+
+
+@dataclass(frozen=True)
+class Decomposition:
+ """Leaf components whose changes sum exactly to ``net_change_cents``.
+
+ ``baseline_net_cents`` and ``reform_net_cents`` are net income as its
+ definition computes it from the cent-rounded leaves;
+ ``reported_*`` are PolicyEngine-US's own ``household_net_income``
+ (float32 sums), and ``reported_gap_cents`` their difference from the
+ definition's change (float rounding inside PolicyEngine-US, bounded by
+ the tolerance). ``max_aggregate_gap`` is the largest difference between
+ an aggregate and the sum of its parts found in either run.
+ """
+
+ components: tuple[Component, ...]
+ baseline_net_cents: int
+ reform_net_cents: int
+ reported_baseline_net: float
+ reported_reform_net: float
+ max_aggregate_gap: float
+
+ @property
+ def net_change_cents(self) -> int:
+ return self.reform_net_cents - self.baseline_net_cents
+
+ @property
+ def reported_gap_cents(self) -> int:
+ reported = to_cents(self.reported_reform_net) - to_cents(
+ self.reported_baseline_net
+ )
+ return reported - self.net_change_cents
+
+ def by_category(self) -> dict[str, dict[str, int]]:
+ """Each category's baseline, reform and change, in cents."""
+
+ out = {
+ name: {"baseline": 0, "reform": 0, "change": 0}
+ for name in CATEGORY_ORDER
+ }
+ for component in self.components:
+ entry = out.setdefault(
+ component.category, {"baseline": 0, "reform": 0, "change": 0}
+ )
+ entry["baseline"] += component.baseline_cents
+ entry["reform"] += component.reform_cents
+ entry["change"] += component.change_cents
+ return out
+
+ def as_dict(self) -> dict[str, Any]:
+ return {
+ "net_change": self.net_change_cents / 100,
+ "baseline_net_income": self.baseline_net_cents / 100,
+ "reform_net_income": self.reform_net_cents / 100,
+ "reported_baseline_net_income": self.reported_baseline_net,
+ "reported_reform_net_income": self.reported_reform_net,
+ "reported_gap": self.reported_gap_cents / 100,
+ "max_aggregate_gap": self.max_aggregate_gap,
+ "categories": {
+ name: {key: cents / 100 for key, cents in entry.items()}
+ for name, entry in self.by_category().items()
+ },
+ "components": [
+ component.as_dict()
+ for component in self.components
+ if component.baseline_cents or component.reform_cents
+ ],
+ }
+
+
+def _aggregate_gap(tree: ComponentTree, values: Mapping[str, float]) -> float:
+ """The largest |aggregate - sum of parts|; infinite if any is not finite.
+
+ ``max(0.0, nan)`` is ``0.0``, so a non-finite gap must be caught before
+ it is compared, or a NaN aggregate would pass as consistent.
+ """
+
+ worst = 0.0
+ for name in tree.aggregates():
+ parts = tree.children[name]
+ terms = [sign * float(values[part]) for part, sign in parts]
+ value = float(values[name])
+ if not all(math.isfinite(term) for term in (*terms, value)):
+ return math.inf
+ gap = abs(value - math.fsum(terms))
+ if not math.isfinite(gap):
+ return math.inf
+ worst = max(worst, gap)
+ return worst
+
+
+def decompose(
+ tree: ComponentTree,
+ baseline: Mapping[str, float],
+ reform: Mapping[str, float],
+ *,
+ reform_tree: ComponentTree | None = None,
+ tolerance: float = 0.5,
+ expected_definition: Sequence[tuple[str, int]] | None = (
+ NET_INCOME_DEFINITION
+ ),
+) -> Decomposition:
+ """Split the change in ``tree.root`` into its leaves.
+
+ Both runs must share ``tree`` (``reform_tree``, when given, must equal
+ it). Every aggregate in each run must equal the signed sum of its parts
+ within ``tolerance`` dollars, and every value must be finite, else
+ :class:`AggregateMismatchError`; the
+ root's parts must equal ``expected_definition`` (skip with ``None``),
+ else :class:`DefinitionDriftError`. Leaves are rounded to cents, so the
+ components' changes sum exactly to the net change; PolicyEngine-US's own
+ root values are kept as ``reported_*``.
+ """
+
+ if reform_tree is not None and reform_tree != tree:
+ raise DefinitionDriftError("baseline and reform trees differ")
+ if expected_definition is not None:
+ observed = tuple(tree.children[tree.root])
+ if observed != tuple(tuple(item) for item in expected_definition):
+ raise DefinitionDriftError(
+ f"{tree.root} is {observed}, not the definition the bridge "
+ f"was checked against ({tuple(expected_definition)})"
+ )
+ gaps = []
+ for label, values in (("baseline", baseline), ("reform", reform)):
+ gap = _aggregate_gap(tree, values)
+ if not math.isfinite(gap):
+ raise AggregateMismatchError(
+ f"{label}: an aggregate or one of its parts is not finite"
+ )
+ if gap > tolerance:
+ raise AggregateMismatchError(
+ f"{label}: an aggregate differs from its parts by {gap:.4f}"
+ )
+ gaps.append(gap)
+ components = tuple(
+ Component(
+ variable=name,
+ path=path,
+ sign=sign,
+ category=category_for(name, path),
+ baseline_cents=sign * to_cents(baseline[name]),
+ reform_cents=sign * to_cents(reform[name]),
+ )
+ for name, path, sign in tree.leaves()
+ )
+ decomposition = Decomposition(
+ components=components,
+ baseline_net_cents=sum(c.baseline_cents for c in components),
+ reform_net_cents=sum(c.reform_cents for c in components),
+ reported_baseline_net=float(baseline[tree.root]),
+ reported_reform_net=float(reform[tree.root]),
+ max_aggregate_gap=max(gaps),
+ )
+ if abs(decomposition.reported_gap_cents) > round(tolerance * 100):
+ raise AggregateMismatchError(
+ f"{tree.root}'s reported change differs from its definition by "
+ f"{decomposition.reported_gap_cents} cents"
+ )
+ return decomposition
+
+
+# ---------------------------------------------------------------------------
+# Carrying a Microcosm PIA to the payment year (42 USC 415(i))
+# ---------------------------------------------------------------------------
+#: COLA rates are quantized to a millionth before use: statutory COLAs are
+#: tenths of a percent (0.001), and a fraction built as ``percent / 100`` in
+#: floating point can carry noise (``5.9 / 100`` is ``0.059000000000000004``)
+#: that would otherwise reach the dime truncation.
+_RATE_QUANTUM = Decimal("0.000001")
+
+
+def _rate(value: float) -> Decimal:
+ return Decimal(repr(float(value))).quantize(
+ _RATE_QUANTUM, rounding=ROUND_HALF_EVEN
+ )
+
+
+def floor_to_dime(amount: float | Decimal) -> float:
+ """The next lower multiple of $0.10 (415(i)(2)(A)(ii)), exactly."""
+
+ value = amount if isinstance(amount, Decimal) else Decimal(repr(amount))
+ tenths = (value * 10).to_integral_value(rounding=ROUND_FLOOR)
+ return float(tenths / 10)
+
+
+def carry_pia_forward(
+ pia: float,
+ rates: Mapping[int, float],
+ *,
+ first_determination_year: int,
+ last_determination_year: int,
+) -> tuple[float, tuple[dict[str, Any], ...]]:
+ """Apply each COLA from ``first_determination_year`` through the last.
+
+ 42 USC 415(i)(2)(A)(ii) multiplies the PIA by each increase and
+ decreases any amount that is not a multiple of $0.10 to the next lower
+ multiple; (iii) applies the increase of the year of eligibility and
+ every later one to a PIA first computed in that year. ``rates`` maps a
+ determination year to a fraction (``0.028`` for 2.8 percent). Returns
+ the carried PIA and one record per step. Decimal arithmetic on the
+ rates (quantized to a millionth) keeps the truncation exact. An empty
+ range (``first_determination_year`` after the last) is refused: it
+ would return the PIA untruncated, not a whole dime.
+ """
+
+ value = _check_amount("pia", pia)
+ for label, year in (
+ ("first_determination_year", first_determination_year),
+ ("last_determination_year", last_determination_year),
+ ):
+ if isinstance(year, bool) or not isinstance(year, int):
+ raise TypeError(f"{label} must be an integer year")
+ if first_determination_year > last_determination_year:
+ raise ValueError(
+ f"first_determination_year {first_determination_year} is after "
+ f"last_determination_year {last_determination_year}"
+ )
+ current = Decimal(repr(value))
+ steps: list[dict[str, Any]] = []
+ for year in range(first_determination_year, last_determination_year + 1):
+ if year not in rates:
+ raise ValueError(f"no COLA for determination year {year}")
+ rate = float(rates[year])
+ if not math.isfinite(rate) or rate < 0:
+ raise ValueError(f"COLA for {year} must be nonnegative: {rate}")
+ before = current
+ increased = before * (1 + _rate(rate))
+ current = Decimal(repr(floor_to_dime(increased)))
+ steps.append(
+ {
+ "determination_year": year,
+ "rate": rate,
+ "pia_before": float(before),
+ "pia_after": float(current),
+ }
+ )
+ return float(current), tuple(steps)
+
+
+def cola_rates_from_cpi_w(
+ cpi_w_by_year: Mapping[int, float],
+) -> dict[int, float]:
+ """COLAs from third-quarter CPI-W averages (42 USC 415(i)(1)).
+
+ ``cpi_w_by_year[y]`` is the third-quarter average of year ``y - 1`` (the
+ keying of PolicyEngine-US's ``gov.ssa.uprating``, whose entry dated
+ ``y``-01-01 is ``y - 1``'s third quarter). The COLA determined in
+ ``y - 1`` is the percentage by which that quarter's index exceeds the
+ index of the most recent prior cost-of-living computation quarter,
+ rounded to the nearest one-tenth of 1 percent (415(i)(1)(D)); a quarter
+ is a computation quarter only when that percentage is greater than zero
+ (415(i)(1)(B)), else the COLA is zero and the base carries forward.
+ The first entry is taken to be a computation quarter. Returns
+ ``{determination year: fraction}``.
+ """
+
+ years = sorted(int(year) for year in cpi_w_by_year)
+ if len(years) < 2:
+ raise ValueError("need at least two third-quarter averages")
+ if years != list(range(years[0], years[-1] + 1)):
+ raise ValueError(
+ f"third-quarter averages must be consecutive: {years}"
+ )
+ for year in years:
+ _check_amount(f"CPI-W {year}", cpi_w_by_year[year])
+ if cpi_w_by_year[year] <= 0:
+ raise ValueError(f"CPI-W {year} must be positive")
+ base = Decimal(repr(float(cpi_w_by_year[years[0]])))
+ rates: dict[int, float] = {}
+ for year in years[1:]:
+ quarter = Decimal(repr(float(cpi_w_by_year[year])))
+ percent = ((quarter / base) - 1) * 100
+ rounded = percent.quantize(Decimal("0.1"), rounding=ROUND_HALF_UP)
+ determination_year = year - 1
+ if rounded > 0:
+ rates[determination_year] = float(rounded / 100)
+ base = quarter
+ else:
+ rates[determination_year] = 0.0
+ return rates
diff --git a/tests/README-tiers.md b/tests/README-tiers.md
index 3fac15136..fd363b0a1 100644
--- a/tests/README-tiers.md
+++ b/tests/README-tiers.md
@@ -7,7 +7,8 @@ beyond pytest's normal collection. Classification uses this precedence:
1. `reproduction_legacy`: `tests/test_gate2_candidate*.py`, the frozen
historical reproduction ledger.
2. `oracle_policyengine`: modules that reference
- `POPULACE_DYNAMICS_PE_US_DIR` or the legacy default
+ `POPULACE_DYNAMICS_PE_US_DIR`, `POPULACE_DYNAMICS_PE_US_PYTHON` (the
+ interpreter with policyengine-us installed) or the legacy default
`~/PolicyEngine/policyengine-us` checkout.
3. `artifact`: the path-exact, design-sanctioned M6 truth-side byte-identity
test (`test_m6_truth_identity.py`). It reads staged PSID when available,
@@ -38,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1
| Tier | Tests at HEAD |
|---|---:|
-| `unit` | 5,418 |
+| `unit` | 5,628 |
| `artifact` | 3,331 |
| `integration_psid` | 1,341 |
| `reproduction_legacy` | 520 |
-| `oracle_policyengine` | 189 |
-| **Total** | **10,799** |
+| `oracle_policyengine` | 207 |
+| **Total** | **11,027** |
diff --git a/tests/bridge/__init__.py b/tests/bridge/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/bridge/test_pe_us_bridge_artifact.py b/tests/bridge/test_pe_us_bridge_artifact.py
new file mode 100644
index 000000000..29dc58ad4
--- /dev/null
+++ b/tests/bridge/test_pe_us_bridge_artifact.py
@@ -0,0 +1,330 @@
+"""The committed PolicyEngine-US bridge results satisfy the review's fixes.
+
+Reads the JSON and Markdown the sample-household script wrote to
+``docs/analysis/pe_us_bridge_20260930/`` (ILLUSTRATIVE households, not
+survey data) and checks the properties the review asked for, without
+starting policyengine-us:
+
+* finding 3: the run used policyengine-us 2.18.0 from PyPI, its installed
+ files matched the RECORD, and the source check passed;
+* finding 1: California's 2026 payment standard is CDSS's $1,233.94,
+ applied to the California cases only and shown with its source;
+* finding 2: the float32 guard traced every changed component and found
+ no step without a cause, and household C's pension is off the $30,000
+ use-tax edge;
+* findings 6, 7 and 11 and the nits: the Commodity Supplemental Food
+ Program and state refundable credits have their own rows, no local path
+ is written, and the invented history carries no PSID fields;
+* the decomposition identity holds in every row, in integer cents;
+* round 2: Montana's credit keeps net household income and its reduction
+ apart (M1); the Medicaid valuation is described exactly, both ways
+ (L2); the script checks refundable credits (L4); half-dollars round up
+ (+1,886.50 shows as +1,887); the guard text states its noise band (N2);
+ and no public file points at a folder outside the repository (N4).
+"""
+
+from __future__ import annotations
+
+import json
+import sys
+from pathlib import Path
+
+import pytest
+
+ROOT = Path(__file__).resolve().parents[2]
+ANALYSIS = ROOT / "docs" / "analysis" / "pe_us_bridge_20260930"
+DESIGN_DOC = ROOT / "docs" / "design" / "pe_us_bridge.md"
+STEM = "pe_us_minimum_benefit_sample_households"
+sys.path.insert(0, str(ROOT / "scripts"))
+
+import pe_us_minimum_benefit_sample_households as script # noqa: E402
+
+CA_PARAMETER = (
+ "gov.states.ca.cdss.state_supplement.payment_standard."
+ "aged_or_disabled.amount.single"
+)
+
+
+@pytest.fixture(scope="module")
+def document():
+ return json.loads((ANALYSIS / f"{STEM}.json").read_text())
+
+
+@pytest.fixture(scope="module")
+def report():
+ return (ANALYSIS / f"{STEM}.md").read_text()
+
+
+def _cents(value):
+ return round(value * 100)
+
+
+def _strings(value):
+ if isinstance(value, dict):
+ for key, item in value.items():
+ yield str(key)
+ yield from _strings(item)
+ elif isinstance(value, list):
+ for item in value:
+ yield from _strings(item)
+ elif isinstance(value, str):
+ yield value
+
+
+def test__artifact__then_it_is_labelled_illustrative(document, report):
+ assert document["label"] == "Illustrative household, not survey data"
+ assert document["worker"]["label"] == document["label"]
+ assert document["label"] in report
+
+
+def test__artifact__then_the_release_is_the_pinned_pypi_one(document):
+ pe = document["provenance"]["policyengine_us"]
+ assert pe["release"] == script.PE_US_RELEASE
+ assert pe["installed"]["version"] == "2.18.0"
+ assert pe["source_check"]["kind"] == "index"
+ assert pe["source_check"]["published"] is True
+ assert pe["source_check"]["reasons"] == []
+ record = pe["record_check"]
+ assert (
+ record["files_checked"] == script.PE_US_RELEASE["package_record_lines"]
+ )
+ assert record["mismatched"] == 0 and record["missing"] == 0
+ assert record["package_record_digest"] == (
+ script.PE_US_RELEASE["package_record_digest"]
+ )
+ assert document["provenance"]["microcosm_dynamics"]["code_dirty"] is False
+
+
+def test__artifact__then_no_local_path_is_written(document):
+ for text in _strings(document):
+ assert not text.startswith(("/", "~")), text
+ assert "/Users/" not in text, text
+
+
+def test__artifact__then_california_uses_the_published_standard(
+ document, report
+):
+ updates = document["provenance"]["policyengine_us"]["parameter_updates"]
+ assert [u["parameter"] for u in updates] == [CA_PARAMETER]
+ update = updates[0]
+ assert update["value"] == 1_233.94
+ assert update["installed_value"] == 1_206.94
+ assert update["source"]["url"].startswith("https://cdss.ca.gov/")
+ assert update["source"]["retrieved"] == "2026-09-30"
+ for row in document["results"]:
+ for variant, overrides in row["parameter_overrides"].items():
+ has = overrides.get(CA_PARAMETER)
+ assert has == (1_233.94 if row["state"] == "CA" else None), (
+ row["household"],
+ row["state"],
+ variant,
+ )
+ assert "1,233.94" in report and update["source"]["url"] in report
+
+
+def test__artifact__then_the_california_supplement_follows_the_standard(
+ document,
+):
+ """Household A keeps $239.94 a month; household B's $1,123 of monthly
+ countable income leaves $110.94 under the published standard."""
+
+ by_case = {(r["household"], r["state"]): r for r in document["results"]}
+ a = by_case[("A", "CA")]["decomposition"]["categories"]["state_benefits"]
+ b = by_case[("B", "CA")]["decomposition"]["categories"]["state_benefits"]
+ assert a["baseline"] == a["reform"] == pytest.approx(12 * 239.94)
+ assert b["baseline"] == pytest.approx(12 * (1_233.94 - 1_123))
+ assert b["reform"] == 0
+
+
+def test__artifact__then_the_float32_guard_found_nothing(document, report):
+ guard = document["float32_guard"]
+ assert guard["comparisons"] == 18
+ assert guard["uncaused_changes"] == 0
+ for row in document["results"]:
+ for variant in ("default", "with_health"):
+ entry = row["float32_guard"][variant]
+ assert entry["uncaused_changes"] == []
+ changed = {
+ c["variable"]
+ for c in row["decomposition"]["components"]
+ if c["change"]
+ }
+ if variant == "default":
+ assert changed <= set(entry["traced_leaves"])
+ assert guard["summary"] in report
+
+
+def test__artifact__then_household_c_is_off_the_use_tax_edge(document):
+ pension = {
+ h["key"]: h["taxable_private_pension_income"]
+ for h in document["households"]
+ }
+ assert pension["C"] == 31_200
+ # California AGI excludes Social Security, so the use tax cannot move.
+ for row in document["results"]:
+ if row["household"] == "C" and row["state"] == "CA":
+ other = row["decomposition"]["categories"]["other_taxes"]
+ assert other["change"] == 0
+
+
+def test__artifact__then_components_sum_exactly_to_net_change(document):
+ for row in document["results"]:
+ d = row["decomposition"]
+ # Components with zero in both runs are omitted from the JSON.
+ assert sum(_cents(c["change"]) for c in d["components"]) == (
+ _cents(d["net_change"])
+ )
+ assert sum(_cents(v["change"]) for v in d["categories"].values()) == (
+ _cents(d["net_change"])
+ )
+ assert _cents(d["reform_net_income"]) - _cents(
+ d["baseline_net_income"]
+ ) == _cents(d["net_change"])
+
+
+def test__artifact__then_rows_name_each_program_and_credit(document, report):
+ assert "csfp" in document["table_rows"]
+ assert "Commodity Supplemental Food Program" in report
+ assert "caseload-limited" in report
+ # Montana's refundable credit on a zero liability is its own row.
+ by_case = {(r["household"], r["state"]): r for r in document["results"]}
+ montana_a = by_case[("A", "MT")]["decomposition"]["categories"]
+ assert montana_a["state_refundable_credits"]["baseline"] == 1_150
+ assert montana_a["state_income_tax"]["baseline"] == 0
+ # Every table and every chart shows the same rows, zeros included.
+ assert "federal_income_tax" in document["chart_rows"]
+ assert "state_income_tax" in document["chart_rows"]
+
+
+def test__artifact__then_the_reform_is_named_plainly(document, report):
+ reform = document["reform"]
+ assert "73% of the aged poverty threshold for 22 years of work" in reform
+ assert "Favreault, Mermin and Steuerle 2006, option 2" in reform
+ assert reform in report
+ assert "Exercise 4, option 2" not in report
+
+
+def test__artifact__then_the_invented_history_has_no_psid_fields(document):
+ coverage = document["worker"]["years_of_coverage"]
+ assert coverage["years"] == 22
+ for field in ("flagged", "unobserved_years", "imputed_years"):
+ assert field not in coverage
+
+
+def test__artifact__then_florida_medicaid_loss_is_explained(document, report):
+ explanations = document["medicaid_explanations"]
+ assert list(explanations) == ["B-FL"]
+ text = explanations["B-FL"]
+ assert "88%" in text and "individual.yaml:44-45" in text
+ assert "QMB-eligible in every month of both runs" in text
+ assert "coverage at average program cost" in report
+ assert "upper-end" not in report
+ # The valuation says what the Medicaid figure is: spending per enrollee
+ # from 2023 spending and October 2024 enrollment, all ages.
+ assert script.MEDICAID_VALUATION in report
+ assert "all ages" in script.MEDICAID_VALUATION
+
+
+def _by_case(document):
+ return {(r["household"], r["state"]): r for r in document["results"]}
+
+
+def test__artifact__then_half_dollars_round_up(document, report):
+ """Household C in Montana gains $1,886.50, shown as $1,887 (``format``
+ rounds half to even, to $1,886)."""
+
+ c_mt = _by_case(document)[("C", "MT")]
+ assert c_mt["decomposition"]["net_change"] == 1_886.50
+ assert c_mt["with_health_benefits_in_net_income"]["net_change"] == (
+ 1_886.50
+ )
+ assert "| C | MT | +2,052 | +1,887 | 92% | +1,887 |" in report
+ assert "+1,886 " not in report and "+1,886." not in report
+ assert "half-dollars rounded up in magnitude" in report
+
+
+def test__artifact__then_montana_credit_is_described_exactly(document):
+ """Round 2's M1: the credit subtracts an income reduction, 5 percent of
+ net household income, not net household income itself."""
+
+ evidence = " ".join(document["states"]["MT"]["evidence"])
+ assert "less net household income" not in evidence
+ assert "less an income reduction" in evidence
+ assert "15-30-2337(8)" in evidence and "15-30-2340(4)" in evidence
+ doc = DESIGN_DOC.read_text()
+ assert "Net household income rises from $1,376" not in doc
+ assert "rises from $27,516 to $29,568" in doc
+ assert "from $1,376 to $1,478" in doc
+
+
+def test__artifact__then_the_medicaid_valuation_is_exact_both_ways(
+ document, report
+):
+ """Round 2's L2: 2023 spending over October 2024 Medicaid and CHIP
+ enrollment; Florida is $9,200 so, and $7,034 with 2023 enrollment."""
+
+ valuation = document["medicaid_valuation"]
+ florida = valuation["per_enrollee"]["FL"]
+ assert florida["release"] == pytest.approx(9_200.24, abs=0.005)
+ assert florida["same_year"] == pytest.approx(7_033.96, abs=0.005)
+ assert florida["spending_entry"] == "2023-01-01"
+ assert florida["enrollment_entry"] == "2024-10-01"
+ text = valuation["text"]
+ assert text in report
+ for phrase in (
+ "Medicaid and CHIP enrollment",
+ "overstate or understate",
+ "not a cash loss",
+ "$7,034 in Florida (not $9,200)",
+ ):
+ assert phrase in text, phrase
+ # Every memo where she is enrolled is the release's ratio.
+ for row in document["results"]:
+ expected = valuation["per_enrollee"][row["state"]]["release"]
+ for memo in row["memo"].values():
+ cost = memo["medicaid_cost"]
+ assert cost == 0 or cost == pytest.approx(expected, abs=0.5)
+ doc = DESIGN_DOC.read_text()
+ assert "may understate" not in doc and "upper-end" not in doc
+
+
+def test__artifact__then_the_script_checked_refundable_credits(document):
+ """Round 2's L4: the doc says credits are checked for wrong-way moves."""
+
+ checks = " ".join(document["invariants_checked"])
+ assert "refundable tax credits never rise" in checks
+ assert "income taxes never fall" in checks
+
+
+def test__artifact__then_the_guard_text_states_its_noise_band(
+ document, report
+):
+ """Round 2's N2: the rule is a cent or four float32 steps."""
+
+ summary = document["float32_guard"]["summary"]
+ assert "held within a cent" not in summary
+ assert "four float32 steps" in summary
+ assert "held within a cent" not in report
+
+
+def test__artifact__then_no_public_file_points_outside_the_repository(
+ document, report
+):
+ """Round 2's N4: the design doc, the JSON and the Markdown name
+ repository paths only."""
+
+ texts = [DESIGN_DOC.read_text(), report, *_strings(document)]
+ for text in texts:
+ assert "microcosm-launch-evidence" not in text
+ assert "/Users/" not in text
+ assert "evidence folder" not in text
+
+
+def test__artifact__then_the_medicaid_limit_is_cited_in_full(document):
+ """Round 2's N5: ``individual.yaml`` is ambiguous beside other files."""
+
+ text = document["medicaid_explanations"]["B-FL"]
+ assert (
+ "parameters/gov/hhs/medicaid/eligibility/categories/"
+ "senior_or_disabled/income/limit/individual.yaml:44-45"
+ ) in text
diff --git a/tests/bridge/test_pe_us_sample_households_script.py b/tests/bridge/test_pe_us_sample_households_script.py
new file mode 100644
index 000000000..7eb62aad6
--- /dev/null
+++ b/tests/bridge/test_pe_us_sample_households_script.py
@@ -0,0 +1,590 @@
+"""The sample-household script's own checks, on INVENTED inputs.
+
+No policyengine-us interpreter is started: the bridge's runner functions
+are replaced with fakes, parameter files are written to a temporary
+release directory, and decompositions are built from small invented
+trees. These pin the script's logic itself; the committed results are
+pinned in ``test_pe_us_bridge_artifact.py``.
+"""
+
+from __future__ import annotations
+
+import sys
+from decimal import ROUND_HALF_UP, Decimal
+from pathlib import Path
+
+import pytest
+from hypothesis import given
+from hypothesis import strategies as st
+
+ROOT = Path(__file__).resolve().parents[2]
+sys.path.insert(0, str(ROOT / "scripts"))
+
+import pe_us_minimum_benefit_sample_households as script # noqa: E402
+
+from populace_dynamics.bridge import policyengine_us as bridge # noqa: E402
+
+UPDATE = script.PARAMETER_UPDATES[0]
+HEALTH = "gov.simulation.include_health_benefits_in_net_income"
+
+
+# ---------------------------------------------------------------------------
+# Parameter updates
+# ---------------------------------------------------------------------------
+def _release(tmp_path, values: str) -> Path:
+ path = tmp_path / UPDATE["file"]
+ path.parent.mkdir(parents=True)
+ path.write_text(f"values:\n{values}")
+ return tmp_path
+
+
+def test__given_no_entry_for_the_year__then_the_update_applies(tmp_path):
+ root = _release(tmp_path, " 1991-01-01: 630\n 2025-01-01: 1_206.94\n")
+ [applied] = script.parameter_updates(root)
+ assert applied["value"] == 1_233.94
+ assert applied["installed_value"] == 1_206.94
+ assert applied["installed_value_dated"] == "2025-01-01"
+ assert len(applied["file_sha256"]) == 64
+
+
+def test__given_the_published_entry__then_the_update_is_dropped(tmp_path):
+ root = _release(
+ tmp_path, " 2025-01-01: 1_206.94\n 2026-01-01: 1_233.94\n"
+ )
+ assert script.parameter_updates(root) == []
+
+
+def test__given_a_different_entry__then_the_script_refuses(tmp_path):
+ root = _release(tmp_path, " 2025-01-01: 1_206.94\n 2026-01-01: 1_250\n")
+ with pytest.raises(ValueError, match="not the published"):
+ script.parameter_updates(root)
+
+
+@pytest.mark.parametrize(
+ ("state", "variant", "expected"),
+ [
+ ("CA", "default", {UPDATE["parameter"]: 1_233.94}),
+ ("CA", "with_health", {UPDATE["parameter"]: 1_233.94, HEALTH: True}),
+ ("FL", "default", {}),
+ ("MT", "with_health", {HEALTH: True}),
+ ],
+)
+def test__given_state_and_variant__then_overrides_are_the_right_ones(
+ state, variant, expected
+):
+ assert script._overrides_for(state, variant, [UPDATE]) == expected
+
+
+@pytest.mark.parametrize(
+ ("values", "on", "expected"),
+ [
+ ({"2018-01-01": 0.88}, "2026-01-01", 0.88),
+ ({"2025-10-01": 298, "2026-10-01": 306}, "2026-10-01", 306),
+ ({"2025-10-01": 298, "2026-10-01": 306}, "2026-09-30", 298),
+ ],
+)
+def test__given_dated_values__then_the_one_in_force_is_read(
+ values, on, expected
+):
+ assert script._dated_value(values, on) == expected
+
+
+def test__given_no_value_in_force__then_reading_is_refused():
+ with pytest.raises(ValueError, match="no value in force"):
+ script._dated_value({"2027-01-01": 1}, "2026-01-01")
+
+
+# ---------------------------------------------------------------------------
+# Paths
+# ---------------------------------------------------------------------------
+@pytest.mark.parametrize(
+ "document",
+ [
+ {"path": "/abs/file.json"},
+ {"nested": [{"python": "~/.venvs/x/bin/python"}]},
+ {"text": "see /Users/someone/file"},
+ ],
+)
+def test__given_a_local_path__then_writing_is_refused(document):
+ with pytest.raises(ValueError, match="local path"):
+ script._no_absolute_paths(document)
+
+
+def test__given_relative_paths__then_writing_is_allowed():
+ script._no_absolute_paths(
+ {"a": "docs/analysis/x.md", "b": ["policyengine_us/p.yaml"]}
+ )
+
+
+# ---------------------------------------------------------------------------
+# The pinned release
+# ---------------------------------------------------------------------------
+def _fake_installation(version="2.18.0"):
+ return bridge.PolicyEngineUSInstallation(
+ python="python",
+ python_version="3.13.9",
+ package_dir="site/policyengine_us",
+ version=version,
+ core_version="3.32.11",
+ location="site",
+ record_path="site/RECORD",
+ record_sha256="0" * 64,
+ direct_url=None,
+ installer="uv",
+ )
+
+
+def _record(**overrides):
+ record = {
+ "prefix": "policyengine_us/",
+ "files_checked": 17_551,
+ "mismatched": [],
+ "missing": [],
+ "extra": [],
+ "package_record_digest": script.PE_US_RELEASE["package_record_digest"],
+ "package_record_lines": 17_551,
+ }
+ record.update(overrides)
+ return record
+
+
+@pytest.fixture
+def fake_release(monkeypatch):
+ state = {"installation": _fake_installation(), "record": _record()}
+ monkeypatch.setattr(
+ bridge, "inspect_installation", lambda python: state["installation"]
+ )
+ monkeypatch.setattr(
+ bridge, "verify_record", lambda installation: state["record"]
+ )
+ return state
+
+
+def test__given_the_pinned_release__then_provenance_is_recorded(
+ fake_release,
+):
+ installation, provenance = script.pinned_release(None)
+ assert installation.version == "2.18.0"
+ assert provenance["release"] == script.PE_US_RELEASE
+ assert provenance["source_check"]["published"] is True
+ assert provenance["record_check"]["extra"] == 0
+
+
+@pytest.mark.parametrize(
+ ("change", "match"),
+ [
+ ({"installation": _fake_installation("2.17.3")}, "version 2.17.3"),
+ ({"record": _record(mismatched=["policyengine_us/a.py"])}, "differ"),
+ ({"record": _record(extra=["policyengine_us/p/x.yaml"])}, "not in"),
+ ({"record": _record(package_record_digest="0" * 64)}, "RECORD"),
+ ],
+)
+def test__given_anything_but_the_pinned_release__then_the_script_refuses(
+ fake_release, change, match
+):
+ fake_release.update(change)
+ with pytest.raises(ValueError, match=match):
+ script.pinned_release(None)
+
+
+# ---------------------------------------------------------------------------
+# Invariants, the float32 guard and the Medicaid text
+# ---------------------------------------------------------------------------
+TREE = bridge.ComponentTree(
+ "household_net_income",
+ {
+ "household_net_income": bridge.NET_INCOME_DEFINITION,
+ "household_benefits": (("social_security", 1), ("ssi", 1)),
+ },
+)
+ZERO = {
+ "household_market_income": 0.0,
+ "household_refundable_tax_credits": 0.0,
+ "household_tax_before_refundable_credits": 0.0,
+ "household_health_costs": 0.0,
+}
+
+
+def _values(social_security, ssi):
+ leaves = {**ZERO, "social_security": social_security, "ssi": ssi}
+ leaves["household_benefits"] = social_security + ssi
+ leaves["household_net_income"] = social_security + ssi
+ return leaves
+
+
+def _row(baseline_ssi=3_252.0, reform_ssi=1_200.0, uncaused=()):
+ baseline = _values(8_916.0, baseline_ssi)
+ reform = _values(10_968.0, reform_ssi)
+ decomposition = bridge.decompose(TREE, baseline, reform)
+ guard = {"uncaused_changes": list(uncaused)}
+ return {
+ "household": "A",
+ "state": "FL",
+ "baseline_social_security_annual": 8_916.0,
+ "reform_social_security_annual": 10_968.0,
+ "decomposition": decomposition,
+ "with_health": decomposition,
+ "float32_guard": {"default": guard, "with_health": guard},
+ "situations": {"baseline": {}, "reform": {}},
+ "overrides": {"default": {}, "with_health": {}},
+ "run_values": {
+ variant: {"baseline": baseline, "reform": reform}
+ for variant in script.VARIANTS
+ },
+ }
+
+
+def test__given_a_consistent_row__then_the_invariants_pass():
+ checks = script.check_invariants([_row()])
+ assert any("float32 guard" in check for check in checks)
+
+
+def test__given_ssi_rising_with_social_security__then_writing_is_refused():
+ with pytest.raises(script.InvariantError, match="ssi rose"):
+ script.check_invariants([_row(baseline_ssi=0.0, reform_ssi=100.0)])
+
+
+@pytest.mark.parametrize(
+ "name", ["federal_refundable_credits", "state_refundable_credits"]
+)
+def test__given_a_refundable_credit_rising__then_writing_is_refused(name):
+ """Round 2's L4: the check covers the refundable credits the design
+ doc lists, not only benefits and income taxes."""
+
+ row = _row()
+ decomposition = row["decomposition"]
+ categories = decomposition.by_category()
+ # Move a dollar from SSI (still falling) to the credit, so the
+ # identity and the Social Security check still hold.
+ categories["ssi"]["change"] -= 100
+ categories[name]["change"] += 100
+ row["decomposition"] = row["with_health"] = _Categories(
+ decomposition, categories
+ )
+ with pytest.raises(script.InvariantError, match=f"{name} rose"):
+ script.check_invariants([row])
+
+
+class _Categories:
+ """A decomposition whose categories are replaced (one moved up)."""
+
+ def __init__(self, decomposition, categories):
+ self._decomposition = decomposition
+ self._categories = categories
+ self.components = decomposition.components
+ self.net_change_cents = decomposition.net_change_cents
+ self.reported_gap_cents = decomposition.reported_gap_cents
+
+ def by_category(self):
+ return self._categories
+
+
+def test__wrong_way_categories__then_they_cover_benefits_taxes_and_credits():
+ assert set(script.WRONG_WAY_CATEGORIES) == {
+ "ssi",
+ "snap",
+ "csfp",
+ "state_benefits",
+ "federal_income_tax",
+ "state_income_tax",
+ "federal_refundable_credits",
+ "state_refundable_credits",
+ }
+ assert set(script.WRONG_WAY_CATEGORIES) <= set(bridge.CATEGORY_ORDER)
+
+
+def test__given_an_uncaused_change__then_writing_is_refused():
+ row = _row(uncaused=[{"variable": "ca_use_tax@2026"}])
+ with pytest.raises(script.InvariantError, match="float32 guard"):
+ script.check_invariants([row])
+
+
+def test__invariant_error__then_it_is_raised_not_asserted():
+ """``python -O`` strips ``assert``; the checks must still raise."""
+
+ source = (ROOT / "scripts" / f"{script.OUTPUT_STEM}.py").read_text()
+ body = source.split("def check_invariants", 1)[1].split("\ndef ", 1)[0]
+ assert "assert " not in body
+
+
+def _trace(value, children=(), is_input=False):
+ return bridge.TraceNode(
+ value=(value,), children=tuple(children), input=is_input
+ )
+
+
+def _traces(ssi_traced):
+ """Traces of the invented row: SSI read from Social Security."""
+
+ out = {}
+ for scenario, ss, ssi in (
+ ("baseline", 8_916.0, 3_252.0),
+ ("reform", 10_968.0, ssi_traced),
+ ):
+ nodes = {
+ "social_security@2026": _trace(ss, ["ssr@2026"]),
+ "ssr@2026": _trace(ss, is_input=True),
+ "ssi@2026": _trace(ssi, ["social_security@2026"]),
+ }
+ for variant in script.VARIANTS:
+ out[script._case_id("A", "FL", scenario, variant)] = nodes
+ return out
+
+
+def test__given_traced_values_equal_to_the_run__then_the_guard_records_them(
+ monkeypatch,
+):
+ row = _row()
+ monkeypatch.setattr(
+ bridge, "trace_policyengine_us", lambda *a, **k: _traces(1_200.0)
+ )
+ script.float32_guard([row], None)
+ guard = row["float32_guard"]["default"]
+ assert guard["traced_leaves"] == ["social_security", "ssi"]
+ assert guard["uncaused_changes"] == []
+ assert guard["small_changes"] == []
+
+
+def test__given_a_traced_value_off_the_run__then_writing_is_refused(
+ monkeypatch,
+):
+ """The traced-versus-untraced differential check."""
+
+ monkeypatch.setattr(
+ bridge, "trace_policyengine_us", lambda *a, **k: _traces(1_250.0)
+ )
+ with pytest.raises(script.InvariantError, match="traced"):
+ script.float32_guard([_row()], None)
+
+
+def _medicaid_row(*, eligible_after=0.0, msp_after=5_335.0, qmb=1.0):
+ row = _row()
+ # With health counted the change differs from the default one.
+ row["with_health"] = bridge.decompose(
+ TREE, _values(8_916.0, 3_252.0), _values(10_968.0, 0.0)
+ )
+ qmb_months = {name: qmb for name in script.QMB_MONTHS}
+ income = "medicaid_optional_senior_or_disabled_countable_income"
+ limit = "medicaid_optional_senior_or_disabled_income_limit"
+ row["memo"] = {
+ "baseline": {
+ "is_medicaid_eligible": 1.0,
+ income: 13_476.0,
+ limit: 14_044.8,
+ "msp_cost": 0.0,
+ **qmb_months,
+ },
+ "reform": {
+ "is_medicaid_eligible": eligible_after,
+ income: 15_528.0,
+ limit: 14_044.8,
+ "msp_cost": msp_after,
+ **qmb_months,
+ },
+ }
+ return row
+
+
+def test__given_medicaid_ending_at_the_limit__then_the_text_explains_it():
+ out = script.medicaid_explanations([_medicaid_row()], {"FL": 0.88})
+ assert list(out) == [("A", "FL")]
+ text = out[("A", "FL")]
+ assert "$13,476 to $15,528" in text and "88%" in text
+ assert "QMB-eligible in every month of both runs" in text
+
+
+def test__given_no_msp_in_the_reform__then_the_qmb_sentence_is_left_out():
+ """Finding 9 of the code review: the sentence needs MSP to appear."""
+
+ out = script.medicaid_explanations(
+ [_medicaid_row(msp_after=0.0)], {"FL": 0.88}
+ )
+ assert "QMB" not in out[("A", "FL")]
+
+
+def test__given_another_reason_for_the_difference__then_it_is_refused():
+ with pytest.raises(script.InvariantError, match="does not describe"):
+ script.medicaid_explanations(
+ [_medicaid_row(eligible_after=1.0)], {"FL": 0.88}
+ )
+
+
+# ---------------------------------------------------------------------------
+# Money display
+# ---------------------------------------------------------------------------
+@pytest.mark.parametrize(
+ ("cents", "signed", "text"),
+ [
+ (188_650, True, "+1,887"),
+ (-188_650, True, "−1,887"),
+ (188_649, True, "+1,886"),
+ (50, False, "1"),
+ (150, False, "2"),
+ (250, False, "3"),
+ (49, True, "0"),
+ (-49, True, "0"),
+ (0, True, "0"),
+ (-273_744, True, "−2,737"),
+ (1_234_567_850, False, "12,345,679"),
+ ],
+)
+def test__given_cents__then_half_dollars_round_up(cents, signed, text):
+ """Round 2's rounding nit: C-MT's +1,886.50 shows as +1,887, where
+ ``format`` rounds half to even (1,886)."""
+
+ assert script._money(cents, signed=signed) == text
+
+
+@given(st.integers(-(10**12), 10**12), st.booleans())
+def test__given_any_cents__then_money_is_the_half_up_dollar(cents, signed):
+ text = script._money(cents, signed=signed)
+ dollars = int(text.lstrip("+−").replace(",", ""))
+ # Half up in magnitude, from exact decimal arithmetic.
+ assert dollars == int(
+ (Decimal(abs(cents)) / 100).quantize(Decimal(1), ROUND_HALF_UP)
+ )
+ if dollars == 0:
+ assert text == "0"
+ elif cents < 0:
+ assert text.startswith("−")
+ else:
+ assert text.startswith("+") is signed
+
+
+@pytest.mark.parametrize(
+ "cents", [1.5, True, float("nan"), float("inf"), "188650"]
+)
+def test__given_non_whole_cents__then_money_is_refused(cents):
+ with pytest.raises(ValueError):
+ script._money(cents)
+
+
+# ---------------------------------------------------------------------------
+# The Medicaid valuation
+# ---------------------------------------------------------------------------
+def _medicaid_release(tmp_path, enrollment_entry="2024-10-01"):
+ totals = tmp_path / script.PARAMETER_PREFIX / script.MEDICAID_TOTALS
+ totals.mkdir(parents=True)
+ spending = {"CA": 124_063_730_563, "FL": 34_641_023_901, "MT": 1_000}
+ enrollment = {
+ "CA": (14_176_618, 13_431_928),
+ "FL": (4_924_826, 3_765_231),
+ "MT": (10, 8),
+ }
+ totals.joinpath("spending.yaml").write_text(
+ "".join(
+ f"{state}:\n 2022-01-01: 1\n 2023-01-01: {value}\n"
+ for state, value in spending.items()
+ )
+ )
+ totals.joinpath("enrollment.yaml").write_text(
+ "".join(
+ f"{state}:\n 2023-01-01: {same}\n {enrollment_entry}: {later}\n"
+ for state, (same, later) in enrollment.items()
+ )
+ )
+ return tmp_path
+
+
+def test__given_the_release_totals__then_both_ratios_are_computed(tmp_path):
+ """Round 2's L2: Florida is $9,200 as the release computes it (2023
+ spending over October 2024 enrollment) and $7,034 with 2023
+ enrollment."""
+
+ out = script.medicaid_per_enrollee(_medicaid_release(tmp_path))
+ florida = out["FL"]
+ assert florida["release"] == pytest.approx(9_200.24, abs=0.005)
+ assert florida["same_year"] == pytest.approx(7_033.96, abs=0.005)
+ assert florida["spending_entry"] == "2023-01-01"
+ assert florida["enrollment_entry"] == "2024-10-01"
+ assert out["MT"]["release"] == 125 and out["MT"]["same_year"] == 100
+
+
+def test__given_other_enrollment_dates__then_the_valuation_is_refused(
+ tmp_path,
+):
+ """The text names 2023 spending and October 2024 enrollment; a release
+ with other entries cannot reuse it."""
+
+ with pytest.raises(ValueError, match="not the entries"):
+ script.medicaid_per_enrollee(
+ _medicaid_release(tmp_path, enrollment_entry="2025-10-01")
+ )
+
+
+def test__valuation_text__then_it_is_exact_and_two_sided(tmp_path):
+ per_enrollee = script.medicaid_per_enrollee(_medicaid_release(tmp_path))
+ text = script.medicaid_valuation_text(per_enrollee)
+ assert text.startswith(script.MEDICAID_VALUATION)
+ assert (
+ "2023 Medicaid spending divided by its October 2024 Medicaid and "
+ "CHIP enrollment"
+ ) in text
+ assert "overstate or understate" in text
+ assert "not a cash loss" in text
+ assert "upper-end" not in text and "may understate" not in text
+ # Florida first: the one case where the valuation moves a result.
+ assert "it would be $7,034 in Florida (not $9,200), $8,751 in " in text
+
+
+def test__given_memo_off_the_ratio__then_writing_is_refused(tmp_path):
+ per_enrollee = script.medicaid_per_enrollee(_medicaid_release(tmp_path))
+ row = _medicaid_row()
+ row["memo"]["baseline"]["medicaid_cost"] = 9_200.24
+ row["memo"]["reform"]["medicaid_cost"] = 0.0
+ script.check_medicaid_memo([row], per_enrollee)
+ row["memo"]["baseline"]["medicaid_cost"] = 7_033.96
+ with pytest.raises(script.InvariantError, match="spending over"):
+ script.check_medicaid_memo([row], per_enrollee)
+
+
+def test__given_with_health_medicaid_off_the_ratio__then_it_is_refused(
+ tmp_path,
+):
+ """The with-health column counts the ``medicaid_cost`` leaf; it is
+ checked against the ratio as the memo is."""
+
+ per_enrollee = script.medicaid_per_enrollee(_medicaid_release(tmp_path))
+ tree = bridge.ComponentTree(
+ "household_net_income",
+ {
+ "household_net_income": bridge.NET_INCOME_DEFINITION,
+ "household_benefits": (
+ ("social_security", 1),
+ ("ssi", 1),
+ ("medicaid_cost", 1),
+ ),
+ },
+ )
+
+ def values(medicaid):
+ leaves = {**ZERO, "social_security": 8_916.0, "ssi": 0.0}
+ leaves["medicaid_cost"] = medicaid
+ leaves["household_benefits"] = 8_916.0 + medicaid
+ leaves["household_net_income"] = 8_916.0 + medicaid
+ return leaves
+
+ row = _medicaid_row()
+ row["memo"]["baseline"]["medicaid_cost"] = 0.0
+ row["memo"]["reform"]["medicaid_cost"] = 0.0
+ row["with_health"] = bridge.decompose(tree, values(9_200.24), values(0))
+ script.check_medicaid_memo([row], per_enrollee)
+ row["with_health"] = bridge.decompose(tree, values(7_033.96), values(0))
+ with pytest.raises(script.InvariantError, match="with-health baseline"):
+ script.check_medicaid_memo([row], per_enrollee)
+
+
+# ---------------------------------------------------------------------------
+# State evidence
+# ---------------------------------------------------------------------------
+def test__montana_evidence__then_income_and_its_reduction_are_separate():
+ """Round 2's M1: the credit subtracts an income reduction (5 percent
+ of net household income), not net household income itself."""
+
+ evidence = " ".join(script.STATES["MT"]["evidence"])
+ assert "less net household income" not in evidence
+ assert "less an income reduction" in evidence
+ assert "despite its name, this variable is the income reduction" in (
+ evidence
+ )
+ assert "15-30-2337(8)" in evidence and "15-30-2340(4)" in evidence
diff --git a/tests/bridge/test_policyengine_us_bridge.py b/tests/bridge/test_policyengine_us_bridge.py
new file mode 100644
index 000000000..0709599c3
--- /dev/null
+++ b/tests/bridge/test_policyengine_us_bridge.py
@@ -0,0 +1,1436 @@
+"""Properties of the PolicyEngine-US bridge's pure-Python parts.
+
+No policyengine-us import and no subprocess: the situation builder, the
+decomposition and the COLA helpers are checked with Hypothesis on INVENTED
+households, trees and amounts. The live runner is exercised in
+``test_policyengine_us_bridge_oracle.py``.
+"""
+
+from __future__ import annotations
+
+import base64
+import hashlib
+import itertools
+import json
+import math
+import subprocess
+from decimal import Decimal
+
+import numpy as np
+import pytest
+from hypothesis import given, settings
+from hypothesis import strategies as st
+
+from populace_dynamics.bridge import policyengine_us as bridge
+
+AMOUNT = st.floats(
+ min_value=0.0, max_value=1e7, allow_nan=False, allow_infinity=False
+)
+IDS = st.lists(
+ st.from_regex(r"[a-z]{1,6}", fullmatch=True),
+ min_size=1,
+ max_size=5,
+ unique=True,
+)
+
+
+@st.composite
+def partitions(draw, members, max_group=None):
+ """Split ``members`` into nonempty groups (each at most ``max_group``)."""
+
+ labels = draw(
+ st.lists(
+ st.integers(0, len(members) - 1),
+ min_size=len(members),
+ max_size=len(members),
+ )
+ )
+ groups: dict[int, list[str]] = {}
+ for member, label in zip(members, labels, strict=True):
+ groups.setdefault(label, []).append(member)
+ out = []
+ for group in groups.values():
+ size = max_group or len(group)
+ out.extend(
+ tuple(group[i : i + size]) for i in range(0, len(group), size)
+ )
+ return tuple(out)
+
+
+@st.composite
+def people(draw):
+ ids = draw(IDS)
+ return tuple(
+ bridge.BridgePerson(
+ person_id=pid,
+ age=draw(st.integers(0, 125)),
+ medicare_quarters_of_coverage=draw(
+ st.none() | st.integers(0, 200)
+ ),
+ **{name: draw(AMOUNT) for name in bridge.AMOUNT_FIELDS},
+ )
+ for pid in ids
+ )
+
+
+@st.composite
+def households(draw):
+ members = draw(people())
+ ids = [person.person_id for person in members]
+ return bridge.BridgeHousehold(
+ household_id=draw(st.from_regex(r"h[a-z0-9]{0,6}", fullmatch=True)),
+ state=draw(st.sampled_from(sorted(bridge.STATE_FIPS))),
+ people=members,
+ tax_units=draw(partitions(ids)),
+ spm_units=draw(partitions(ids)),
+ marital_units=draw(partitions(ids, max_group=2)),
+ families=draw(st.none() | partitions(ids)),
+ has_heating_cooling_expense=draw(st.booleans()),
+ takes_up_housing_assistance=draw(st.booleans()),
+ food_preparation_allowed=draw(st.booleans()),
+ )
+
+
+YEAR = st.integers(2000, 2100)
+GROUP_ENTITIES = (
+ "tax_units",
+ "spm_units",
+ "marital_units",
+ "families",
+ "households",
+)
+
+
+# ---------------------------------------------------------------------------
+# The situation builder
+# ---------------------------------------------------------------------------
+@given(households(), YEAR)
+def test__given_household__then_every_person_is_in_exactly_one_unit(
+ household, year
+):
+ situation = bridge.to_situation(household, year)
+ ids = sorted(person.person_id for person in household.people)
+ assert sorted(situation["people"]) == ids
+ for plural in GROUP_ENTITIES:
+ members = [
+ member
+ for entry in situation[plural].values()
+ for member in entry["members"]
+ ]
+ assert sorted(members) == ids, plural
+ assert len(situation["households"]) == 1
+
+
+@given(households(), YEAR)
+def test__given_household__then_amounts_ages_and_state_round_trip(
+ household, year
+):
+ situation = bridge.to_situation(household, year)
+ assert bridge.person_inputs_from_situation(situation, year) == {
+ person.person_id: person.amounts() for person in household.people
+ }
+ period = str(year)
+ for person in household.people:
+ entry = situation["people"][person.person_id]
+ assert entry["age"] == {period: person.age}
+ quarters = entry.get("medicare_quarters_of_coverage")
+ expected = person.medicare_quarters_of_coverage
+ assert quarters == (None if expected is None else {period: expected})
+ (entry,) = situation["households"].values()
+ assert entry["state_fips"] == {period: bridge.STATE_FIPS[household.state]}
+
+
+@given(households(), YEAR)
+def test__given_household__then_situation_is_deterministic_json(
+ household, year
+):
+ first = bridge.to_situation(household, year)
+ second = bridge.to_situation(household, year)
+ assert first == second
+ assert json.dumps(first, sort_keys=True) == json.dumps(
+ second, sort_keys=True
+ )
+ # The situation is plain JSON (the runner sends it on stdin).
+ assert json.loads(json.dumps(first)) == first
+
+
+@given(
+ st.sampled_from(bridge.AMOUNT_FIELDS),
+ st.floats(max_value=-1e-12, allow_nan=False, allow_infinity=False),
+)
+def test__given_negative_amount__then_person_is_refused(name, value):
+ with pytest.raises(ValueError, match="nonnegative"):
+ bridge.BridgePerson("p", 70, **{name: value})
+
+
+@pytest.mark.parametrize("value", [math.nan, math.inf, -math.inf])
+@pytest.mark.parametrize("name", bridge.AMOUNT_FIELDS)
+def test__given_nonfinite_amount__then_person_is_refused(name, value):
+ with pytest.raises(ValueError, match="finite"):
+ bridge.BridgePerson("p", 70, **{name: value})
+
+
+@pytest.mark.parametrize("value", [True, "1", None])
+def test__given_non_number_amount__then_person_is_refused(value):
+ with pytest.raises(TypeError):
+ bridge.BridgePerson("p", 70, social_security_retirement=value)
+
+
+@pytest.mark.parametrize("age", [-1, 126, 67.5, True])
+def test__given_bad_age__then_person_is_refused(age):
+ with pytest.raises((ValueError, TypeError)):
+ bridge.BridgePerson("p", age)
+
+
+def _two_people():
+ return (bridge.BridgePerson("a", 70), bridge.BridgePerson("b", 68))
+
+
+@pytest.mark.parametrize(
+ "units",
+ [
+ (("a",),), # b missing
+ (("a", "b"), ("b",)), # b twice
+ (("a", "b", "c"),), # unknown person
+ ((),), # empty group
+ ],
+)
+@pytest.mark.parametrize("kind", ["tax_units", "spm_units", "marital_units"])
+def test__given_bad_partition__then_household_is_refused(units, kind):
+ good = (("a", "b"),)
+ fields = {"tax_units": good, "spm_units": good, "marital_units": good}
+ fields[kind] = units
+ with pytest.raises(ValueError):
+ bridge.BridgeHousehold("h", "CA", _two_people(), **fields)
+
+
+def test__given_three_person_marital_unit__then_household_is_refused():
+ trio = (*_two_people(), bridge.BridgePerson("c", 30))
+ group = (("a", "b", "c"),)
+ with pytest.raises(ValueError, match="at most two"):
+ bridge.BridgeHousehold("h", "CA", trio, group, group, group)
+
+
+@pytest.mark.parametrize(
+ "units",
+ [
+ ("ab",), # a bare string as a group: once read as ("a", "b")
+ "ab", # a bare string as the groups
+ ],
+)
+def test__given_bare_string_group__then_household_is_refused(units):
+ people = (bridge.BridgePerson("a", 70), bridge.BridgePerson("b", 68))
+ good = (("a", "b"),)
+ with pytest.raises(TypeError, match="sequence of ids|groups of ids"):
+ bridge.BridgeHousehold("h", "CA", people, units, good, good)
+
+
+def test__given_generators_as_groups__then_household_is_built():
+ people = (bridge.BridgePerson("a", 70), bridge.BridgePerson("b", 68))
+ household = bridge.BridgeHousehold(
+ "h",
+ "CA",
+ people,
+ (group for group in (("a", "b"),)),
+ (group for group in (("a",), ("b",))),
+ (group for group in (("a", "b"),)),
+ )
+ assert household.tax_units == (("a", "b"),)
+ assert household.spm_units == (("a",), ("b",))
+
+
+def test__given_unknown_state_or_duplicate_ids__then_household_is_refused():
+ group = (("a", "b"),)
+ with pytest.raises(ValueError, match="STATE_FIPS"):
+ bridge.BridgeHousehold("h", "PR", _two_people(), group, group, group)
+ twins = (bridge.BridgePerson("a", 70), bridge.BridgePerson("a", 70))
+ with pytest.raises(ValueError, match="unique"):
+ bridge.BridgeHousehold("h", "CA", twins, group, group, group)
+
+
+def test__state_fips_table__then_codes_are_distinct_census_codes():
+ assert len(bridge.STATE_FIPS) == 51
+ assert len(set(bridge.STATE_FIPS.values())) == 51
+ assert all(1 <= code <= 56 for code in bridge.STATE_FIPS.values())
+
+
+# ---------------------------------------------------------------------------
+# The decomposition
+# ---------------------------------------------------------------------------
+CENTS = st.integers(-(10**9), 10**9).map(lambda cents: cents / 100)
+
+
+def _values(tree, leaf_values):
+ values = dict(leaf_values)
+
+ def value(name):
+ if tree.is_leaf(name):
+ return values[name]
+ total = math.fsum(
+ sign * value(part) for part, sign in tree.children[name]
+ )
+ values[name] = total
+ return total
+
+ value(tree.root)
+ return values
+
+
+@st.composite
+def trees(draw, root="root"):
+ counter = itertools.count()
+ children: dict[str, tuple[tuple[str, int], ...]] = {}
+
+ def build(name, depth):
+ parts = []
+ for _ in range(draw(st.integers(1, 4))):
+ child = f"v{next(counter)}"
+ parts.append((child, draw(st.sampled_from((1, -1)))))
+ if depth < 3 and draw(st.booleans()):
+ build(child, depth + 1)
+ children[name] = tuple(parts)
+
+ build(root, 0)
+ return bridge.ComponentTree(root, children)
+
+
+@st.composite
+def runs(draw, amounts=CENTS):
+ tree = draw(trees())
+ leaves = [name for name, _, _ in tree.leaves()]
+ baseline = _values(tree, {name: draw(amounts) for name in leaves})
+ reform = _values(tree, {name: draw(amounts) for name in leaves})
+ return tree, baseline, reform
+
+
+def _decompose(tree, baseline, reform, **kwargs):
+ return bridge.decompose(
+ tree, baseline, reform, expected_definition=None, **kwargs
+ )
+
+
+@settings(max_examples=300)
+@given(runs())
+def test__given_runs__then_components_sum_exactly_to_net_change(run):
+ tree, baseline, reform = run
+ result = _decompose(tree, baseline, reform)
+ changes = [component.change_cents for component in result.components]
+ assert sum(changes) == result.net_change_cents
+ expected = bridge.to_cents(reform[tree.root]) - bridge.to_cents(
+ baseline[tree.root]
+ )
+ assert result.net_change_cents == expected
+ assert result.reported_gap_cents == 0
+ categories = result.by_category()
+ assert sum(entry["change"] for entry in categories.values()) == sum(
+ changes
+ )
+ assert sum(entry["baseline"] for entry in categories.values()) == (
+ result.baseline_net_cents
+ )
+ # Every leaf appears once, with the product of the signs on its path.
+ assert len(result.components) == len(tree.leaves())
+
+
+@given(runs())
+def test__given_reform_equal_to_baseline__then_every_change_is_zero(run):
+ tree, baseline, _ = run
+ result = _decompose(tree, baseline, dict(baseline))
+ assert result.net_change_cents == 0
+ assert all(c.change_cents == 0 for c in result.components)
+ assert all(entry["change"] == 0 for entry in result.by_category().values())
+
+
+@given(runs())
+def test__given_same_inputs__then_decomposition_is_deterministic(run):
+ tree, baseline, reform = run
+ first = _decompose(tree, baseline, reform).as_dict()
+ second = _decompose(tree, dict(baseline), dict(reform)).as_dict()
+ assert json.dumps(first, sort_keys=True) == json.dumps(
+ second, sort_keys=True
+ )
+
+
+@given(runs(), st.randoms(use_true_random=False))
+def test__given_reordered_parts__then_changes_are_unchanged(run, random):
+ tree, baseline, reform = run
+ shuffled = {}
+ for name, parts in tree.children.items():
+ parts = list(parts)
+ random.shuffle(parts)
+ shuffled[name] = tuple(parts)
+ other = bridge.ComponentTree(tree.root, shuffled)
+ first = _decompose(tree, baseline, reform)
+ second = _decompose(other, baseline, reform)
+ assert first.net_change_cents == second.net_change_cents
+ assert sorted((c.variable, c.change_cents) for c in first.components) == (
+ sorted((c.variable, c.change_cents) for c in second.components)
+ )
+
+
+@given(runs(), st.floats(0.51, 1e6), st.data())
+def test__given_inconsistent_aggregate__then_decomposition_is_refused(
+ run, shift, data
+):
+ tree, baseline, reform = run
+ name = data.draw(st.sampled_from(tree.aggregates()))
+ broken = dict(reform)
+ broken[name] = broken[name] + shift
+ with pytest.raises(bridge.AggregateMismatchError):
+ _decompose(tree, baseline, broken)
+
+
+def test__given_nan_intermediate_aggregate__then_decomposition_is_refused():
+ """The review's minimal counterexample (finding 4).
+
+ root -> mid -> (x, y) with mid = nan, x = 1, y = 2 and root = 3 once
+ decomposed without error: ``max(0.0, nan)`` is ``0.0``.
+ """
+
+ tree = bridge.ComponentTree(
+ "root",
+ {"root": (("mid", 1),), "mid": (("x", 1), ("y", 1))},
+ )
+ values = {"root": 3.0, "mid": math.nan, "x": 1.0, "y": 2.0}
+ with pytest.raises(bridge.AggregateMismatchError, match="not finite"):
+ _decompose(tree, values, values)
+
+
+@given(
+ runs(),
+ st.sampled_from([math.nan, math.inf, -math.inf]),
+ st.data(),
+)
+def test__given_any_nonfinite_value__then_decomposition_is_refused(
+ run, bad, data
+):
+ tree, baseline, reform = run
+ name = data.draw(st.sampled_from(sorted(reform)))
+ broken = dict(reform)
+ broken[name] = bad
+ with pytest.raises(bridge.AggregateMismatchError, match="not finite"):
+ _decompose(tree, baseline, broken)
+
+
+@settings(max_examples=100)
+@given(
+ runs(
+ amounts=st.floats(
+ min_value=-1e6,
+ max_value=1e6,
+ allow_nan=False,
+ allow_infinity=False,
+ )
+ )
+)
+def test__given_arbitrary_floats__then_identity_still_holds_in_cents(run):
+ tree, baseline, reform = run
+ result = _decompose(tree, baseline, reform)
+ assert (
+ sum(c.change_cents for c in result.components)
+ == result.net_change_cents
+ )
+ # Rounding each leaf to cents moves the root by at most half a cent a
+ # leaf.
+ assert abs(result.reported_gap_cents) <= len(result.components) + 1
+
+
+def _net_income_tree():
+ children = {
+ "household_net_income": bridge.NET_INCOME_DEFINITION,
+ "household_benefits": (("social_security", 1), ("ssi", 1)),
+ "household_tax_before_refundable_credits": (
+ ("income_tax_before_refundable_credits", 1),
+ ),
+ }
+ return bridge.ComponentTree("household_net_income", children)
+
+
+def test__given_net_income_tree__then_signs_and_categories_follow_the_definition():
+ tree = _net_income_tree()
+ leaves = {
+ "household_market_income": 0.0,
+ "social_security": 9000.0,
+ "ssi": 3000.0,
+ "household_refundable_tax_credits": 0.0,
+ "income_tax_before_refundable_credits": 100.0,
+ "household_health_costs": 0.0,
+ }
+ baseline = _values(tree, leaves)
+ reform = _values(
+ tree,
+ {
+ **leaves,
+ "social_security": 11000.0,
+ "ssi": 1000.0,
+ "income_tax_before_refundable_credits": 150.0,
+ },
+ )
+ result = bridge.decompose(tree, baseline, reform)
+ by_variable = {c.variable: c for c in result.components}
+ assert by_variable["income_tax_before_refundable_credits"].sign == -1
+ assert by_variable[
+ "income_tax_before_refundable_credits"
+ ].change_cents == (-5000)
+ categories = result.by_category()
+ assert categories["social_security"]["change"] == 200000
+ assert categories["ssi"]["change"] == -200000
+ assert categories["federal_income_tax"]["change"] == -5000
+ assert result.net_change_cents == -5000
+
+
+def test__given_other_net_income_definition__then_drift_is_refused():
+ tree = _net_income_tree()
+ drifted = bridge.ComponentTree(
+ tree.root,
+ {
+ **tree.children,
+ "household_net_income": bridge.NET_INCOME_DEFINITION[:-1],
+ },
+ )
+ values = {name: 0.0 for name in ("household_net_income",)}
+ for name, _ in bridge.NET_INCOME_DEFINITION:
+ values[name] = 0.0
+ values.update(
+ social_security=0.0,
+ ssi=0.0,
+ income_tax_before_refundable_credits=0.0,
+ )
+ with pytest.raises(bridge.DefinitionDriftError):
+ bridge.decompose(drifted, values, values)
+ with pytest.raises(bridge.DefinitionDriftError):
+ bridge.decompose(tree, values, values, reform_tree=drifted)
+
+
+@pytest.mark.parametrize(
+ ("variable", "path", "category"),
+ [
+ ("social_security", ("household_benefits",), "social_security"),
+ ("ssi", ("household_benefits",), "ssi"),
+ ("snap", ("household_benefits",), "snap"),
+ (
+ "ca_state_supplement",
+ ("household_benefits", "household_state_benefits"),
+ "state_benefits",
+ ),
+ (
+ "income_tax_before_refundable_credits",
+ ("household_tax_before_refundable_credits",),
+ "federal_income_tax",
+ ),
+ (
+ "income_tax_refundable_credits",
+ ("household_refundable_tax_credits",),
+ "federal_refundable_credits",
+ ),
+ (
+ "mt_refundable_credits",
+ (
+ "household_refundable_tax_credits",
+ "household_refundable_state_tax_credits",
+ ),
+ "state_refundable_credits",
+ ),
+ (
+ "state_income_tax_before_refundable_credits",
+ (
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits",
+ ),
+ "state_income_tax",
+ ),
+ (
+ "state_use_tax",
+ (
+ "household_tax_before_refundable_credits",
+ "household_state_tax_before_refundable_credits",
+ ),
+ "other_taxes",
+ ),
+ ("pension_income", ("household_market_income",), "market_income"),
+ (
+ "medicaid_cost",
+ ("household_benefits", "household_health_benefits"),
+ "health_net",
+ ),
+ (
+ "commodity_supplemental_food_program",
+ ("household_benefits",),
+ "csfp",
+ ),
+ ("wic", ("household_benefits",), "other_benefits"),
+ ],
+)
+def test__given_leaf__then_category_is_the_documented_one(
+ variable, path, category
+):
+ assert bridge.category_for(variable, ("household_net_income", *path)) == (
+ category
+ )
+ assert category in bridge.CATEGORY_ORDER
+
+
+def test__category_table__then_every_category_has_one_label():
+ assert set(bridge.CATEGORY_LABELS) == set(bridge.CATEGORY_ORDER)
+ assert len(set(bridge.CATEGORY_ORDER)) == len(bridge.CATEGORY_ORDER)
+
+
+def test__given_refundable_credit_on_zero_tax__then_it_is_not_a_tax_row():
+ """A refundable credit on a zero liability is its own positive row.
+
+ Montana's elderly homeowner and renter credit on a zero liability once
+ read as "state income tax +1,150" (review finding 7).
+ """
+
+ tree = bridge.ComponentTree(
+ "household_net_income",
+ {
+ "household_net_income": bridge.NET_INCOME_DEFINITION,
+ "household_refundable_tax_credits": (
+ ("household_refundable_state_tax_credits", 1),
+ ),
+ "household_refundable_state_tax_credits": (
+ ("mt_refundable_credits", 1),
+ ),
+ "household_tax_before_refundable_credits": (
+ ("household_state_tax_before_refundable_credits", 1),
+ ),
+ "household_state_tax_before_refundable_credits": (
+ ("state_income_tax_before_refundable_credits", 1),
+ ),
+ },
+ )
+ leaves = {
+ "household_market_income": 0.0,
+ "household_benefits": 0.0,
+ "mt_refundable_credits": 1150.0,
+ "state_income_tax_before_refundable_credits": 0.0,
+ "household_health_costs": 0.0,
+ }
+ values = _values(tree, leaves)
+ categories = bridge.decompose(tree, values, values).by_category()
+ assert categories["state_refundable_credits"]["baseline"] == 115000
+ assert categories["state_income_tax"]["baseline"] == 0
+
+
+def test__given_cyclic_or_badly_signed_tree__then_it_is_refused():
+ with pytest.raises(ValueError, match="cycle"):
+ bridge.ComponentTree("a", {"a": (("b", 1),), "b": (("a", 1),)})
+ with pytest.raises(ValueError, match="sign"):
+ bridge.ComponentTree("a", {"a": (("b", 2),)})
+ with pytest.raises(ValueError, match="root"):
+ bridge.ComponentTree("a", {"b": (("c", 1),)})
+
+
+@given(st.integers(-(10**12), 10**12))
+def test__given_whole_cents__then_to_cents_round_trips(cents):
+ assert bridge.to_cents(cents / 100) == cents
+
+
+# ---------------------------------------------------------------------------
+# Carrying a PIA forward
+# ---------------------------------------------------------------------------
+RATES = st.lists(
+ st.integers(0, 150).map(lambda tenths: tenths / 1000),
+ min_size=0,
+ max_size=12,
+)
+NONEMPTY_RATES = RATES.filter(bool)
+PIA = st.floats(min_value=0.0, max_value=10_000.0, allow_nan=False)
+
+
+def _rate_map(rates, first=2000):
+ return {first + i: rate for i, rate in enumerate(rates)}
+
+
+def _carry(pia, rates, first=2000):
+ return bridge.carry_pia_forward(
+ pia,
+ _rate_map(rates, first),
+ first_determination_year=first,
+ last_determination_year=first + len(rates) - 1,
+ )[0]
+
+
+@given(PIA, NONEMPTY_RATES)
+def test__given_colas__then_carried_pia_is_a_whole_dime(pia, rates):
+ carried = _carry(pia, rates)
+ tenths = Decimal(repr(carried)) * 10
+ assert tenths == tenths.to_integral_value()
+
+
+@given(PIA, PIA, NONEMPTY_RATES)
+def test__given_larger_pia__then_carried_pia_is_not_smaller(a, b, rates):
+ low, high = sorted((a, b))
+ assert _carry(low, rates) <= _carry(high, rates)
+
+
+@given(PIA, NONEMPTY_RATES)
+def test__given_colas__then_truncation_loses_less_than_a_dime_a_step(
+ pia, rates
+):
+ carried = _carry(pia, rates)
+ exact = Decimal(repr(pia))
+ for rate in rates:
+ exact *= 1 + Decimal(repr(rate))
+ growth = math.prod(1 + rate for rate in rates)
+ assert Decimal(repr(carried)) <= exact
+ assert float(exact) - carried <= 0.10 * (len(rates) + 1) * growth + 1e-9
+
+
+@given(PIA, NONEMPTY_RATES, NONEMPTY_RATES)
+def test__given_split_years__then_carrying_composes(pia, first, second):
+ rates = first + second
+ part = _carry(pia, first)
+ rest = bridge.carry_pia_forward(
+ part,
+ _rate_map(rates),
+ first_determination_year=2000 + len(first),
+ last_determination_year=2000 + len(rates) - 1,
+ )[0]
+ assert rest == _carry(pia, rates)
+
+
+@given(PIA, st.integers(1, 10))
+def test__given_zero_colas__then_pia_is_only_truncated_to_a_dime(pia, n):
+ assert _carry(pia, [0.0] * n) == bridge.floor_to_dime(pia)
+
+
+def test__given_missing_cola__then_carry_is_refused():
+ with pytest.raises(ValueError, match="no COLA"):
+ bridge.carry_pia_forward(
+ 600.0,
+ {2020: 0.013},
+ first_determination_year=2020,
+ last_determination_year=2021,
+ )
+
+
+@given(PIA, st.integers(1, 30))
+def test__given_first_year_after_last__then_carry_is_refused(pia, gap):
+ """An empty range once returned the PIA untruncated (finding 5)."""
+
+ with pytest.raises(ValueError, match="after"):
+ bridge.carry_pia_forward(
+ pia,
+ {},
+ first_determination_year=2026,
+ last_determination_year=2026 - gap,
+ )
+
+
+def test__given_float_noise_in_a_rate__then_it_does_not_reach_the_dime():
+ # 5.9 / 100 is 0.059000000000000004 in floating point.
+ noisy = 5.9 / 100
+ assert repr(noisy) != "0.059"
+ assert (
+ bridge.carry_pia_forward(
+ 1000.0,
+ {2021: noisy},
+ first_determination_year=2021,
+ last_determination_year=2021,
+ )[0]
+ == bridge.carry_pia_forward(
+ 1000.0,
+ {2021: 0.059},
+ first_determination_year=2021,
+ last_determination_year=2021,
+ )[0]
+ )
+
+
+def test__given_ssa_third_quarter_cpi_w__then_colas_match_the_published_ones():
+ # Third-quarter CPI-W averages 2021-2025 (the entries dated 2022-2026 of
+ # policyengine-us's gov.ssa.uprating), and SSA's COLAs determined
+ # 2022-2025: 8.7, 3.2, 2.5 and 2.8 percent.
+ cpi_w = {
+ 2022: 268.421,
+ 2023: 291.901,
+ 2024: 301.236,
+ 2025: 308.729,
+ 2026: 317.265,
+ }
+ assert bridge.cola_rates_from_cpi_w(cpi_w) == {
+ 2022: 0.087,
+ 2023: 0.032,
+ 2024: 0.025,
+ 2025: 0.028,
+ }
+
+
+def test__given_a_quarter_without_increase__then_the_base_carries_forward():
+ # INVENTED: no increase in the second year, so the third year's COLA
+ # is measured from the first year's quarter (415(i)(1)(B), (D)).
+ rates = bridge.cola_rates_from_cpi_w(
+ {2014: 100.0, 2015: 99.0, 2016: 100.3}
+ )
+ assert rates == {2014: 0.0, 2015: 0.003}
+
+
+@given(
+ st.lists(
+ st.floats(min_value=50.0, max_value=500.0, allow_nan=False),
+ min_size=2,
+ max_size=10,
+ )
+)
+def test__given_any_cpi_w_path__then_colas_are_nonnegative_tenths(levels):
+ rates = bridge.cola_rates_from_cpi_w(
+ {2000 + i: level for i, level in enumerate(levels)}
+ )
+ assert sorted(rates) == list(range(2000, 2000 + len(levels) - 1))
+ for rate in rates.values():
+ assert rate >= 0
+ assert abs(rate * 1000 - round(rate * 1000)) < 1e-9
+
+
+# ---------------------------------------------------------------------------
+# The runner's refusals (no interpreter is started)
+# ---------------------------------------------------------------------------
+def test__given_missing_interpreter__then_runner_raises_unavailable(tmp_path):
+ with pytest.raises(bridge.PolicyEngineUSUnavailable):
+ bridge.run_policyengine_us(
+ [bridge.RunCase("a", {})],
+ year=2026,
+ python=tmp_path / "no-such-python",
+ )
+
+
+def test__given_duplicate_case_ids__then_runner_refuses(tmp_path):
+ with pytest.raises(ValueError, match="unique"):
+ bridge.run_policyengine_us(
+ [bridge.RunCase("a", {}), bridge.RunCase("a", {})],
+ year=2026,
+ python=tmp_path / "no-such-python",
+ )
+
+
+def test__given_explicit_env_or_nothing__then_interpreter_resolves_in_order(
+ monkeypatch, tmp_path
+):
+ explicit = tmp_path / "explicit"
+ env = tmp_path / "env"
+ monkeypatch.setenv(bridge.PE_US_PYTHON_ENV, str(env))
+ assert bridge.resolve_pe_us_python(explicit) == explicit
+ assert bridge.resolve_pe_us_python() == env
+ monkeypatch.delenv(bridge.PE_US_PYTHON_ENV)
+ assert bridge.resolve_pe_us_python() == (
+ bridge.DEFAULT_PE_US_PYTHON.expanduser()
+ )
+
+
+# ---------------------------------------------------------------------------
+# The source check (no interpreter is started)
+# ---------------------------------------------------------------------------
+def _installation(tmp_path, **overrides):
+ fields = {
+ "python": str(tmp_path / "python"),
+ "python_version": "3.13.9",
+ "package_dir": str(tmp_path / "site" / "policyengine_us"),
+ "version": "2.18.0",
+ "core_version": "3.32.11",
+ "location": str(tmp_path / "site"),
+ "record_path": str(tmp_path / "site" / "dist-info" / "RECORD"),
+ "record_sha256": "0" * 64,
+ "direct_url": None,
+ "installer": "uv",
+ }
+ fields.update(overrides)
+ return bridge.PolicyEngineUSInstallation(**fields)
+
+
+def test__given_index_install__then_source_is_published(tmp_path):
+ installation = _installation(tmp_path)
+ assert installation.source_kind == "index"
+ record = bridge.check_published_source(installation)
+ assert record["published"] and record["reasons"] == []
+
+
+@pytest.mark.parametrize(
+ ("direct_url", "kind"),
+ [
+ ({"url": "https://x", "vcs_info": {"commit_id": "a"}}, "vcs"),
+ ({"url": "file:///w.whl", "archive_info": {}}, "archive"),
+ ({"url": "file:///src", "dir_info": {}}, "directory"),
+ ],
+)
+def test__given_vcs_archive_or_directory_install__then_source_is_refused(
+ tmp_path, direct_url, kind
+):
+ installation = _installation(tmp_path, direct_url=direct_url)
+ assert installation.source_kind == kind
+ with pytest.raises(bridge.UnpublishedSourceError, match=kind):
+ bridge.check_published_source(installation)
+ record = bridge.check_published_source(installation, require=False)
+ assert record["published"] is False
+
+
+def _git(directory, *args):
+ subprocess.run(
+ ["git", "-C", str(directory), *args],
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+
+
+@pytest.fixture
+def checkout(tmp_path):
+ """A clone of a local origin: pushed ``main`` plus a package dir."""
+
+ origin = tmp_path / "origin.git"
+ subprocess.run(
+ ["git", "init", "--bare", "-q", str(origin)],
+ check=True,
+ capture_output=True,
+ )
+ clone = tmp_path / "clone"
+ subprocess.run(
+ ["git", "clone", "-q", str(origin), str(clone)],
+ check=True,
+ capture_output=True,
+ )
+ _git(clone, "config", "user.email", "t@example.com")
+ _git(clone, "config", "user.name", "t")
+ _git(clone, "checkout", "-q", "-b", "main")
+ package = clone / "policyengine_us"
+ package.mkdir()
+ (package / "__init__.py").write_text("")
+ _git(clone, "add", ".")
+ _git(clone, "commit", "-q", "-m", "published")
+ _git(clone, "push", "-q", "origin", "main")
+ return clone
+
+
+def _checkout_installation(tmp_path, clone):
+ return _installation(
+ tmp_path,
+ package_dir=str(clone / "policyengine_us"),
+ direct_url={"url": f"file://{clone}", "dir_info": {"editable": True}},
+ )
+
+
+def _check(tmp_path, clone, **kwargs):
+ # The fixture's origin is a local bare repository, not GitHub, so the
+ # tests of reachability and cleanliness switch the host check off; its
+ # own test is below.
+ return bridge.check_published_source(
+ _checkout_installation(tmp_path, clone),
+ **{"public_origin": False, **kwargs},
+ )
+
+
+def test__given_pushed_clean_checkout__then_source_is_published(
+ tmp_path, checkout
+):
+ installation = _checkout_installation(tmp_path, checkout)
+ assert installation.source_kind == "path"
+ record = _check(tmp_path, checkout)
+ assert record["published"]
+ assert record["checkout"]["origin_heads_containing"] == ["refs/heads/main"]
+
+
+def test__given_origin_not_on_github__then_source_is_refused(
+ tmp_path, checkout
+):
+ """A pushed commit on a local origin is not published."""
+
+ with pytest.raises(
+ bridge.UnpublishedSourceError, match="is not a GitHub repository"
+ ):
+ bridge.check_published_source(
+ _checkout_installation(tmp_path, checkout)
+ )
+
+
+@pytest.mark.parametrize(
+ ("url", "github"),
+ [
+ ("https://github.com/PolicyEngine/policyengine-us.git", True),
+ ("https://github.com/PolicyEngine/policyengine-us", True),
+ ("git@github.com:PolicyEngine/policyengine-us.git", True),
+ ("ssh://git@github.com/PolicyEngine/policyengine-us.git", True),
+ ("https://github.example.com/PolicyEngine/policyengine-us", False),
+ ("/Users/someone/policyengine-us.git", False),
+ ("file:///tmp/origin.git", False),
+ ],
+)
+def test__given_origin_url__then_only_github_urls_match(url, github):
+ """The check is the host only: a URL cannot say whether a GitHub
+ repository is public or private."""
+
+ assert bool(bridge._GITHUB_ORIGIN.match(url)) is github
+
+
+def test__given_local_commit_not_on_origin__then_source_is_refused(
+ tmp_path, checkout
+):
+ """The review's finding 3: a revision GitHub does not have."""
+
+ (checkout / "policyengine_us" / "extra.py").write_text("X = 1\n")
+ _git(checkout, "add", ".")
+ _git(checkout, "commit", "-q", "-m", "fixup! local only")
+ with pytest.raises(bridge.UnpublishedSourceError, match="not reachable"):
+ _check(tmp_path, checkout)
+
+
+def test__given_branch_deleted_on_origin__then_a_stale_ref_does_not_count(
+ tmp_path, checkout
+):
+ """A remote-tracking ref can outlive its branch; origin is asked now."""
+
+ _git(checkout, "checkout", "-q", "-b", "feature")
+ (checkout / "policyengine_us" / "feature.py").write_text("F = 1\n")
+ _git(checkout, "add", ".")
+ _git(checkout, "commit", "-q", "-m", "feature")
+ _git(checkout, "push", "-q", "origin", "feature")
+ assert _check(tmp_path, checkout)["published"]
+ # Delete the branch on origin only: refs/remotes/origin/feature stays.
+ _git(tmp_path / "origin.git", "branch", "-q", "-D", "feature")
+ _git(checkout, "rev-parse", "refs/remotes/origin/feature")
+ with pytest.raises(bridge.UnpublishedSourceError, match="not reachable"):
+ _check(tmp_path, checkout)
+
+
+def test__given_unreachable_origin__then_source_is_refused(tmp_path, checkout):
+ _git(checkout, "remote", "set-url", "origin", str(tmp_path / "gone.git"))
+ with pytest.raises(bridge.UnpublishedSourceError, match="could not be"):
+ _check(tmp_path, checkout)
+
+
+@pytest.mark.parametrize("change", ["tracked", "untracked", "ignored"])
+def test__given_modified_checkout__then_source_is_refused(
+ tmp_path, checkout, change
+):
+ package = checkout / "policyengine_us"
+ if change == "tracked":
+ (package / "__init__.py").write_text("Y = 2\n")
+ elif change == "untracked":
+ (package / "new.py").write_text("Z = 3\n")
+ else:
+ # A git-ignored parameter file still loads: policyengine-core reads
+ # every YAML under parameters/.
+ exclude = checkout / ".git" / "info" / "exclude"
+ exclude.write_text(exclude.read_text() + "hidden.yaml\n")
+ (package / "hidden.yaml").write_text("values: {2026-01-01: 1}\n")
+ with pytest.raises(bridge.UnpublishedSourceError, match="uncommitted"):
+ _check(tmp_path, checkout)
+
+
+def test__given_only_bytecode_untracked__then_checkout_stays_clean(
+ tmp_path, checkout
+):
+ cache = checkout / "policyengine_us" / "__pycache__"
+ cache.mkdir()
+ (cache / "__init__.cpython-313.pyc").write_bytes(b"\x00")
+ assert _check(tmp_path, checkout)["published"]
+
+
+def test__given_package_outside_any_checkout__then_source_is_refused(
+ tmp_path,
+):
+ outside = tmp_path / "loose" / "policyengine_us"
+ outside.mkdir(parents=True)
+ installation = _installation(tmp_path, package_dir=str(outside))
+ assert installation.source_kind == "path"
+ with pytest.raises(bridge.UnpublishedSourceError, match="git checkout"):
+ bridge.check_published_source(installation)
+
+
+def _record_line(root, relative, content):
+ path = root / relative
+ path.parent.mkdir(parents=True, exist_ok=True)
+ path.write_bytes(content)
+ digest = hashlib.sha256(content).digest()
+ encoded = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
+ return f"{relative},sha256={encoded},{len(content)}"
+
+
+def test__given_installed_files__then_record_verification_checks_each(
+ tmp_path,
+):
+ site = tmp_path / "site"
+ lines = [
+ _record_line(site, "policyengine_us/__init__.py", b"a = 1\n"),
+ _record_line(site, "policyengine_us/p/x.yaml", b"values: {}\n"),
+ _record_line(site, "policyengine_us-2.18.0.dist-info/METADATA", b"m"),
+ "policyengine_us-2.18.0.dist-info/RECORD,,",
+ ]
+ record = site / "policyengine_us-2.18.0.dist-info" / "RECORD"
+ record.write_text("\n".join(lines) + "\n")
+ installation = _installation(tmp_path, record_path=str(record))
+ # Bytecode written at import time is neither in the RECORD nor extra.
+ cache = site / "policyengine_us" / "__pycache__"
+ cache.mkdir()
+ (cache / "__init__.cpython-313.pyc").write_bytes(b"\x00")
+ clean = bridge.verify_record(installation)
+ assert clean["files_checked"] == 2
+ assert clean["mismatched"] == [] and clean["missing"] == []
+ assert clean["extra"] == []
+ (site / "policyengine_us" / "p" / "x.yaml").write_text("values: {1: 2}\n")
+ (site / "policyengine_us" / "__init__.py").unlink()
+ (site / "policyengine_us" / "p" / "override.yaml").write_text("x: 1\n")
+ dirty = bridge.verify_record(installation)
+ assert dirty["mismatched"] == ["policyengine_us/p/x.yaml"]
+ assert dirty["missing"] == ["policyengine_us/__init__.py"]
+ assert dirty["extra"] == ["policyengine_us/p/override.yaml"]
+ assert dirty["package_record_digest"] == clean["package_record_digest"]
+
+
+@given(
+ st.lists(
+ st.from_regex(
+ r"policyengine_us/[a-z]{1,8}\.py,sha256=[a-z]{4},\d",
+ fullmatch=True,
+ ),
+ min_size=1,
+ max_size=8,
+ unique=True,
+ ),
+ st.lists(
+ st.one_of(
+ st.from_regex(r"[a-z]{1,8},,", fullmatch=True),
+ # pip compiles bytecode and lists it under the package.
+ st.from_regex(
+ r"policyengine_us/(?:[a-z]{1,8}/)?__pycache__/"
+ r"[a-z]{1,8}\.cpython-31[0-4]\.pyc,,",
+ fullmatch=True,
+ ),
+ ),
+ max_size=6,
+ ),
+ st.randoms(use_true_random=False),
+)
+def test__given_installer_lines_and_order__then_package_digest_is_unchanged(
+ package_lines, installer_lines, random
+):
+ """The installed RECORD adds installer lines (pip's bytecode among them)
+ and may reorder; the digest of the sorted package lines is the wheel's
+ either way."""
+
+ wheel = "\n".join(package_lines) + "\n"
+ installed = package_lines + installer_lines
+ random.shuffle(installed)
+ prefix = "policyengine_us/"
+ assert bridge.package_record_digest(wheel, prefix) == (
+ bridge.package_record_digest("\n".join(installed), prefix)
+ )
+
+
+# ---------------------------------------------------------------------------
+# The float32 guard (no interpreter is started)
+# ---------------------------------------------------------------------------
+def _node(value, children=(), is_input=False):
+ return bridge.TraceNode(
+ value=None if value is None else tuple(value),
+ children=tuple(children),
+ input=is_input,
+ )
+
+
+def _use_tax_traces(reform_agi):
+ """The review's finding 2, as PolicyEngine-US 2.18.0 traces it.
+
+ California excludes Social Security from its AGI, so ca_agi should not
+ move; float32 subtraction put the reform run at 29,999.998046875, one
+ float32 step under the $30,000 edge of the use-tax table.
+ """
+
+ def run(ss, agi, ca_agi, use_tax):
+ return {
+ "state_use_tax@2026": _node([use_tax], ["ca_use_tax@2026"]),
+ "ca_use_tax@2026": _node([use_tax], ["CA@2026", "ca_agi@2026"]),
+ "CA@2026": _node([1.0]),
+ "ca_agi@2026": _node(
+ [ca_agi],
+ ["adjusted_gross_income@2026", "ca_agi_subtractions@2026"],
+ ),
+ "adjusted_gross_income@2026": _node(
+ [agi], ["social_security_retirement@2026"]
+ ),
+ "ca_agi_subtractions@2026": _node(
+ [agi - ca_agi], ["social_security_retirement@2026"]
+ ),
+ "social_security_retirement@2026": _node([ss], is_input=True),
+ }
+
+ baseline = run(8916.0, 34847.30, 30000.0, 3.0)
+ reform = run(
+ 10968.0, 35761.40, reform_agi, 2.0 if reform_agi < 3e4 else 3.0
+ )
+ return baseline, reform
+
+
+def test__given_step_on_float32_noise__then_the_guard_reports_its_origin():
+ baseline, reform = _use_tax_traces(29999.998046875)
+ found = bridge.uncaused_changes(baseline, reform, ["state_use_tax@2026"])
+ assert [item["variable"] for item in found] == ["ca_use_tax@2026"]
+ assert found[0]["change"] == pytest.approx(1.0)
+ assert found[0]["reads"]["ca_agi@2026"] < bridge.CENT_TOLERANCE
+
+
+def test__given_no_step__then_the_guard_is_silent():
+ baseline, reform = _use_tax_traces(30000.0)
+ assert (
+ bridge.uncaused_changes(baseline, reform, ["state_use_tax@2026"]) == []
+ )
+
+
+def test__given_change_through_a_changed_input__then_it_is_caused():
+ baseline = {
+ "tax@2026": _node([100.0], ["agi@2026"]),
+ "agi@2026": _node([1000.0], ["ss@2026"]),
+ "ss@2026": _node([500.0], is_input=True),
+ }
+ reform = {
+ "tax@2026": _node([100.5], ["agi@2026"]),
+ "agi@2026": _node([1005.0], ["ss@2026"]),
+ "ss@2026": _node([505.0], is_input=True),
+ }
+ assert bridge.uncaused_changes(baseline, reform, ["tax@2026"]) == []
+
+
+def test__given_branch_taken_in_one_run_only__then_it_is_a_cause():
+ """Medicaid ending sends the calculation down another branch: a node
+ read in one run only is a change, and a node traced in one run only is
+ not compared."""
+
+ baseline = {
+ "cost@2026": _node([9000.0], ["eligible@2026", "medicaid@2026"]),
+ "eligible@2026": _node([1.0], ["income@2026"]),
+ "income@2026": _node([100.0], is_input=True),
+ "medicaid@2026": _node([9000.0], ["age@2026"]),
+ "age@2026": _node([68.0], is_input=True),
+ }
+ reform = {
+ "cost@2026": _node([5000.0], ["eligible@2026", "msp@2026"]),
+ "eligible@2026": _node([0.0], ["income@2026"]),
+ "income@2026": _node([200.0], is_input=True),
+ "msp@2026": _node([5000.0], ["age@2026"]),
+ "age@2026": _node([68.0], is_input=True),
+ }
+ assert bridge.uncaused_changes(baseline, reform, ["cost@2026"]) == []
+
+
+def test__given_branch_copy_of_a_main_value__then_it_reads_the_main_node():
+ """A branch clones its parent's calculated values (2.18.0's itemizing
+ branch copies adjusted gross income): the copy is caused when the main
+ value is, and reported when the main value changed without a cause."""
+
+ def run(ss, agi, main_agi_children):
+ return {
+ "tax@2026": _node([agi * 0.1], ["itemizing:agi@2026"]),
+ "itemizing:agi@2026": _node([agi]),
+ "agi@2026": _node([agi], main_agi_children),
+ "ss@2026": _node([ss], is_input=True),
+ }
+
+ caused_b = run(8916.0, 37067.30, ["ss@2026"])
+ caused_r = run(10968.0, 37981.40, ["ss@2026"])
+ assert bridge.uncaused_changes(caused_b, caused_r, ["tax@2026"]) == []
+ # The main value moves while nothing it read did: the main node is the
+ # origin, and the copy and the tax are caused by it.
+ noise_b = run(8916.0, 37067.30, ["pension@2026"])
+ noise_r = run(8916.0, 37981.40, ["pension@2026"])
+ for nodes in (noise_b, noise_r):
+ nodes["pension@2026"] = _node([31200.0], is_input=True)
+ found = bridge.uncaused_changes(noise_b, noise_r, ["tax@2026"])
+ assert [item["variable"] for item in found] == ["agi@2026"]
+
+
+def test__given_step_on_noise_above_131072__then_the_guard_still_sees_it():
+ """Above 2**17 one float32 step is $0.015625, more than a cent; the
+ review's counterexample at a $150,000 edge was missed with a fixed
+ one-cent tolerance."""
+
+ def run(ss, agi, ca_agi, tax):
+ return {
+ "tax@2026": _node([tax], ["ca_agi@2026"]),
+ "ca_agi@2026": _node([ca_agi], ["agi@2026", "sub@2026"]),
+ "agi@2026": _node([agi], ["ss@2026"]),
+ "sub@2026": _node([agi - ca_agi], ["ss@2026"]),
+ "ss@2026": _node([ss], is_input=True),
+ }
+
+ baseline = run(8916.0, 260099.70, 150000.0, 15.0)
+ reform = run(10968.0, 262151.70, 149999.984375, 14.0)
+ found = bridge.uncaused_changes(baseline, reform, ["tax@2026"])
+ assert [item["variable"] for item in found] == ["tax@2026"]
+ assert bridge.float32_step(150000.0) == 0.015625
+ assert bridge.float32_step(30000.0) == 2**-9
+
+
+@given(st.floats(1.0, 1e9, allow_nan=False, allow_infinity=False))
+def test__given_any_value__then_float32_step_is_its_spacing(value):
+ step = bridge.float32_step(value)
+ # value lies in [2**(e-1), 2**e) and float32 keeps 24 bits.
+ assert step * 2**23 <= value < step * 2**24
+
+
+@given(
+ st.lists(
+ st.floats(0.02, 1e5, allow_nan=False, allow_infinity=False),
+ min_size=1,
+ max_size=6,
+ ),
+ st.floats(0.02, 1e4, allow_nan=False, allow_infinity=False),
+)
+def test__given_chain_of_real_changes__then_the_guard_is_silent(
+ steps, input_change
+):
+ """A chain where every variable moves with the one it reads, down to a
+ changed input, is caused all the way."""
+
+ def run(shift):
+ nodes = {"x@2026": _node([100.0 + shift], is_input=True)}
+ below = "x@2026"
+ level = 100.0 + shift
+ for index, step in enumerate(steps):
+ key = f"v{index}@2026"
+ level += step if shift else 0.0
+ nodes[key] = _node([level], [below])
+ below = key
+ return nodes, below
+
+ baseline, root = run(0.0)
+ reform, _ = run(input_change)
+ assert bridge.uncaused_changes(baseline, reform, [root]) == []
+
+
+@given(
+ st.lists(
+ st.tuples(
+ st.floats(-1e6, 1e6, allow_nan=False, allow_infinity=False),
+ st.floats(-1e6, 1e6, allow_nan=False, allow_infinity=False),
+ ),
+ min_size=1,
+ max_size=6,
+ )
+)
+def test__given_identical_runs__then_the_guard_is_silent(values):
+ nodes = {
+ f"v{i}@2026": _node(
+ [a, b], [f"v{i + 1}@2026"] if i + 1 < len(values) else []
+ )
+ for i, (a, b) in enumerate(values)
+ }
+ assert bridge.uncaused_changes(nodes, dict(nodes), ["v0@2026"]) == []
+
+
+@given(
+ st.floats(-1e6, 1e6, allow_nan=False, allow_infinity=False),
+ st.floats(0.01, 1e4, allow_nan=False, allow_infinity=False),
+ st.floats(0.0, 0.0099, allow_nan=False, allow_infinity=False),
+)
+def test__given_any_jump_over_a_sub_cent_read__then_it_is_reported(
+ level, jump, noise
+):
+ """A jump of a cent or more is reported at every level, although
+ ``level + jump - level`` can measure a little under a cent."""
+
+ baseline = {
+ "out@2026": _node([level], ["in@2026"]),
+ "in@2026": _node([0.0], ["x@2026"]),
+ "x@2026": _node([1.0], is_input=True),
+ }
+ reform = {
+ "out@2026": _node([level + jump], ["in@2026"]),
+ "in@2026": _node([noise], ["x@2026"]),
+ "x@2026": _node([1.0], is_input=True),
+ }
+ found = bridge.uncaused_changes(baseline, reform, ["out@2026"])
+ assert [item["variable"] for item in found] == ["out@2026"]
+
+
+def _one_node(value):
+ """An output that reads only an unchanged input."""
+
+ return {
+ "out@2026": _node([value], ["x@2026"]),
+ "x@2026": _node([1.0], is_input=True),
+ }
+
+
+def test__given_exact_one_cent_float32_step__then_it_is_reported():
+ """Round 2's nit N3, its minimal counterexample: float32 3.00 to 3.01
+ measures 0.0099999905, under a cent, and was not reported."""
+
+ before, after = float(np.float32(3.00)), float(np.float32(3.01))
+ assert after - before < bridge.CENT_TOLERANCE
+ found = bridge.uncaused_changes(
+ _one_node(before), _one_node(after), ["out@2026"]
+ )
+ assert [item["variable"] for item in found] == ["out@2026"]
+
+
+@given(st.integers(-(10**8), 10**8))
+def test__given_any_one_cent_float32_step__then_it_is_reported(cents):
+ """A one-cent step stored in float32 is reported at every magnitude
+ where float32 can tell the two amounts apart (above $131,072 a cent
+ can round away entirely, and then nothing changed)."""
+
+ before = float(np.float32(cents / 100))
+ after = float(np.float32((cents + 1) / 100))
+ found = bridge.uncaused_changes(
+ _one_node(before), _one_node(after), ["out@2026"]
+ )
+ reported = [item["variable"] for item in found]
+ assert reported == (["out@2026"] if after != before else [])
+
+
+def test__given_one_float32_step_of_noise_at_70000__then_it_is_reported():
+ """The tradeoff, stated: from $65,536 to $131,072 one float32 step is
+ $0.0078125, which float32 cannot tell from a one-cent change, so a
+ step of pure noise is reported (a false alarm, not a miss)."""
+
+ step = bridge.float32_step(70_000.0)
+ assert step == 2**-7
+ found = bridge.uncaused_changes(
+ _one_node(70_000.0), _one_node(70_000.0 + step), ["out@2026"]
+ )
+ assert [item["variable"] for item in found] == ["out@2026"]
+
+
+def test__given_one_cent_step_computed_from_larger_operands__then_missed():
+ """The limit, stated: the bound covers a stored value. x = a - 5,760
+ in float32, with ``a`` moving by a cent near $35,760, moves by
+ $0.0078125 (two float32 steps of ``a``), under the threshold at
+ $30,000."""
+
+ f32 = np.float32
+ before = float(f32(f32(35_760.01) - f32(5_760.0)))
+ after = float(f32(f32(35_760.02) - f32(5_760.0)))
+ assert after - before == 2**-7
+ found = bridge.uncaused_changes(
+ _one_node(before), _one_node(after), ["out@2026"]
+ )
+ assert found == []
+
+
+@given(
+ st.floats(-2e5, 2e5, allow_nan=False, allow_infinity=False),
+ st.integers(1, 1_000),
+)
+def test__given_change_under_half_a_cent__then_it_is_not_reported(
+ level, microdollars
+):
+ """The float32 allowance never lowers the threshold below half a
+ cent, so float noise on a large value is not a change."""
+
+ change = min(microdollars * 1e-6, 0.0049)
+ found = bridge.uncaused_changes(
+ _one_node(level), _one_node(level + change), ["out@2026"]
+ )
+ assert found == []
+
+
+def test__given_small_leaf_changes__then_they_are_listed():
+ tree = bridge.ComponentTree(
+ "root", {"root": (("a", 1), ("b", 1), ("c", -1))}
+ )
+ baseline = {"root": 10.0, "a": 5.0, "b": 3.0, "c": -2.0}
+ reform = {"root": 12.01, "a": 6.99, "b": 3.0, "c": -2.02}
+ result = _decompose(tree, baseline, reform)
+ small = bridge.small_changes(result)
+ assert [c.variable for c in small] == ["a", "c"]
+ assert bridge.small_changes(result, limit_cents=199) == [small[1]]
diff --git a/tests/bridge/test_policyengine_us_bridge_oracle.py b/tests/bridge/test_policyengine_us_bridge_oracle.py
new file mode 100644
index 000000000..ea3204f86
--- /dev/null
+++ b/tests/bridge/test_policyengine_us_bridge_oracle.py
@@ -0,0 +1,457 @@
+"""The PolicyEngine-US bridge against a live policyengine-us interpreter.
+
+Skipped unless the interpreter exists and imports ``policyengine_us``: the
+one named by ``POPULACE_DYNAMICS_PE_US_PYTHON``, else
+:data:`bridge.DEFAULT_PE_US_PYTHON`, a virtual environment holding
+policyengine-us 2.18.0 from PyPI. The path is resolved and checked, not
+just the variable read (another test module sets policyengine-us variables
+at import). The bridge refuses an interpreter whose ``policyengine-us`` is
+not a published source, so these tests fail loudly on an unpushed checkout.
+
+Each directional fact below was read in policyengine-us 2.18.0 before it
+was asserted (paths under ``policyengine_us/``):
+
+* SSI is the benefit rate less countable income (``variables/gov/ssa/ssi/
+ uncapped_ssi.py:13-16``, floored at zero in ``ssi_if_takes_up.py:20-23``);
+ ``social_security`` is countable unearned income (``parameters/gov/ssa/
+ ssi/income/sources/unearned.yaml:6``) and the $20 general exclusion comes
+ off unearned income first (``eligibility/income/_apply_ssi_exclusions.py:
+ 28-30``). So SSI cannot rise when Social Security rises, and while SSI
+ stays positive it falls dollar for dollar.
+* SNAP counts ``ssi`` and ``social_security`` as unearned income
+ (``parameters/gov/usda/snap/income/sources/unearned.yaml:6,63``), so
+ their constant sum leaves SNAP unchanged, and SNAP is the maximum
+ allotment less 30 percent of net income (``snap_normal_allotment.py:
+ 19-23``; ``snap_expected_contribution.py:17-32``), so more income never
+ raises it.
+* California's supplement is the payment standard less SSI less countable
+ income (``variables/gov/states/ca/cdss/state_supplement/
+ ca_state_supplement.py:13-17``), constant while SSI is positive. Its
+ single aged or disabled payment standard has no entry after 2025-01-01
+ (``parameters/gov/states/ca/cdss/state_supplement/payment_standard/
+ aged_or_disabled/amount/single.yaml:12-13``).
+* Taxable Social Security follows IRC 86 (``tax_unit_taxable_social_
+ security.py:13-85``), nondecreasing in benefits here.
+* California's use tax is a step schedule of California AGI in $10,000
+ brackets (``variables/gov/states/ca/tax/income/ca_use_tax.py:13-20``;
+ ``parameters/gov/states/ca/tax/income/use_tax/main.yaml:19-28``).
+
+The amounts are ILLUSTRATIVE (the annual benefits of the worker in
+``scripts/pe_us_minimum_benefit_sample_households.py``).
+"""
+
+from __future__ import annotations
+
+import os
+import subprocess
+from pathlib import Path
+
+import pytest
+
+from populace_dynamics.bridge import policyengine_us as bridge
+
+
+def _interpreter() -> Path:
+ env = os.environ.get(bridge.PE_US_PYTHON_ENV)
+ return (
+ Path(env).expanduser()
+ if env
+ else bridge.DEFAULT_PE_US_PYTHON.expanduser()
+ )
+
+
+pytestmark = pytest.mark.skipif(
+ not _interpreter().is_file(),
+ reason=f"no policyengine-us interpreter at {_interpreter()}",
+)
+
+YEAR = 2026
+BASELINE_SS = 8_916.0
+REFORM_SS = 10_968.0
+PENSION_B = 4_800.0
+PENSION_C = 31_200.0
+TOLERANCE = 0.05
+#: California's 2026 payment standard, single aged person living
+#: independently: CDSS, "SSI Total Monthly Payment Amounts 2026" (Rev. 1/26,
+#: effective 2026-01-01), $1,233.94 a month. policyengine-us 2.18.0 holds
+#: 2025's $1,206.94.
+CA_STANDARD = "gov.states.ca.cdss.state_supplement.payment_standard." + (
+ "aged_or_disabled.amount.single"
+)
+CA_2026 = 1_233.94
+CA_2025 = 1_206.94
+
+
+@pytest.fixture(scope="module")
+def interpreter() -> Path:
+ path = _interpreter()
+ probe = subprocess.run(
+ [str(path), "-c", "import policyengine_us"],
+ capture_output=True,
+ timeout=300,
+ )
+ if probe.returncode != 0:
+ pytest.skip(f"{path} cannot import policyengine_us")
+ return path
+
+
+def _household(key: str, state: str, ss: float, pension: float):
+ person = bridge.BridgePerson(
+ "worker",
+ 68,
+ social_security_retirement=ss,
+ taxable_private_pension_income=pension,
+ bank_account_assets=1_500.0,
+ pre_subsidy_rent=9_600.0,
+ medicare_quarters_of_coverage=88,
+ )
+ return bridge.BridgeHousehold(
+ f"{key}_{state}".lower(),
+ state,
+ (person,),
+ (("worker",),),
+ (("worker",),),
+ (("worker",),),
+ has_heating_cooling_expense=True,
+ )
+
+
+CASES = {
+ ("A", "FL", "baseline"): ("A", "FL", BASELINE_SS, 0.0),
+ ("A", "FL", "reform"): ("A", "FL", REFORM_SS, 0.0),
+ ("A", "CA", "baseline"): ("A", "CA", BASELINE_SS, 0.0),
+ ("A", "CA", "reform"): ("A", "CA", REFORM_SS, 0.0),
+ ("B", "FL", "baseline"): ("B", "FL", BASELINE_SS, PENSION_B),
+ ("B", "FL", "reform"): ("B", "FL", REFORM_SS, PENSION_B),
+ ("C", "MT", "baseline"): ("C", "MT", BASELINE_SS, PENSION_C),
+ ("C", "MT", "reform"): ("C", "MT", REFORM_SS, PENSION_C),
+}
+
+
+def _case_id(key) -> str:
+ return "_".join(key)
+
+
+@pytest.fixture(scope="module")
+def result(interpreter):
+ cases = [
+ bridge.RunCase(
+ _case_id(key),
+ bridge.to_situation(_household(*spec), YEAR),
+ expected_state=spec[1],
+ memo=("ssi_countable_income",),
+ )
+ for key, spec in CASES.items()
+ ]
+ return bridge.run_policyengine_us(cases, year=YEAR, python=interpreter)
+
+
+def _pair(result, household, state):
+ baseline = result.runs[_case_id((household, state, "baseline"))]
+ reform = result.runs[_case_id((household, state, "reform"))]
+ return baseline, reform
+
+
+def _decompose(result, household, state):
+ baseline, reform = _pair(result, household, state)
+ return bridge.decompose(
+ baseline.tree,
+ baseline.values,
+ reform.values,
+ reform_tree=reform.tree,
+ )
+
+
+def test__live_tree__then_net_income_definition_is_the_one_read(result):
+ for run in result.runs.values():
+ assert run.tree.children[bridge.ROOT_VARIABLE] == (
+ bridge.NET_INCOME_DEFINITION
+ )
+ assert result.policyengine_us_version
+
+
+def test__live_interpreter__then_its_source_is_published_and_intact(
+ interpreter, result
+):
+ """Finding 3: the run records a published policyengine-us source."""
+
+ installation = bridge.inspect_installation(interpreter)
+ assert result.installation == installation
+ assert result.source["published"] is True
+ assert result.source["kind"] in ("index", "path")
+ assert result.policyengine_us_version == installation.version
+ if installation.source_kind == "index":
+ record = bridge.verify_record(installation)
+ assert record["files_checked"] > 1_000
+ assert record["mismatched"] == [] and record["missing"] == []
+ assert record["extra"] == []
+ # The runner verified the same files before running.
+ assert result.source["record_check"]["mismatched"] == 0
+ assert result.source["record_check"]["extra"] == 0
+
+
+def test__given_a_digest_other_than_the_release__then_the_run_is_refused(
+ interpreter,
+):
+ installation = bridge.inspect_installation(interpreter)
+ if installation.source_kind != "index":
+ pytest.skip(
+ "the interpreter's policyengine-us is not an index install"
+ )
+ situation = bridge.to_situation(
+ _household("A", "FL", BASELINE_SS, 0.0), YEAR
+ )
+ with pytest.raises(bridge.UnpublishedSourceError, match="expected"):
+ bridge.run_policyengine_us(
+ [bridge.RunCase("a", situation)],
+ year=YEAR,
+ python=interpreter,
+ expected_package_record_digest="0" * 64,
+ )
+
+
+@pytest.mark.parametrize("state", ["FL", "CA"])
+def test__household_a__then_ssi_absorbs_the_benefit_increase(result, state):
+ baseline, reform = _pair(result, "A", state)
+ ss_change = reform.values["social_security"] - (
+ baseline.values["social_security"]
+ )
+ assert ss_change == pytest.approx(REFORM_SS - BASELINE_SS)
+ assert reform.values["ssi"] <= baseline.values["ssi"]
+ # SSI stays positive in both runs, so it falls dollar for dollar.
+ assert baseline.values["ssi"] > 0 and reform.values["ssi"] > 0
+ assert reform.values["ssi"] - baseline.values["ssi"] == pytest.approx(
+ -ss_change, abs=TOLERANCE
+ )
+ assert reform.values["snap"] == pytest.approx(
+ baseline.values["snap"], abs=TOLERANCE
+ )
+ result_ = _decompose(result, "A", state)
+ assert abs(result_.net_change_cents) <= round(100 * TOLERANCE)
+
+
+def test__household_a_in_california__then_the_supplement_is_unchanged(
+ result,
+):
+ baseline, reform = _pair(result, "A", "CA")
+ assert baseline.values["ca_state_supplement"] > 0
+ assert reform.values["ca_state_supplement"] == pytest.approx(
+ baseline.values["ca_state_supplement"], abs=TOLERANCE
+ )
+
+
+@pytest.mark.parametrize(
+ ("household", "state"),
+ [("A", "FL"), ("A", "CA"), ("B", "FL"), ("C", "MT")],
+)
+def test__live_runs__then_components_sum_exactly_to_net_change(
+ result, household, state
+):
+ decomposition = _decompose(result, household, state)
+ assert (
+ sum(c.change_cents for c in decomposition.components)
+ == decomposition.net_change_cents
+ )
+ assert abs(decomposition.reported_gap_cents) <= 1
+ assert decomposition.max_aggregate_gap < TOLERANCE
+
+
+def test__household_b__then_snap_falls_and_ssi_stays_zero(result):
+ baseline, reform = _pair(result, "B", "FL")
+ assert baseline.values["ssi"] == 0 and reform.values["ssi"] == 0
+ assert reform.values["snap"] <= baseline.values["snap"]
+ assert reform.values["snap"] < baseline.values["snap"]
+
+
+def test__household_c__then_taxes_do_not_fall(result):
+ baseline, reform = _pair(result, "C", "MT")
+ categories = _decompose(result, "C", "MT").by_category()
+ # Taxes enter net income negatively: a nonpositive change means the
+ # liability did not fall.
+ assert categories["federal_income_tax"]["change"] <= 0
+ assert categories["state_income_tax"]["change"] <= 0
+ assert reform.values["income_tax_before_refundable_credits"] >= (
+ baseline.values["income_tax_before_refundable_credits"]
+ )
+
+
+def test__california_payment_standard_override__then_it_sets_the_supplement(
+ interpreter,
+):
+ """Finding 1: the 2026 override reaches the supplement's formula.
+
+ Household B's SSI countable income is $743 + $400 - $20 = $1,123 a
+ month and her SSI is zero, so the supplement is the payment standard
+ less $1,123: $110.94 a month under CDSS's $1,233.94, $83.94 under the
+ release's held $1,206.94.
+ """
+
+ situation = bridge.to_situation(
+ _household("B", "CA", BASELINE_SS, PENSION_B), YEAR
+ )
+ out = bridge.run_policyengine_us(
+ [
+ bridge.RunCase("held", situation, expected_state="CA"),
+ bridge.RunCase(
+ "published",
+ situation,
+ expected_state="CA",
+ parameter_overrides={CA_STANDARD: CA_2026},
+ ),
+ ],
+ year=YEAR,
+ python=interpreter,
+ )
+ countable = 743 + 400 - 20
+ assert out.runs["held"].values["ca_state_supplement"] == pytest.approx(
+ 12 * (CA_2025 - countable), abs=TOLERANCE
+ )
+ assert out.runs["published"].values[
+ "ca_state_supplement"
+ ] == pytest.approx(12 * (CA_2026 - countable), abs=TOLERANCE)
+
+
+@pytest.mark.parametrize(
+ ("pension", "flagged"),
+ [(30_000.0, ["ca_use_tax@2026"]), (PENSION_C, [])],
+)
+def test__use_tax_at_a_bracket_edge__then_the_float32_guard_flags_it(
+ interpreter, pension, flagged
+):
+ """Finding 2, live: at a $30,000 pension California AGI sits on the
+ $30,000 use-tax edge and float32 noise steps the tax by $1; at the
+ script's $31,200 nothing changes without a cause."""
+
+ variables = ["state_use_tax", "income_tax_before_refundable_credits"]
+ traces = bridge.trace_policyengine_us(
+ [
+ (
+ scenario,
+ bridge.to_situation(_household("C", "CA", ss, pension), YEAR),
+ variables,
+ )
+ for scenario, ss in (
+ ("baseline", BASELINE_SS),
+ ("reform", REFORM_SS),
+ )
+ ],
+ year=YEAR,
+ python=interpreter,
+ )
+ found = bridge.uncaused_changes(
+ traces["baseline"],
+ traces["reform"],
+ [f"{name}@{YEAR}" for name in variables],
+ )
+ assert [item["variable"] for item in found] == flagged
+ # Federal income tax rises with the benefit in both: a caused change.
+ federal = f"income_tax_before_refundable_credits@{YEAR}"
+ assert traces["reform"][federal].value[0] > (
+ traces["baseline"][federal].value[0]
+ )
+ # 2.18.0 computes federal tax through itemizing and not_itemizing
+ # branch simulations (tax_liability_if_itemizing.py:13,
+ # tax_liability_if_not_itemizing.py:14); their nodes are keyed apart
+ # from the main calculation's.
+ branches = {
+ key.split(":", 1)[0] for key in traces["baseline"] if ":" in key
+ }
+ assert {"itemizing", "not_itemizing"} <= branches
+
+
+def test__case_run_with_others__then_values_equal_the_case_run_alone(
+ interpreter, result
+):
+ """Each case runs in its own simulation, and cases with the same
+ overrides share a tax-benefit system; neither the other cases nor the
+ order changes a case's values."""
+
+ key = ("A", "FL", "baseline")
+ alone = bridge.run_policyengine_us(
+ [
+ bridge.RunCase(
+ "alone",
+ bridge.to_situation(_household(*CASES[key]), YEAR),
+ expected_state="FL",
+ )
+ ],
+ year=YEAR,
+ python=interpreter,
+ ).runs["alone"]
+ batched = result.runs[_case_id(key)]
+ assert alone.tree == batched.tree
+ assert alone.values == batched.values
+
+
+def test__override_cases_run_with_others__then_each_equals_the_case_alone(
+ interpreter,
+):
+ """Round 2's L6: the shared reformed system, with an override.
+
+ Cases with the same overrides share one reformed tax-benefit system,
+ built from a throwaway simulation of the first such case. In the
+ batch below household B in California runs on a system built from
+ household A's situation, beside a case with no overrides. It must
+ equal the same case run alone, on a system built from its own
+ situation, down to the Medicaid memo that aggregates over a
+ simulation's population.
+ """
+
+ override = {CA_STANDARD: CA_2026}
+ specs = {
+ "a_ca": (("A", "CA", BASELINE_SS, 0.0), override),
+ "b_ca": (("B", "CA", BASELINE_SS, PENSION_B), override),
+ "a_fl": (("A", "FL", BASELINE_SS, 0.0), {}),
+ }
+
+ def case(name):
+ household, overrides = specs[name]
+ return bridge.RunCase(
+ name,
+ bridge.to_situation(_household(*household), YEAR),
+ expected_state=household[1],
+ memo=("medicaid_cost", "ca_state_supplement"),
+ parameter_overrides=overrides,
+ )
+
+ batched = bridge.run_policyengine_us(
+ [case(name) for name in specs], year=YEAR, python=interpreter
+ ).runs
+ alone = bridge.run_policyengine_us(
+ [case("b_ca")], year=YEAR, python=interpreter
+ ).runs["b_ca"]
+ assert alone.tree == batched["b_ca"].tree
+ assert alone.values == batched["b_ca"].values
+ assert alone.memo == batched["b_ca"].memo
+ # The override reached the shared system: household B's supplement is
+ # the published standard less her $1,123 of monthly countable income.
+ assert alone.memo["ca_state_supplement"] == pytest.approx(
+ 12 * (CA_2026 - (743 + 400 - 20)), abs=TOLERANCE
+ )
+ assert batched["a_ca"].memo["medicaid_cost"] > 0
+
+
+def test__state_fips_table__then_every_code_resolves_to_its_state(
+ interpreter,
+):
+ cases = [
+ bridge.RunCase(
+ state,
+ bridge.to_situation(_household("fips", state, 0.0, 0.0), YEAR),
+ expected_state=state,
+ )
+ for state in sorted(bridge.STATE_FIPS)
+ ]
+ # A small root keeps the run cheap: only the state code is under test.
+ out = bridge.run_policyengine_us(
+ cases,
+ year=YEAR,
+ python=interpreter,
+ root="household_market_income",
+ expand=("household_market_income",),
+ )
+ assert {run.state_code for run in out.runs.values()} == set(
+ bridge.STATE_FIPS
+ )
+ for state, run in out.runs.items():
+ assert run.state_code == state
diff --git a/tests/conftest.py b/tests/conftest.py
index ea7db6c27..5c906f7a0 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -20,6 +20,7 @@
)
_POLICYENGINE_ORACLE_INDICATORS = (
"POPULACE_DYNAMICS_PE_US_DIR",
+ "POPULACE_DYNAMICS_PE_US_PYTHON",
"~/PolicyEngine/policyengine-us",
)
# Section 2.8.4 explicitly assigns this one truth-side-only, byte-identity
diff --git a/tests/estimates/test_birth_evidence_artifact.py b/tests/estimates/test_birth_evidence_artifact.py
index 7f95b8f8a..eed887b64 100644
--- a/tests/estimates/test_birth_evidence_artifact.py
+++ b/tests/estimates/test_birth_evidence_artifact.py
@@ -203,6 +203,8 @@ def test_post_review_sources_are_outside_historical_reducer_identity():
Path("scripts/track_u2_component_diagnostics.py"),
Path("scripts/run_track_u2_registered.py"),
Path("scripts/u2_u1_differential.py"),
+ Path("src/populace_dynamics/bridge/__init__.py"),
+ Path("src/populace_dynamics/bridge/policyengine_us.py"),
)
assert reducer.POST_REVIEW_SHARED_SOURCE_BLOBS == {
Path(
@@ -439,6 +441,15 @@ def test_post_review_exclusions_are_unreachable_from_birth_evidence():
"U2 milestone-2 modules became reachable from the birth-evidence "
f"reducer: {sorted(u2_milestone2 & reachable)}"
)
+ bridge_modules = {
+ "populace_dynamics.bridge",
+ "populace_dynamics.bridge.policyengine_us",
+ }
+ assert bridge_modules.issubset(module_paths)
+ assert bridge_modules.isdisjoint(reachable), (
+ "the opt-in PolicyEngine-US bridge became reachable from the "
+ f"birth-evidence reducer: {sorted(bridge_modules & reachable)}"
+ )
assert graph_exclusions.isdisjoint(reachable), (
"opt-in graph modules became reachable from the birth-evidence "
f"reducer: {sorted(graph_exclusions & reachable)}"
diff --git a/tests/tier_counts.json b/tests/tier_counts.json
index b0381d2d1..71b6828ed 100644
--- a/tests/tier_counts.json
+++ b/tests/tier_counts.json
@@ -1,10 +1,10 @@
{
"schema_version": 1,
"counts": {
- "unit": 5418,
+ "unit": 5628,
"artifact": 3331,
"integration_psid": 1341,
"reproduction_legacy": 520,
- "oracle_policyengine": 189
+ "oracle_policyengine": 207
}
}