Parameterize Head Start income eligibility: 100–130% FPG discretionary band and 2024 housing cost adjustment - #9291
Parameterize Head Start income eligibility: 100–130% FPG discretionary band and 2024 housing cost adjustment#9291PavelMakarchuk wants to merge 3 commits into
Conversation
Expose two grantee-discretionary provisions of 45 CFR 1302.12 as reformable parameters, both defaulting to the statutory floor so baseline behavior (and partner contract tests) are unchanged: - gov.hhs.head_start.income_limit (default 1.0): the 1302.12(d) discretionary band lets grantees serve families between 100% and 130% of the poverty guidelines; reforms set this to 1.3. The 35% slot-share cap is an enrollment-composition rule that cannot be modeled at the household level. - gov.hhs.head_start.housing_cost_adjustment (in_effect default false, income_share_threshold 0.3): the 2024 final rule (89 FR 67720, effective October 2024) lets grantees deduct housing costs exceeding 30% of gross income when determining eligibility. Developed test-first: reform tests exercise both provisions, and baseline YAML guards pin the unchanged default behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHNW1s7kaFkksoyiTDoEkm
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHNW1s7kaFkksoyiTDoEkm
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9291 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 22 16 -6
Branches 0 1 +1
=========================================
- Hits 22 16 -6
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:
|
…YAML - Gate the housing cost adjustment behind a Python if on the scalar in_effect parameter (repo convention), so baseline runs never materialize the housing_cost dependency tree. - Replace the pytest reform file with a YAML reform test using dotted parameter inputs: faster (no extra CountryTaxBenefitSystem builds), runs in the sharded YAML CI jobs, and counts toward coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHNW1s7kaFkksoyiTDoEkm
hua7450
left a comment
There was a problem hiding this comment.
Thanks for this. After reading the current eCFR text I want to restructure the approach a bit before this merges — three points.
1. Both flexibilities are current law, not reforms — they should be baseline toggles, not reform-only parameters
- The 100–130% band is 45 CFR 1302.12(d): "A program may enroll an additional 35 percent of participants … whose incomes are below 130 percent of the poverty line."
- The housing adjustment is in the CFR itself, not just the FR preamble — 45 CFR 1302.12(i)(1)(ii): "A program may make an adjustment to a family's gross income calculation … to account for excessive housing costs … (A) Determine if a family spends more than 30 percent of their total gross income on housing costs, as defined in part 1305; and (B) If applicable, reduce the total gross income by the amount spent on housing costs that exceed more than 30 percent."
Both are grantee-discretionary ("may"), so we shouldn't apply them universally in the microsim, but they're also not policy reforms, so gating them behind income_limit: 1.3 / in_effect: true reform edits (as this PR does) puts them in the wrong place — a screener shouldn't have to construct a reform to reach current law. Please instead model each as a baseline input variable on the SPM unit, default False, following the is_tanf_enrolled / takes_up_head_start_if_eligible pattern:
head_start_uses_discretionary_income_limit(bool, SPMUnit, YEAR, default False) → when true, use the 130% limithead_start_applies_housing_cost_adjustment(bool, SPMUnit, YEAR, default False) → when true, deduct housing costs above 30% of gross income
Parameters become: income_limit/statutory.yaml = 1 (§1302.12(c)(1)(i)), income_limit/discretionary.yaml = 1.3 (§1302.12(d)), keep housing_cost_adjustment/income_share_threshold.yaml = 0.3 (§1302.12(i)(1)(ii)(A)), and drop housing_cost_adjustment/in_effect.yaml. The reform test file should become baseline tests that set the toggles per case.
2. Income base should be Head Start gross income per part 1305, not AGI
The pre-existing formula compares tax-unit adjusted_gross_income to FPG, but 45 CFR 1305.2 defines: "Income means gross income and only includes wages, business income, unemployment compensation, pension or annuity payments, gifts that exceed the threshold for taxable income, and military income (excluding [hostile-fire pay and BAH]) … it does not include refundable tax credits nor any forms of public assistance." Since the housing test in point 1 is 30% of that gross income, we need the right base. Please add a list parameter parameters/gov/hhs/head_start/income/sources.yaml (same shape as gov/hhs/tanf/cash/income/sources/earned.yaml):
- employment_income # wages
- self_employment_income # business income
- unemployment_compensation
- pension_income # pension or annuity payments (taxable + tax-exempt)
- military_service_income # military income; BAH/hostile-fire exclusion not separately modeledand a head_start_gross_income variable that adds them, used by is_head_start_income_eligible. Gifts above the taxable threshold have no model variable — note that in the parameter comment. Interest, dividends, capital gains, rental, Social Security, alimony, and public assistance are excluded by the definition and should not be listed.
3. Entity
Part 1305 defines family as the persons in the household supported by the enrolling child's parents/guardians, which maps to the SPM unit better than the tax unit. The PR already reads housing_cost from the SPM unit while taking income from the tax unit, so for multi-tax-unit households the deduction is misattributed. Please move the whole comparison to the SPM unit (spm_unit_fpg, SPM-unit head_start_gross_income, SPM-unit toggles).
Happy to talk through any of this before you rewrite.
Responds to a partner request on
is_head_start_income_eligible(backs bothhead_startandearly_head_start). Developed test-first: the reform tests were written and confirmed failing before the implementation.What this does
Exposes two grantee-discretionary provisions of 45 CFR 1302.12 as reformable parameters. Both default to the statutory floor, so baseline behavior is unchanged — no partner contract tests are touched (the
analytics_coveragesuite pinsis_head_start_income_eligible: falseat ~100.3% FPG, inside the band, so a default change would be a partner-facing API change).gov.hhs.head_start.income_limit(default 1.0 × FPG) — §1302.12(c)(1)(i) statutory floor. §1302.12(d) lets grantees fill up to 35% of slots with families between 100% and 130% FPG; that slot-share cap is an enrollment-composition rule that can't be modeled at the household level, so the discretionary ceiling is represented by reforming this parameter to 1.3 (e.g., for screeners surfacing "possibly eligible with caveat").gov.hhs.head_start.housing_cost_adjustment(in_effectdefault false,income_share_threshold0.3) — the 2024 final rule (89 FR 67720, Aug 21, 2024; effective October 2024) lets grantees deduct housing costs exceeding 30% of gross income when determining eligibility. Uses the existinghousing_costvariable (rent, real estate taxes, HOA fees, mortgage payments, homeowners insurance), projected from the SPM unit.Tests
tests/policy/reform/test_head_start_income_eligibility.py: baseline floor holds at/just above FPG; baseline ignores housing costs; the band reform makes 120% and exactly 130% FPG eligible but not 135%; the housing reform deducts only the excess over 30% of gross income (19,200 ≤ 20,000 eligible; 23,200 > 20,000 not).Draft caveats (why not ready for review yet)
housing_costcomponent list matches the rule's definition needs the same verification.in_effectswitch gates behavior; flagging for maintainer preference.🤖 Generated with Claude Code
https://claude.ai/code/session_01WHNW1s7kaFkksoyiTDoEkm
Generated by Claude Code