Skip to content

Add Iowa LIHEAP regular heating assistance - #10292

Open
hua7450 wants to merge 11 commits into
mainfrom
ia-liheap-wave1c
Open

hua7450 wants to merge 11 commits into
mainfrom
ia-liheap-wave1c

Conversation

@hua7450

@hua7450 hua7450 commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Iowa's LIHEAP Regular Assistance heating benefit for FY2026. The model counts members with citizenship or Social Security number documentation for household size, counts the gross income of members 18 and over less child support paid, tests it against 200% of the 2025 poverty guideline, scores the Appendix I matrix (poverty-level points, targeting factors and fuel points) and pays $40 a point between $80 and $800, with a flat $800 for liquid propane, fuel oil and kerosene. In FY2026, a 65-year-old living alone on $18,000 of Social Security in a detached house heated with natural gas receives $480: $18,000 / $15,650 = 115% of poverty gives 5 points, fixed income, age 60 or over and a detached dwelling add 3, natural gas adds 4, and 12 points x $40 = $480.

Fixes #10289

Regulatory authority

Eligibility

Requirement Source How modeled
Iowa residence Manual section 5.00 defined_for = StateCode.IA
Household size counts members with citizenship or SSN documentation; excluded members' income still counts Manual sections 7.00 (page 31) and 7.30 (page 34) ia_liheap_household_size adds is_citizen_or_legal_immigrant; a household with no counted member is ineligible
Income at or below 200% of the poverty guideline; the FY26 table equals 2.0 times the 2025 guideline Manual section 5.10 and Appendix B (page 89); plan section 2.1 ia_liheap_fpg reads the guideline with a one-year lag, ia_liheap_income_limit applies the 2.0 rate, and the limit is inclusive
Energy burden: the household pays a heating vendor, or heat is in unsubsidized rent, or heat is in subsidized rent and the household has an electric account Manual sections 5.41, 5.42 and 5.50 (pages 20 to 22) and Appendix D ia_liheap_has_energy_burden from heat_expense_included_in_rent, receives_housing_assistance and pre_subsidy_electricity_expense
Heating fuel Appendix I item 3 (page 137) heating_type NONE and SOLAR are ineligible; UNSPECIFIED pays nothing

Countable income

Item Source How modeled
Members 18 and over Manual section 6.20 (page 26), Appendix C (pages 110 to 121), plan section 1.9 ia_liheap_countable_earned_income and ia_liheap_countable_unearned_income count members aged 18 or older. Section 6.00 (page 24) counts wages of minors working full time; the model follows section 6.20, Appendix C and the plan, and the conflict is recorded in a comment.
Earned income Appendix C Wages, military basic pay, self-employment (including SSTB), partnership or S corporation, farm operations and farm rental income, each floored at zero
Unearned income Appendix C Social Security (gross), SSI, pensions, military retirement and survivor pay, retirement distributions (which include IRA distributions), railroad retirement, unemployment, workers' compensation, strike benefits, disability benefits, veterans benefits, alimony, rental income, interest, ordinary dividends and gambling winnings. Child support received, capital gains and FIP are not counted.
Child support paid Manual section 6.10 Subtracted once per household, floored at zero
Fixed income only (targeting factor) Definitions (page 81); Appendix C (pages 92, 98, 109-110, 114); section 4.40(3)(a) ia_liheap_fixed_income_only is true when a member receives Social Security, SSI, veterans benefits or a pension, including railroad retirement and military retirement or survivor pay, and no member of any age has another recorded source. Non-countable receipts that Appendix C says are not fixed income, child support received and FIP, also cancel the point. A minor's Social Security identifies a fixed source while its amount stays excluded.

Benefit

  • Poverty-level points (Appendix I item 1): 8 at or below 75% of poverty, 6 up to 100%, 5 up to 125%, 4 up to 200%, with inclusive band tops and the ratio formed in float64 before the lookup.
  • Targeting factors (Appendix I item 2; Definitions pages 79 to 85): +1 each for fixed income only, a member aged 60 or older, a disabled member (is_disabled), a child under 6 and a detached dwelling; -1 for five or more units; -2 for subsidized housing; -4 for heat in rent; -4 for savings over $50,000 (spm_unit_cash_assets). The new ia_liheap_dwelling_type input (detached, 2 to 4 units, 5 or more units) defaults to detached.
  • Fuel points (Appendix I item 3): natural gas 4, electric 4, liquid propane 5, fuel oil 5, solid fuel 2. Kerosene follows fuel oil, and wood and coal take the solid fuel points.
  • Payment (Appendix I award calculation; manual section 11.00; plan section 2.6): $40 a point, at least $80 and at most $800, so 17 points, the most a natural gas or electric household can score, pay $680. Liquid propane, fuel oil and kerosene households receive a flat $800. The bill does not cap the award.

Years covered

Model year Program year Guideline One-person limit
2026 FY2026, heating season October 1, 2025 to April 30, 2026 2025 $31,300

The registry entry records verified_years: "2026".

