From 9164f9f5063a223f524e9b9dc15c52322b58b47f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 8 Aug 2026 14:17:51 -0700 Subject: [PATCH 1/3] Gate Head Start benefits out of net income by default Move head_start and early_head_start from the always-on gov.household.household_benefits list into a household_head_start_benefits wrapper gated by gov.simulation.include_head_start_benefits_in_net_income (default false), mirroring the health benefits switch. On data without seeded take-up flags the engine's default-true flags value every eligible person at per-enrollee cost (~$112B vs $3.8B actual on 2024 microdata), inflating household_net_income. Also adds the wrapper to the BOOST and legacy ECPA static benefit lists so the flag-on path composes under those reforms. Fixes #9192 Co-Authored-By: Claude Fable 5 --- .../head-start-net-income-gate.added.md | 1 + .../gov/household/household_benefits.yaml | 6 +- .../household_head_start_benefits.yaml | 10 +++ ...ude_head_start_benefits_in_net_income.yaml | 6 ++ .../boost/boost_middle_class_tax_credit.py | 1 + .../congress/tlaib/end_child_poverty_act.py | 1 + .../income/household/household_benefits.yaml | 30 +++++++++ .../household_head_start_benefits.yaml | 65 +++++++++++++++++++ .../household/household_net_income.yaml | 33 ++++++++++ .../household_head_start_benefits.py | 24 +++++++ 10 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 changelog.d/head-start-net-income-gate.added.md create mode 100644 policyengine_us/parameters/gov/household/household_head_start_benefits.yaml create mode 100644 policyengine_us/parameters/gov/simulation/include_head_start_benefits_in_net_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml create mode 100644 policyengine_us/variables/household/income/household/household_head_start_benefits.py diff --git a/changelog.d/head-start-net-income-gate.added.md b/changelog.d/head-start-net-income-gate.added.md new file mode 100644 index 00000000000..a63e5498d34 --- /dev/null +++ b/changelog.d/head-start-net-income-gate.added.md @@ -0,0 +1 @@ +Gate Head Start and Early Head Start behind gov.simulation.include_head_start_benefits_in_net_income (default false), so per-enrollee program values no longer enter household net income by default, mirroring the health benefits inclusion switch. diff --git a/policyengine_us/parameters/gov/household/household_benefits.yaml b/policyengine_us/parameters/gov/household/household_benefits.yaml index ab3e4f76b71..593be7c3310 100644 --- a/policyengine_us/parameters/gov/household/household_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_benefits.yaml @@ -19,8 +19,7 @@ values: - educational_assistance - financial_assistance - survivor_benefits - - head_start - - early_head_start + - household_head_start_benefits # One-time energy relief payments. # Paid at the same time as the Alaska Permanent Fund Dividend, # which is part of IRS gross income. @@ -49,8 +48,7 @@ values: - educational_assistance - financial_assistance - survivor_benefits - - head_start - - early_head_start + - household_head_start_benefits # Contributed. - basic_income - spm_unit_capped_housing_subsidy diff --git a/policyengine_us/parameters/gov/household/household_head_start_benefits.yaml b/policyengine_us/parameters/gov/household/household_head_start_benefits.yaml new file mode 100644 index 00000000000..18ea4cb0c83 --- /dev/null +++ b/policyengine_us/parameters/gov/household/household_head_start_benefits.yaml @@ -0,0 +1,10 @@ +description: The government counts these Head Start program sources as household benefits. +values: + 2022-01-01: + - head_start + - early_head_start + +metadata: + unit: list + period: year + label: Household Head Start benefits diff --git a/policyengine_us/parameters/gov/simulation/include_head_start_benefits_in_net_income.yaml b/policyengine_us/parameters/gov/simulation/include_head_start_benefits_in_net_income.yaml new file mode 100644 index 00000000000..0a0b56241e9 --- /dev/null +++ b/policyengine_us/parameters/gov/simulation/include_head_start_benefits_in_net_income.yaml @@ -0,0 +1,6 @@ +description: Whether to include Head Start benefits in net income. +values: + 0000-01-01: false +metadata: + unit: bool + label: Include Head Start benefits in net income diff --git a/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py b/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py index 62dc8fa45ee..f56b87f3bc9 100644 --- a/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py +++ b/policyengine_us/reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py @@ -74,6 +74,7 @@ def formula(household, period, parameters): "basic_income", "spm_unit_capped_housing_subsidy", "household_state_benefits", + "household_head_start_benefits", ] if parameters(period).gov.hud.abolition: BENEFITS = [ diff --git a/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py b/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py index f451389f505..f24d8e49ffc 100644 --- a/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py +++ b/policyengine_us/reforms/congress/tlaib/end_child_poverty_act.py @@ -110,6 +110,7 @@ def formula(household, period, parameters): "basic_income", "spm_unit_capped_housing_subsidy", "household_state_benefits", + "household_head_start_benefits", "ecpa_child_benefit", ] if parameters(period).gov.hud.abolition: diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml index 8b97c7d2f53..545b50217f6 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_benefits.yaml @@ -57,3 +57,33 @@ household_health_benefits: 0 output: household_benefits: 0 + +- name: Head Start values are excluded from household benefits by default. + period: 2024 + input: + age: 30 + head_start: 8_000 + early_head_start: 21_000 + child_support_received: 0 + workers_compensation: 0 + educational_assistance: 0 + financial_assistance: 0 + survivor_benefits: 0 + social_security: 0 + ssi: 0 + tanf: 0 + unemployment_compensation: 0 + snap: 0 + wic: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + acp: 0 + ebb: 0 + basic_income: 0 + spm_unit_capped_housing_subsidy: 0 + household_state_benefits: 0 + commodity_supplemental_food_program: 0 + household_health_benefits: 0 + output: + household_head_start_benefits: 0 + household_benefits: 0 diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml new file mode 100644 index 00000000000..5876df84048 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml @@ -0,0 +1,65 @@ +- name: Case 1, flag off excludes Head Start values from household benefits. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.simulation.include_head_start_benefits_in_net_income: false + head_start: 8_000 + early_head_start: 21_000 + snap: 0 + output: + household_head_start_benefits: 0 + household_benefits: 0 + +- name: Case 2, flag on includes Head Start and Early Head Start values. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.simulation.include_head_start_benefits_in_net_income: true + head_start: 8_000 + early_head_start: 21_000 + snap: 0 + output: + household_head_start_benefits: 29_000 + household_benefits: 29_000 + +- name: Case 3, the flag also governs the 2022 benefit list bracket. + period: 2023 + absolute_error_margin: 0.01 + input: + gov.simulation.include_head_start_benefits_in_net_income: true + head_start: 8_000 + early_head_start: 21_000 + snap: 0 + acp: 0 + ebb: 0 + output: + household_head_start_benefits: 29_000 + household_benefits: 29_000 + +- name: Case 4, the Head Start flag does not activate health benefits. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.simulation.include_head_start_benefits_in_net_income: true + head_start: 8_000 + medicaid_cost: 500 + chip: 300 + snap: 0 + output: + household_head_start_benefits: 8_000 + household_health_benefits: 0 + household_benefits: 8_000 + +- name: Case 5, the health flag does not activate Head Start benefits. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.simulation.include_health_benefits_in_net_income: true + head_start: 8_000 + early_head_start: 21_000 + medicaid_cost: 500 + snap: 0 + output: + household_health_benefits: 500 + household_head_start_benefits: 0 + household_benefits: 500 diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml index b6aea7ef3cf..4578a277500 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml @@ -64,3 +64,36 @@ household_health_costs: 0 output: household_net_income: 0 + +- name: Head Start values are excluded from household net income by default. + period: 2024 + input: + age: 30 + head_start: 8_000 + early_head_start: 21_000 + household_market_income: 0 + child_support_received: 0 + workers_compensation: 0 + educational_assistance: 0 + financial_assistance: 0 + survivor_benefits: 0 + social_security: 0 + ssi: 0 + tanf: 0 + unemployment_compensation: 0 + snap: 0 + wic: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + acp: 0 + ebb: 0 + basic_income: 0 + spm_unit_capped_housing_subsidy: 0 + household_state_benefits: 0 + commodity_supplemental_food_program: 0 + household_health_benefits: 0 + household_refundable_tax_credits: 0 + household_tax_before_refundable_credits: 0 + household_health_costs: 0 + output: + household_net_income: 0 diff --git a/policyengine_us/variables/household/income/household/household_head_start_benefits.py b/policyengine_us/variables/household/income/household/household_head_start_benefits.py new file mode 100644 index 00000000000..1e7a74016d1 --- /dev/null +++ b/policyengine_us/variables/household/income/household/household_head_start_benefits.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class household_head_start_benefits(Variable): + value_type = float + entity = Household + label = "Household Head Start benefits" + unit = USD + definition_period = YEAR + documentation = ( + "Annual Head Start and Early Head Start value included in " + "household_benefits only when " + "gov.simulation.include_head_start_benefits_in_net_income is " + "enabled. The programs are valued at per-enrollee cost and their " + "take-up flags default to true, so datasets without seeded " + "enrollment flags value every eligible person at full cost." + ) + + def formula(household, period, parameters): + p = parameters(period) + if p.gov.simulation.include_head_start_benefits_in_net_income: + return add(household, period, p.gov.household.household_head_start_benefits) + else: + return 0 From faaf13772ac1b32bcac3d304e9b3878cab23f318 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 13 Aug 2026 21:31:31 -0400 Subject: [PATCH 2/3] Add review-requested tests: flag-on net income, ECPA composition, multi-child aggregation Applies the /review-program audit items on #9259: pins flag-on inclusion end-to-end at household_net_income (29,000), exercises the reform static-list wrapper under ECPA with the switch on, and proves Person-to-Household aggregation across two enrolled children. Renames the bracket-coverage case for clarity. The economy metadata item is skipped to preserve template parity: no gov/simulation switch carries the key, including the health template. Co-Authored-By: Claude Fable 5 --- .../household_head_start_benefits.yaml | 22 +++++++++++- .../household/household_net_income.yaml | 34 +++++++++++++++++++ .../end_child_poverty_act/integration.yaml | 33 ++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml index 5876df84048..5360f356863 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_head_start_benefits.yaml @@ -22,7 +22,7 @@ household_head_start_benefits: 29_000 household_benefits: 29_000 -- name: Case 3, the flag also governs the 2022 benefit list bracket. +- name: Case 3, flag on composes through the pre-2024 household_benefits bracket. period: 2023 absolute_error_margin: 0.01 input: @@ -63,3 +63,23 @@ household_health_benefits: 500 household_head_start_benefits: 0 household_benefits: 500 + +- name: Case 6, per-person values aggregate to the household level. + period: 2026 + absolute_error_margin: 0.01 + input: + gov.simulation.include_head_start_benefits_in_net_income: true + people: + parent: + age: 30 + child1: + age: 4 + head_start: 8_000 + child2: + age: 1 + early_head_start: 21_000 + households: + household: + members: [parent, child1, child2] + output: + household_head_start_benefits: 29_000 diff --git a/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml b/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml index 4578a277500..bad1d7579a7 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/household_net_income.yaml @@ -97,3 +97,37 @@ household_health_costs: 0 output: household_net_income: 0 + +- name: Head Start values are included in household net income when the switch is on. + period: 2024 + input: + gov.simulation.include_head_start_benefits_in_net_income: true + age: 30 + head_start: 8_000 + early_head_start: 21_000 + household_market_income: 0 + child_support_received: 0 + workers_compensation: 0 + educational_assistance: 0 + financial_assistance: 0 + survivor_benefits: 0 + social_security: 0 + ssi: 0 + tanf: 0 + unemployment_compensation: 0 + snap: 0 + wic: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + acp: 0 + ebb: 0 + basic_income: 0 + spm_unit_capped_housing_subsidy: 0 + household_state_benefits: 0 + commodity_supplemental_food_program: 0 + household_health_benefits: 0 + household_refundable_tax_credits: 0 + household_tax_before_refundable_credits: 0 + household_health_costs: 0 + output: + household_net_income: 29_000 diff --git a/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml b/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml index c4e598fdfda..366e4729dac 100644 --- a/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml +++ b/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml @@ -156,3 +156,36 @@ household_state_benefits: 0 output: household_benefits: 0 + +- name: Head Start values compose into ECPA household benefits when the inclusion switch is on. + period: 2023 + reforms: policyengine_us.reforms.congress.tlaib.end_child_poverty_act.end_child_poverty_act + input: + gov.contrib.congress.tlaib.end_child_poverty_act.in_effect: true + gov.simulation.include_head_start_benefits_in_net_income: true + age: 30 + head_start: 8_000 + early_head_start: 21_000 + social_security: 0 + ssi: 0 + snap: 0 + wic: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + child_support_received: 0 + workers_compensation: 0 + educational_assistance: 0 + financial_assistance: 0 + survivor_benefits: 0 + acp: 0 + ebb: 0 + tanf: 0 + high_efficiency_electric_home_rebate: 0 + residential_efficiency_electrification_rebate: 0 + unemployment_compensation: 0 + basic_income: 0 + spm_unit_capped_housing_subsidy: 0 + household_state_benefits: 0 + output: + household_head_start_benefits: 29_000 + household_benefits: 29_000 From 8e26f69a3b8484b27167dd2a40f244df19d247f2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 14 Aug 2026 00:14:02 -0400 Subject: [PATCH 3/3] Relocate ECPA composition test to per-file reform suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The congress CI shard runs each proposal directory in one subprocess at its documented memory ceiling (Makefile: romney batch alone peaked 7.1 GB). The new ECPA flag-on case added a reformed-system variant to the tlaib batch — locally measured +1.7 GB peak RSS (19.2 GB vs 17.4 GB without it, macOS accounting) — which stalled the shard on both CI attempts (killed at 28-31m of the 60m budget after integration.yaml's fifth dot; main stays green at 13m without the case). tests/policy/reform runs one subprocess per file, so the same assertion runs in an isolated process instead of the saturated batch. No coverage change: the case is byte-identical plus a header noting the placement constraint. Co-Authored-By: Claude Fable 5 --- .../end_child_poverty_act/integration.yaml | 33 --------------- .../ecpa_head_start_benefits_composition.yaml | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 policyengine_us/tests/policy/reform/ecpa_head_start_benefits_composition.yaml diff --git a/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml b/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml index 366e4729dac..c4e598fdfda 100644 --- a/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml +++ b/policyengine_us/tests/policy/contrib/congress/tlaib/end_child_poverty_act/integration.yaml @@ -156,36 +156,3 @@ household_state_benefits: 0 output: household_benefits: 0 - -- name: Head Start values compose into ECPA household benefits when the inclusion switch is on. - period: 2023 - reforms: policyengine_us.reforms.congress.tlaib.end_child_poverty_act.end_child_poverty_act - input: - gov.contrib.congress.tlaib.end_child_poverty_act.in_effect: true - gov.simulation.include_head_start_benefits_in_net_income: true - age: 30 - head_start: 8_000 - early_head_start: 21_000 - social_security: 0 - ssi: 0 - snap: 0 - wic: 0 - free_school_meals: 0 - reduced_price_school_meals: 0 - child_support_received: 0 - workers_compensation: 0 - educational_assistance: 0 - financial_assistance: 0 - survivor_benefits: 0 - acp: 0 - ebb: 0 - tanf: 0 - high_efficiency_electric_home_rebate: 0 - residential_efficiency_electrification_rebate: 0 - unemployment_compensation: 0 - basic_income: 0 - spm_unit_capped_housing_subsidy: 0 - household_state_benefits: 0 - output: - household_head_start_benefits: 29_000 - household_benefits: 29_000 diff --git a/policyengine_us/tests/policy/reform/ecpa_head_start_benefits_composition.yaml b/policyengine_us/tests/policy/reform/ecpa_head_start_benefits_composition.yaml new file mode 100644 index 00000000000..797d3424607 --- /dev/null +++ b/policyengine_us/tests/policy/reform/ecpa_head_start_benefits_composition.yaml @@ -0,0 +1,41 @@ +# Head Start values compose into ECPA's static household_benefits list when +# gov.simulation.include_head_start_benefits_in_net_income is on. +# +# NOTE: This lives under tests/policy/reform (one subprocess per file) rather +# than contrib/congress/tlaib because the congress structural batch runs each +# proposal directory in a single process at its memory ceiling; the extra +# reformed-system variant this case requires pushed the tlaib batch past the +# CI runner's RAM and stalled the job. + +- name: Head Start values compose into ECPA household benefits when the inclusion switch is on. + period: 2023 + reforms: policyengine_us.reforms.congress.tlaib.end_child_poverty_act.end_child_poverty_act + input: + gov.contrib.congress.tlaib.end_child_poverty_act.in_effect: true + gov.simulation.include_head_start_benefits_in_net_income: true + age: 30 + head_start: 8_000 + early_head_start: 21_000 + social_security: 0 + ssi: 0 + snap: 0 + wic: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + child_support_received: 0 + workers_compensation: 0 + educational_assistance: 0 + financial_assistance: 0 + survivor_benefits: 0 + acp: 0 + ebb: 0 + tanf: 0 + high_efficiency_electric_home_rebate: 0 + residential_efficiency_electrification_rebate: 0 + unemployment_compensation: 0 + basic_income: 0 + spm_unit_capped_housing_subsidy: 0 + household_state_benefits: 0 + output: + household_head_start_benefits: 29_000 + household_benefits: 29_000