Gate Head Start benefits out of net income by default - #9259
Conversation
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 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9259 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 26 13 -13
Branches 0 1 +1
=========================================
- Hits 26 13 -13
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Program Review — multi-agent auditPR #9259 — Gate Head Start out of household net income by default ScopeThe PR moves Why it matters: Files: 2 new params ( Note: branch is 23 behind main — a rebase before merge is advisable. Verified correct (design + code + core test)Design — all 7 checks PASS:
Flag-on reproduces prior inclusion exactly (test Case 2: flag true → 29,000), so any consumer wanting Head Start in net income is fully mitigated. Code — PASS:
Core test — PASS: default-exclusion (the fix) is pinned at all three layers — wrapper (Case 1 = 0), Critical (must fix)None. Should address
Suggestions
Validation summary
Review severity: APPROVEZero criticals; the design is fully verified including phantom closure and SPM neutrality; the core fix (default exclusion from net income) is robustly pinned at three layers with a genuine regression catcher. Remaining items are test-symmetry hardening (flag-on inclusion, reform composition, multi-child aggregation) plus a shared metadata parity nit — none blocking. Next steps
🤖 Generated by /review-program (Claude Code) |
Fixes #9192. Companion data-side record: PolicyEngine/microcosm#593.
What
Moves
head_startandearly_head_startout of the always-ongov.household.household_benefitslist into a newhousehold_head_start_benefitswrapper gated bygov.simulation.include_head_start_benefits_in_net_income(default false) — the exact pattern already used for medicaid/msp/chip/aca viahousehold_health_benefitsandinclude_health_benefits_in_net_income.Why
takes_up_early_head_start_if_eligibleis a bare input defaulting true, andearly_head_startpays state spending/enrollment (≈$21k per enrollee) to every taker. On any dataset that does not store the flag, every modeled-eligible person (age < 3 or pregnant, in the Head Start income/categorical domain) takes up: on the certified populace 2024 artifact this computes $112.3B across 5.16M persons — ≈29.6× the engine's own spending parameters ($3.796B / 180,873 enrollment) — and all of it lands inhousehold_net_income. populace adjudicated the flag as permanently unseedable (no individual-level source exists in SIPP/ASEC/PUF; ACF counts are turnover/capacity — see populace#593), so the fix belongs at this aggregation boundary. Census SPM draws the same line: school meals and WIC count as resources, Head Start does not (these programs were never inspm_unit_benefits).Default false is defensible beyond the phantom: even a correctly-seeded enrollee's per-slot cost is not cash-equivalent household resources, which is the same rationale as the health gate.
Changes
parameters/gov/household/household_head_start_benefits.yaml— new list (head_start,early_head_start)parameters/gov/simulation/include_head_start_benefits_in_net_income.yaml— new switch, default falsevariables/household/income/household/household_head_start_benefits.py— gated wrapper (health-wrapper clone)parameters/gov/household/household_benefits.yaml— wrapper replaces the raw pair in both dated bracketshead_startandearly_head_startvariables tohousehold_benefitsparameter list #4918 and never containedhead_start, so default-path reform behavior is unchanged; the broader staleness of those static copies vs. the live list is pre-existing and out of scope)household_head_start_benefits.yaml(flag off/on, 2022-bracket coverage, cross-switch independence with the health flag in both directions); default-exclusion cases added to thehousehold_benefitsandhousehold_net_incomebaselinesWhat does not change
head_start/early_head_startcompute exactly as before and remain individually queryable (partner analytics-coverage contracts pass unchanged).spm_unit_benefits.Tests run locally
tests/policy/baseline/household— 425 passed (includes new wrapper suite and extended aggregate baselines)tests/policy/contrib/congress/tlaib— 22 passedtests/policy/baseline/gov/hhs/head_start— 40 passedtests/policy/baseline/partners/analytics_coverage/edge_cases/federal/childcare— 47 passedruff format/ruff checkclean🤖 Generated with Claude Code