Not modeled and source conflicts

  • Disability is self-declared in Iowa; is_disabled stands in for it. Savings use spm_unit_cash_assets.
  • Social Security is counted gross; Iowa nets Medicare premiums and SSA repayments. Bank interest and the $120 dividend rule are not separable from the model's inputs.
  • Self-employment and farm income are the model's net inputs floored at zero, which follows Appendix C's filer route; the non-filer gross route is not represented.
  • Kerosene has no matrix row. The model treats it as fuel oil for both the 5 points and the flat $800, an inference: Appendix I and the award rule use the same term "Fuel Oil", the FY26 draft plan (page 48) ties the flat amount to deliverable fuels, and section 12.20 (page 47) lists kerosene as a deliverable fuel. Other fuels have no published points.
  • The fixed-income factor follows the FY26 wording, "no other sources of income" (Appendix I) and "the ONLY source(s) of income" (Definitions), so a working minor's wages and non-countable child support or FIP cancel it. The FY27 manual and Program Notice 27-01, effective October 1, 2026, reword the factor to "no other countable sources of income"; that change is not modeled because only FY2026 is verified. Railroad retirement counts as a pension for the factor: Appendix C defines pensions as regular payments for past service or age (page 109) and labels RRB-1099-R as annuities or pensions (page 110).
  • Dual-household children, roomers, special dwellings, payment routing and application timing are not modeled.
  • The two railroad retirement test households are aged 62 because the SSI module's unearned income list omits railroad_benefits, so an older retiree would receive imputed SSI that Iowa counts. That SSI gap is a separate fix.

Test plan

  • 16 YAML files with 156 cases under policyengine_us/tests/policy/baseline/gov/states/ia/hhs/liheap/; expected values are hand computed from the manual in case comments. The suite was not run locally; CI runs it.
  • YAML tests whose expected values come from an external source (statute, official form or worksheet, official calculator)
  • make format
  • Changelog fragment in changelog.d/

axiom: TheAxiomFoundation/rulespec-us#1834 queued

🤖 Generated with Claude Code

hua7450 and others added 4 commits October 3, 2026 23:37
Encode Iowa's FY2026 regular heating benefit from the FY26 LIHEAP Policy
and Procedures Manual: household size of citizens and qualified
noncitizens, gross countable income of members aged 18 and over less
child support paid, a 200 percent federal poverty guideline limit on the
prior-year guideline through the shared fpg() helper, the energy burden
and housing rules of Appendix D, and the Appendix I payment matrix
(poverty-level points, nine targeting factors, fuel points, $40 per
point between $80 and $800, flat $800 for liquid propane and fuel oil).
Adds the ia_liheap_dwelling_type input the matrix requires and registers
the program with verified_years "2026". Crisis assistance, cooling and
weatherization are out of scope.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Count railroad retirement benefits as unearned income per Appendix C,
score zero poverty-level points when a household has no counted member
and so no guideline, and test the fixed-income-only factor on the
household's recorded income sources across all members so a minor's
Social Security still identifies a fixed-income household while staying
excluded from the countable amount. Reword the self-employment and
Medicare comments to match the manual's two-route and net-of-premium
rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The SSI unearned income list does not include railroad_benefits, so a
railroad retiree aged 65 or older with no other income receives the full
SSI federal benefit, which Iowa then counts. Age 62 keeps the two cases on
the railroad retirement rule they test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hua7450 and others added 2 commits October 20, 2026 01:09
- Fixed income only: recorded non-countable sources that Appendix C says
  are not fixed income (child support received, FIP) now cancel the
  point, through a new other_recorded source list; railroad retirement
  counts as a pension; the test no longer depends on float summation order.
- Kerosene follows fuel oil for both the fuel points and the flat $800.
- References follow the repo convention for PDF pages.
- Replace internal workflow wording with the policy reasons.
- Tests: new fixed-income, child support and kerosene cases (153 total).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review October 20, 2026 05:31
Main's code-health check rejects the reported-only unemployment_compensation
input in income source lists; the total uses the reported amount when present
and otherwise the modeled state unemployment insurance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as draft October 20, 2026 15:16
hua7450 and others added 2 commits October 20, 2026 11:16
Add main's military_retirement_income (military retirement pay and survivor
benefit payments) to Iowa's unearned and fixed income lists: Appendix C
counts pensions and retirement income, and the fixed income definition
includes pensions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review October 21, 2026 04:45
@DTrim99

DTrim99 commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Iowa LIHEAP regular heating assistance

PR #10292 (author: hua7450), head 5a164d58ea. This is a first review. The PR adds Iowa's LIHEAP Regular Assistance heating benefit for FY2026: 25 parameters, 17 variables, 16 test files (156 cases), a programs.yaml entry and a changelog fragment. The PR has no comments or reviews yet. It touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/ia/hhs/liheap/
  • V/ = policyengine_us/variables/gov/states/ia/hhs/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ia/hhs/liheap/

Sources read for this review:

  • FY26 manual: the Iowa LIHEAP Policy and Procedures Manual, effective October 1, 2025 (media/17262, 139 pages). I read pages 13–14, 18–26, 31, 34, 42–43, 46–47, 79–85, 89–123 and 137–138.
  • Benefit matrix: the Clearinghouse FY2026 copy of Appendix I. It is identical to manual page 137.
  • FY2026 state plan: the Clearinghouse copy, pages 5–9. The checkboxes are images, so I decoded each one: a checked box carries a green check mark and an unchecked box does not.
  • Draft plan: the FY26 public comment draft, page 48.
  • Program Notice 26-01: effective October 1, 2025.
  • FY2027: the FY27 manual (pages 89, 96–97 and 147) and the agency LIHEAP page, both fetched 2026-10-21.

Summary

What is right. The published FY26 rules are encoded faithfully:

  • Income limits. 2.0 × the 2025 guideline, with a one-year lag, reproduces all eight Appendix B sizes and the +$11,000 step (p. 89). The limit is inclusive.
  • Matrix and award. The poverty bands, with the ".01% rounded up" note, all nine targeting factors, the five fuel rows, $40 a point, the $80 minimum, the $800 maximum and the flat $800 for liquid propane and fuel oil all match Appendix I (p. 137), section 11.00 (p. 42), the draft plan (p. 48) and plan item 2.6.
  • Eligibility. The household-size rule, the energy-burden rules (Appendix D) and the treatment of income under 18 follow the manual. The under-18 conflict between sections 6.00 and 6.20 is resolved toward 6.20, Appendix C and the plan, and the PR says so.
  • Tests. I hand-checked 15 cases. CI passed all 156.
  • Batch checks. The PR reads total_unemployment_compensation (Integrate modeled state unemployment insurance into income flows #9558) and military_retirement_income (Wire military retirement pay and survivors into federal and state tax (#10181) #10182), with the survivor input pinned. Its comments pass the source-conflict ratchet on the merged tree.

There are no critical findings and four should-address items:

  • S1. Medicare premiums. Social Security counts gross. Plan item 1.9 checks "Excluding MediCare deduction", and the manual counts the amount "paid by check or direct deposit". The PR defers netting "as in Kansas", but Kansas's own rule includes the deduction. medicare_part_b_premium exists, and eight merged LIHEAP states subtract it. Example: a 66-year-old with $33,000 of Social Security gets $0 at the head and $440 under the rule.
  • S2. Rental losses. A rental loss offsets wages. The PR floors self-employment, farm-operations and farm-rent losses per source, but not rental_income. Example: $34,000 of wages and a $6,000 rental loss get $360 instead of $0.
  • S3. Stale Axiom issue. rulespec-us#1834 is open, labelled pe-parity and dispatch-ready, but it describes head 189af398b8. It still lists raw unemployment_compensation, omits military_retirement_income from both source lists, and counts 153 cases where the head has 156.
  • S4. An unpinned source. self_employment_income is never positive in any test. Every case gives it a loss, which floors to zero, so no test fails if it is removed from the earned list.

There are also seven suggestions. They include the flat $800 for heat-in-rent households in propane or oil-heated buildings, the published FY2027 rewording of the fixed-income factor, and Iowa's state supplement (item 17), which is not on main yet.

Status.

  • CI: 17 checks pass and 10 are still pending (nine contrib shards and Rest (Python + variables)); none has failed. The 156 Iowa cases passed in states-shard-2.
  • Branch: 11 commits ahead of main and 2 behind, and it merges cleanly.
  • Siblings: the 11 open LIHEAP sibling PRs conflict only in programs.yaml.

Critical (must fix)

None.

Should address

S1. Medicare premiums are not subtracted: Social Security counts gross, although the plan and the manual count the amount paid

  • Where:
    • P/income/sources/unearned.yaml:11-17: "The model counts social_security gross as a documented approximation: netting the Medicare Part B premium (the model has medicare_part_b_premium) is deferred, as in Kansas".
    • The documentation of V/income/ia_liheap_countable_unearned_income.py, the header of T/income/ia_liheap_countable_unearned_income.yaml and the PR body ("Social Security is counted gross; Iowa nets Medicare premiums") say the same.
  • Sources:
    • FY2026 plan item 1.9 (p. 6, decoded): "Social Security Administration (SSA) benefits" is checked, with "Excluding MediCare deduction" checked and "Including MediCare deduction" unchecked.
    • Manual section 6.10 (p. 25): when the 1040 amount "would cause the household to be over income for eligibility, agencies can request the SSA 1099 from the customer and utilize the 'paid by check or direct deposit' as shown on the SSA 1099". Appendix C repeats this on p. 114.
    • Appendix C, Social Security Benefits (p. 114): "Count the net amount as income". The same row adds: "If a LIHEAP customer receives social security and is being reimbursed for Medicare Part B that was previously deducted, only count the amount received that applies to the verification period."
    • The one gross route. The SSA-1099 Box 5 route includes the premium. The manual replaces it with the net "paid by check or direct deposit" amount exactly when it would make the household ineligible, and the household picks its most favourable verification period.
  • Why the stated precedent does not hold:
    • Kansas counts Social Security gross because its own rule says so: "SSA benefits are counted gross, including the Medicare deduction". Iowa's plan checks the opposite box.
    • medicare_part_b_premium (person, annual) is on main. It holds the out-of-pocket Part B premium, net of Medicare Savings Program (MSP) coverage, and it is zero for non-enrollees.
    • Merged IN, KY, MD, NJ, NY, OH, RI and SD subtract it.
  • Effect (hand-traced from the code for 2026). Take a single person aged 66 with $33,000 a year of Social Security, natural gas heat with a bill, and the default detached dwelling. Their income of $2,750 a month is well above the MSP limit for Qualifying Individuals (QI, 135% of the guideline), so MSP pays nothing. The premium is 12 × $202.90 = $2,434.80.
    • Head: 33,000 > 31,300, so $0.
    • Rule: 33,000 − 2,434.80 = 30,565.20 ≤ 31,300, so the household is eligible. 195.3% of the guideline gives 4 points. Fixed income, elderly and detached add 3, and natural gas adds 4. 11 points gives $440.
    • Existing cases. Retirees under the QI limit, such as integration Case 1 ($18,000), appear to be MSP-covered in the model, so their awards should not change. Countable-unearned Case 1 ($24,000 at age 67) is above that limit, so its expected value would fall to 21,565.20. CI will confirm.
  • Severity: should, as in Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 S1 (Virginia), Add Arizona LIHEAP Standard benefit #10293 S1 (Arizona) and wave 1 (Add New Jersey LIHEAP regular heating assistance #9745 S1, Add New York HEAP regular heating assistance #9744 S1).
  • Fix:
    • Subtract each member's medicare_part_b_premium from that member's Social Security, floored per person. Use a deductions list like Ohio's or subtract it in the formula like New Jersey's.
    • Replace the three comments and the body row. Keep Part D and SSA repayments as not modeled, since neither has a separate input.
    • Add a case like the example above, and update countable-unearned Case 1.

S2. Rental losses offset wages and other income

  • Where:
    • V/income/ia_liheap_countable_unearned_income.py:41 sums p.sources.unearned with add(), keeping signs. That list includes rental_income (P/income/sources/unearned.yaml:49).
    • V/income/ia_liheap_countable_income.py floors only the household total.
    • The PR floors every earned source, including farm_rent_income ("Proceeds from land rental", p. 98), "so a loss does not offset other income". A loss on a rented dwelling still offsets other income, while a loss on rented farmland does not.
  • Sources:
    • Rental Income (p. 110): "Income received from rental properties, such as farmland or dwellings." A loss is not income received.
    • Section 6.20 (p. 26) counts "the gross income of all household members", and plan item 1.8 checks "Gross Income". Plan item 1.9 checks "Rental income". No source lets a loss in one source reduce another.
  • Effect (hand-traced for 2026). A single person aged 40 has $34,000 of wages and a $6,000 rental loss, with natural gas heat, a bill and a detached house.
    • Head: countable income is 28,000, or 178.9% of the guideline: 4 + 1 + 4 = 9 points, $360.
    • With the loss floored: 34,000 > 31,300, so $0.
  • Same flaw in the fixed-income test. V/income/ia_liheap_fixed_income_only.py sums the other unearned sources with signs, so a rental loss can cancel another source. For example, Social Security plus a $500 rental loss and $500 of interest scores as fixed income only.
  • Tests: no case sets a negative rental_income.
  • Severity: should, as in Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 S2, Add Arizona LIHEAP Standard benefit #10293 S2 and Add New Jersey LIHEAP regular heating assistance #9745 S2.
  • Fix:
    • Floor rental_income per person, either by moving it into the floored loop or by flooring each unearned entry.
    • Apply the same floor in the fixed-income test.
    • Add a rental-loss case like the one above.

S3. rulespec-us#1834 no longer matches the head

  • What the issue gets right. It is open and labelled enhancement and pe-parity, and it is dispatch-ready. It has:
    • module paths and corpus status;
    • verbatim law, including plan item 1.9's "Excluding MediCare deduction" and Appendix C's "SSA (State Supplementary Assistance)" line;
    • required outputs, 14 companion tests derived from the sources, and 16 interpretations;
    • its blockers: sources missing from the corpus, an unchecked signed release, billed-run approval and no reviewed oracle revision.
  • Where it is stale. It describes head 189af398b8 (2026-10-20). Two later commits changed the code:
    • 8490f2e587 switched the unearned list to total_unemployment_compensation.
    • 410a0a0241 added military_retirement_income to the unearned and fixed lists. It also added countable-unearned Case 10 and fixed-income Cases 16–17.
  • The resulting gaps:
    • The required-outputs row for countable unearned income still names raw unemployment_compensation, which the Integrate modeled state unemployment insurance into income flows #9558 guard now rejects. It lists 15 sources; the head has 16.
    • The fixed-income row lists five fixed sources; the head has six.
    • The "Parameters" and "Tests" lines say 15 unearned sources, five fixed sources and 153 cases. The head has 16, six and 156 (countable_unearned_income 10, fixed_income_only 17).
    • No companion test covers military retirement pay or the Survivor Benefit Plan (SBP) annuity.
  • Severity: should, as in Add Arizona LIHEAP Standard benefit #10293 S4 (checklist item 9: stale companion values).
  • Fix: update the issue to the final head after S1 and S2.
    • Name total_unemployment_compensation, military_retirement_income, medicare_part_b_premium and the rental floor in the required outputs.
    • Drop the companion-test assumption "no Medicare premium ... withheld", or add a premium case.
    • Add the military-retirement and SBP rows, and refresh the head SHA and case counts.

S4. self_employment_income has no test that would fail if it were dropped

  • Where: P/income/sources/earned.yaml lists self_employment_income, the main self-employment input.
  • Why it is unpinned. It appears in two cases, each time as a loss: earned Case 4 (−$5,000) and countable-income Case 6 (−$12,000).
    • The per-source floor turns both losses into 0, which is the same result as leaving the input out.
    • No case gives it a positive amount, either in the countable totals or in the fixed-income test, where a profit should cancel the fixed-income point.
  • The other entries are covered. All 22 other income-source entries take a nonzero positive value in some case. total_unemployment_compensation is pinned through unemployment_compensation in countable-unearned Case 5 and countable-income Case 7.
  • Severity: should, as in Add Arizona LIHEAP Standard benefit #10293 S5 and Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 S4 (checklist item 13).
  • Fix: give earned Case 4, or a new case, a positive self_employment_income, for example $4,000 beside the wages. Add a fixed-income case in which a self-employment profit cancels the point. The S2 rental-loss case can sit beside it.

Suggestions

G1. The flat $800 reaches heat-in-rent households in propane or oil-heated buildings.

  • The head. V/ia_liheap.py pays deliverable_fuel_amount whenever the fuel category is PROPANE or FUEL_OIL, including when heat is in the rent. The −4 heat-in-rent factor then has no effect.
  • The sources point the other way:
    • The draft plan (p. 48) explains the flat amount by "deliverable fuel customers ... not being eligible for winter shutoff moratorium protection".
    • Section 11.00 (p. 42) raises "the deliverable fuel benefit".
    • Appendix D (p. 122) pays a household with heat in its rent through the electric vendor ("because gas usage is indeterminate") or directly. Such a tenant does not buy deliverable fuel.
  • Example. A single person aged 40 with $28,000 of wages lives in a two-to-four-unit building, with heat in unsubsidized rent and a propane-heated building.
    • The head: $800.
    • The matrix: 4 − 4 + 5 = 5 points, or $200.
  • Tests. No test combines heat in rent with a deliverable fuel.
  • Fix: consider limiting the flat amount to households that buy the fuel themselves (has_heating_expense, or not heat in rent). Otherwise, record the reading as an inference and pin it with a test.

G2. FY2027 is published and rewords the fixed-income factor.

  • What changed. The FY27 manual, effective October 1, 2026, keeps the matrix and award lines. Its limits are $31,920 plus $11,360 (pp. 96–97), which period 2027 reproduces through the one-year lag.
  • The reworded factor. It reads "Fixed Income Only (no other countable sources of income)" (p. 147), and the Definitions say "countable income types" (p. 89). The head's comment notes this as not modeled.
  • The effect in period 2027. Child support received, FIP and a minor's wages still cancel the point. Example: ia_liheap.yaml Case 15 (a grandparent and a grandchild who receives child support), run for 2027:
    • The rule: 18,000 / 21,640 = 83.2% gives 6, the targeting factors give 3 with the fixed-income point, and natural gas gives 4. 13 points is $520.
    • The head: $480.
  • Severity. 2027 is not claimed (verified_years: "2026"), so this is a suggestion, as in Add Arizona LIHEAP Standard benefit #10293 G5.
  • Fix: consider a 2026-10-01 entry that limits the other-source test to countable sources. For example, empty other_recorded and age-gate the other sources, and add a 2027 case. Otherwise, keep the note and say in the body that 2027 is a carried-forward estimate.

G3. Interest and the fixed-income factor.

  • The head. All interest_income counts as countable income, a documented approximation. As a result, fixed-income Case 9 pins that $200 of interest cancels the fixed-income point.
  • The rule. Appendix C excludes "Interest Bearing Bank Accounts" (p. 103), the usual interest of a low-income retiree, and FY27 limits the factor to countable sources.
  • The effect. In microsimulation, many retirees who live only on Social Security but report some interest lose the point (−$40).
  • Fix: consider leaving interest_income out of the fixed-income other-source test, or record the effect beside the approximation in P/income/sources/unearned.yaml.

G4. The agency-page references now show FY27 (live URL).

  • Where. P/eligibility/fpg_rate.yaml, P/eligibility/fpg_year_lag.yaml, the V/eligibility/ia_liheap_fpg.py and ia_liheap_income_limit.py reference tuples and the T/eligibility/ia_liheap_income_limit.yaml header cite the agency page.
  • What the titles claim: "200% of the 2025 federal poverty guidelines for October 1, 2025 to September 30, 2026".
  • What the page shows. On 2026-10-21 it read "200% of the 2026 federal poverty guidelines. These guidelines are for October 1, 2026 - September 30, 2027", with $31,920 for one person. The closest Wayback capture is from 2025-07-14 (FY25), so no FY26 capture exists.
  • Why only a suggestion. Every value keeps a stable primary reference: the FY26 manual is a fixed file (media/17262), and plan item 2.1 is on p. 9. That makes this milder than Add Arizona LIHEAP Standard benefit #10293 S3, where the primary anchors were broken.
  • Fix: drop the agency-page references, or retitle them to describe what the page shows. Add the FY27 manual if G2 is taken.

G5. Record the under-18 conflict as a source-conflict candidate.

  • The conflict. Section 6.00 (p. 24) counts the income of minors "working full-time". Section 6.20, Appendix C (p. 102) and plan item 1.9 do not.
  • Where the PR records it. P/income/countable_min_age.yaml ("that conflict is resolved in favor of Sections 6.20 and Appendix C") and V/income/ia_liheap_countable_earned_income.py ("a recorded conflict").
  • Why nothing forces it. Neither wording matches the ratchet's pattern, so the guard does not prompt for an entry.
  • Precedent. source_conflicts/candidates.yaml already lists Alabama LIHEAP plan-versus-manual conflicts (for example us-al:liheap-insurance-payments).
  • Fix: consider adding a us-ia: LIHEAP candidate with the three sources.

G6. Iowa's state supplement (item 17, forward-looking branch note).

  • The rule. Appendix C counts "SSA (State Supplementary Assistance) - Fully state-funded" in the Social Security row (p. 114).
  • The state of main. It has no Iowa supplement variable today. Add Iowa state supplementation for 2026 #8017 (ia_ssa, with SNAP and HUD countable partitions) is approved but not merged, so the head is correct against its base.
  • Fix: once Add Iowa state supplementation for 2026 #8017 merges, decide whether ia_liheap should count ia_ssa, probably in both the unearned and the fixed lists. Update the Axiom issue's interpretation 8, which says no supplement is counted.

G7. Small test and polish items.

  • A mixed-state case. No case runs an Iowa and a non-Iowa SPM unit in one simulation (item 15). It is safe today: ia_liheap_fuel_category defaults to OTHER, a valid key in p.fuel_points (0). A mixed case would keep it pinned.
  • A size-9 limit case. Sizes 1, 2, 3, 4 and 8 are pinned. A size-9 case would pin Appendix B's "Add $11,000 for each additional member" line.
  • UNSPECIFIED with heat in rent. V/eligibility/ia_liheap_eligible.py calls ineligibility "the all-state convention". The merged AL, FL, MS, OH and OK formulas admit that household, and main's heating_type documentation names UNSPECIFIED as the right value when the tenant does not know the fuel (Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 G2, Add Arizona LIHEAP Standard benefit #10293 G2).
    • In Iowa the fuel drives 2–5 points. Either admit the household with a documented default fuel, or reword the comment as Iowa's own choice.
  • The NONE exclusion. NONE in fuel_known is unpinned. NONE without heat in rent already fails the burden test. This is optional.

Verified correct

Values against sources:

  • Income limit (section 5.10, p. 18; Appendix B, p. 89; plan 2.1 note, p. 9).
    • 2.0 × (15,650 + 5,500 × (n − 1)) gives 31,300, 42,300, 53,300, 64,300, 75,300, 86,300, 97,300 and 108,300, plus 11,000 a member. That is all of Appendix B.
    • The plan's printed 150.00% is explained on p. 9 as an OLDC form limit, and the PR follows the note.
    • The test is at or below the limit (<=), matching "200% or below" and "Over 200%" ineligible.
  • The lag. fpg_year_lag 1 is required: period 2026 reads the 2025 guideline. The same lag gives FY27's $31,920 and $11,360 for period 2027.
  • Effective date. 2025-10-01 on every parameter matches Program Notice 26-01 ("Effective Date: October 1, 2025").
  • Matrix (Appendix I, p. 137; Clearinghouse copy; draft p. 48):
    • Poverty bands. 8 / 6 / 5 / 4 at band tops of 75 / 100 / 125 / 200%. The -.inf first threshold and right=True reproduce "rounded up to the next percentage", and the float64 ratio keeps exact band tops in band.
    • Targeting factors. +1 each for fixed income only, elderly (60+), disabled, a child under 6 and a detached dwelling. −2 for subsidized housing, −1 for five or more units, −4 for heat in rent and −4 for savings over $50,000. Each applies once.
    • Fuel rows. Natural gas 4, electric 4, liquid propane 5, fuel oil 5, solid fuel 2.
  • Award. $40 a point, $80 minimum and $800 maximum (p. 137; plan 2.6, p. 9). The flat $800 for liquid propane and fuel oil follows Appendix I, section 11.00 (p. 42) and the draft's "both a minimum and maximum". There is no bill cap (section 5.41, "does not depend on the actual amount owed").
  • Eligibility:
    • Household size. Members with citizenship or SSN documentation count, and an ineligible member's income still counts (7.00, p. 31; 7.30, p. 34). An all-ineligible household is ineligible.
    • Energy burden (5.41–5.42, pp. 20–21; Appendix D, pp. 122–123). Paying a vendor directly qualifies, as do heat in unsubsidized rent and subsidized heat in rent with an electric account. Subsidized housing with everything in the rent does not qualify, and neither does solar.
    • Not required. There is no categorical eligibility (plan 1.4 "No", decoded) and no asset test (plan 2.3).
  • Income:
    • Gross income (plan 1.8, decoded). Gross wages follow the pay-stub route, "gross amount before any pre-tax deductions" (p. 97).
    • Under 18. Members under 18 are excluded (6.20; Appendix C, pp. 102 and 114; plan 1.9 box unchecked).
    • Child support paid is deducted (p. 92).
    • Not counted: child support received, FIP, general assistance and capital gains (Appendix C, pp. 92, 98 and 99; plan 1.9 boxes unchecked).
    • Gambling winnings count ("Count only winnings subject to tax", p. 99; plan 1.9 checks lottery winnings).
    • IRA distributions are counted once through retirement_distributions.
  • Fixed-income factor (Definitions, p. 81; Appendix C, pp. 90, 92, 98, 109–110 and 114; 4.40(3)(a), p. 14). SSA, SSI, SSDI, VA and pensions count, with railroad and military retirement as pensions. A minor's Social Security identifies a fixed source. Child support received and FIP cancel the point under the FY26 wording.

Hand-checked expectations (15 cases). The 2025 guideline is 15,650 + 5,500 a member.

  • ia_liheap.yaml Case 1: 18,000 / 15,650 = 1.1502 gives 5; fixed, elderly and detached give 3; gas gives 4. 12 points, $480.
  • ia_liheap.yaml Case 2: 34,000 / 26,650 = 1.2758 gives 4. +1 − 1 − 2 − 4 = −6, plus 4, is 2 points, $80.
  • ia_liheap.yaml Case 3: 28,000 / 15,650 = 1.789 gives 4. −11 plus 4 is −3 points, raised to $80.
  • ia_liheap.yaml Case 4: 4 + 1 + 5 = 10 points, $400, raised to the flat $800.
  • ia_liheap.yaml Case 11: 30,000 / 15,650 = 1.917 gives 4. Detached and wood give 4 + 1 + 2 = 7 points, $280.
  • ia_liheap.yaml Case 13: 60,000 / 32,150 = 1.866 gives 4. Child and detached give 2; electric gives 4. 10 points, $400.
  • ia_liheap.yaml Case 15: 18,000 / 21,150 = 85.1% gives 6. Elderly (63) and detached give 2, with no fixed-income point because of the child support; gas gives 4. 12 points, $480.
  • Integration Case 3: 22,000 + 6,000 = 28,000; 28,000 / 21,150 = 1.324 gives 4. +1 − 4 plus propane 5 is 6 points, $240, raised to $800.
  • Integration Case 4: size 2 with the undocumented parent excluded, limit 42,300. 38,000 / 21,150 = 1.797 gives 4. 4 + 2 + 4 = 10 points, $400.
  • Integration Case 5: 26,000 − 4,800 = 21,200, and 21,200 / 15,650 = 1.355 gives 4. Disabled +1 and electric 4 make 9 points, $360.
  • Integration Case 7: 15,000 / 21,150 = 0.709 gives 8. 8 + 5 + 5 = 18 points, the matrix maximum, and $720 is raised to the flat $800 (kerosene as fuel oil).
  • Poverty-points Cases 2–3 and 6–7: 11,737.5 / 15,650 = 0.75 exactly gives 8, and 11,738 gives 0.750032, so 6. 40,187.5 / 32,150 = 1.25 exactly gives 5, and 40,500 / 32,150 = 1.2597 gives 4.
  • Income-limit Case 3: 2 × (15,650 + 7 × 5,500) = 108,300, Appendix B size 8.
  • Income-eligible Cases 1–2: 31,300 is eligible and 31,301 is not.
  • Fixed-income Case 11: a child's $9,000 of Social Security is the only source, so the factor is true and countable income is 0 (p. 114; 4.40(3)(a)).

Batch checks:

  • Integrate modeled state unemployment insurance into income flows #9558 (item 11): the list reads total_unemployment_compensation, pinned by countable-unearned Case 5 and countable-income Case 7. The four guard tests passed in CI.
  • Wire military retirement pay and survivors into federal and state tax (#10181) #10182 (item 16): military_retirement_income is in the unearned and fixed lists. Fixed-income Case 17 pins the survivor input (countable 14,400).
  • Document disjoint health premium inputs and correct consumer treatment #10046 (item 14): not applicable. Iowa counts wages "before any pre-tax deductions" on the pay-stub route and deducts no health premium; Medicare is S1. Box 1 wages on the elected tax-return route are an alternative the household may choose, not the rule the model has to follow.
  • State-gated keys (item 15): safe.
    • ia_liheap_fuel_category is an enum gated to Iowa whose default, OTHER, is a key in p.fuel_points.
    • ia_liheap_household_size (int) feeds only fpg() arithmetic and where().
    • ia_liheap_dwelling_type is an ungated input that is compared, never indexed.
    • The Microsimulation job passed. A mixed-state case is optional (G7).
  • Federal 110% floor: never binds. The limit is 2.0 × the same lagged guideline that gov.hhs.liheap.fpg_floor (1.1) would use. For size 1 that is $31,300 against $17,215 (2025 guideline) or $17,556 (2026). Not wiring the floor matches the merged states whose limits are far above it.
  • IIJA (item 12): not applicable. Iowa's matrix has no separate supplement, and FY2027 is G2.
  • Source-conflict ratchet (Tie source-conflict markers to the register shared with Axiom #10287):
    • The PR's conflict comments ("that conflict is resolved in favor of", "a recorded conflict") do not match CONFLICT_COMMENT_RE, and there are no marker attempts.
    • In CI, test_conflict_sounding_comments_do_not_grow passed on the merge with a6fafb06c2. main's two newer commits touch no code-health file.
  • Wiring:
    • Registry. The programs.yaml IA entry is present (verified_years: "2026"), and IA is added to the coverage line. test_programs_registry passed.
    • Aggregates. None, which matches every merged LIHEAP state.
    • Gating. defined_for = StateCode.IA is on 16 variables, and ia_liheap is gated by ia_liheap_eligible. test_members_are_state_gated passed.
    • Take-up. FIP is read through the take-up-gated tanf.
    • Changelog. changelog.d/ia-liheap-wave1c.added.md is top level and typed added.
  • Descriptions (item 6): all 25 end "under the Low Income Home Energy Assistance Program".
  • References (item 7): every manual and plan anchor I opened shows its value:
    • manual pp. 14, 18, 20, 24–26, 31, 34, 42, 46–47, 79–85, 89, 90–121 (the Appendix C rows cited) and 137;
    • plan pp. 5, 6, 8 and 9;
    • draft p. 48.
    • Multi-page titles are quoted and use the #page 24,26,102,114 form, and variable tuples carry # PDF pages comments. The agency-page references are G4.

Checklist results (LIHEAP batch):

  1. State plan and manual: pass for every matrix cell, limit and award line.
  2. Income definition: fail on two items. The Medicare premium is not subtracted (S1), and rental losses net (S2).
  3. Backdating: pass for 2026. Earlier years are documented backfill. FY2027 is published and its fixed-income rewording is not used (G2).
  4. Household and arrangement rules: pass, with every eligibility branch tested. Heat in rent with a deliverable fuel is G1, and heat in rent with UNSPECIFIED fuel is G7.
  5. Tests: 156 cases, 15 hand-checked, periods plain. One source is unpinned (S4).
  6. Descriptions: pass.
  7. References: pass, apart from the live agency page (G4).
  8. Wiring: pass.
  9. Axiom: valid but stale (S3).
  10. Branch and CI: a clean merge; 17 pass and 10 pending, none failed.
  11. Integrate modeled state unemployment insurance into income flows #9558: pass.
  12. IIJA / FY2027: IIJA not applicable; FY2027 is G2.
  13. Mutation check: every eligibility, limit, points and award branch is pinned except self_employment_income (S4) and the NONE exclusion (G7).
  14. Document disjoint health premium inputs and correct consumer treatment #10046: not applicable.
  15. State-gated index defaults: safe.
  16. Wire military retirement pay and survivors into federal and state tax (#10181) #10182: pass.
  17. Merged state SSPs: not applicable at this head; main has no Iowa supplement. G6 is the branch note for Add Iowa state supplementation for 2026 #8017.
  • Federal 110% floor: never binds.

Axiom line assessment

Valid but stale: axiom: TheAxiomFoundation/rulespec-us#1834 queued (S3).

  • What is in order. The issue is open and labelled enhancement and pe-parity.
    • Blockers. Its "Why queued" section records the sources missing from the corpus: the FY26 manual, Program Notice 26-01, the Clearinghouse matrix, the draft plan, the 2025 guidelines, a dated agency-page capture and IAC 421-22. It also records the unchecked signed release, billed-run approval and the lack of a reviewed oracle revision.
    • Content. Its verbatim text matches the sources I read, including the decoded plan checkboxes. Its 14 companion tests are derived from the sources, not from PolicyEngine output.
  • What needs updating:
    • The head SHA, the unearned and fixed source lists (total_unemployment_compensation, military_retirement_income) and the case count (156).
    • After S1 and S2: the Medicare subtraction and the rental floor in the required outputs and companion tests.
    • If taken, G1, G2 and G6.

CI status

17 pass, 10 pending, 0 failed. Run 39635435060 tests merge commit 57ef08f560, which merges head 5a164d58ea into a6fafb06c2 (pull_request, started 2026-10-21 16:30 UTC). I re-checked gh pr checks 10292 at 19:44 UTC, at the end of this review.

  • Passed: all four states shards; the household, irs, ssa-usda, rest-a, rest-b, reform and contrib-hhs baseline shards; Rest (Python, shard-1 + variables); Microsimulation; Household API Partners; PR validation; Package and compatibility; and Quick Feedback.
  • Still pending: nine contrib shards (congress, other 1, 2a, 2b and 3, and states 1–4) and Rest (Python + variables). None of them runs the Iowa LIHEAP files.
  • Where the Iowa tests ran. States-shard-2 (batch 4/14, states/ia) ran all 16 files: 12 + 4 + 9 + 6 + 5 + 15 + 11 + 5 + 6 + 8 + 10 + 17 + 8 + 10 + 11 + 19 = 156 passed.
  • Guards. Rest shard-1 passed:
  • Known main-caused failure. Not applicable: the run started after 13:24 UTC, and the ratchet passed.
  • Locally: I ran no tests or model probes. The examples in S1, S2, G1 and G2 are hand traces of the code at the head.

Branch status

0 critical, 4 should and 7 suggestions are open at this head.

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FY26 limits, every Appendix I matrix cell, $40 a point, the $80/$800 bounds and the energy-burden rules check out, and all 156 Iowa cases pass. Four should-address items, the same pattern as VA and AZ: (1) Social Security counts gross though plan 1.9 excludes the Medicare deduction (age 66 with $33k SS: $0 vs $440); (2) rental losses offset wages; (3) rulespec-us#1834 is stale; (4) self_employment_income is never positive in a test. Seven suggestions. Full report in the comment above.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Iowa LIHEAP regular heating assistance

2 participants