Skip to content

Add Wisconsin WHEAP regular heating benefit - #10434

Open
hua7450 wants to merge 2 commits into
mainfrom
wi-liheap-wave1d
Open

hua7450 wants to merge 2 commits into
mainfrom
wi-liheap-wave1d

Conversation

@hua7450

@hua7450 hua7450 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Wisconsin's Home Energy Assistance Program (WHEAP) regular heating benefit for FFY2026, administered by the Department of Administration's Division of Energy, Housing and Community Resources. The model counts citizens, qualified noncitizens and noncitizens who receive Social Security in household size (ineligible members' income still counts), tests gross income against 60% of state median income with categorical eligibility and the federal 110% poverty floor, and computes the HE+ formula: a share of heating cost that falls from 70.2% at or below 60% of the poverty guideline to 17.6% at 215% or above. The heating cost is bounded by a heat proxy for the fuel, dwelling type and room count. Payments are truncated to whole dollars, capped at $2,580, and not issued below $30. The separate non-heating electric (public benefits) award, crisis assistance and weatherization are out of scope.

Fixes #10424

Regulatory authority

Eligibility

Requirement Source How modeled
Wisconsin residence Stat. 16.27; Manual 3.2.2 defined_for = StateCode.WI
At least one citizen or eligible noncitizen Manual 3.2.1, 3.2.2.4, 3.2.6.2; p.33, 35, 44, 82 wi_wheap_household_size counts members who are citizens or qualified noncitizens (is_citizen_or_legal_immigrant) or who receive Social Security (social_security > 0), the manual's eligible noncitizens; size 0 is ineligible
Income at or below 60% of SMI Stat. 16.27(5)(b); Manual p.45, 166 liheap_smi_limit (floor of 60% of the four-person SMI, then of the size share) reproduces all 20 calculator rows; annual income stands in for the annualized one-month test
Federal 110% poverty floor 42 U.S.C. 8624(b)(2)(B) 2025 guidelines; binds at size 20
Categorical eligibility Stat. 16.27(5)(c); Manual p.45-46 Every member, including ineligible noncitizens, must receive W-2 cash, FoodShare or SSI: a member is covered by ssi or receives_ssi (read month by month), by FoodShare when in the SNAP unit (is_snap_excluded_member, so refugees and five-year-bar residents outside SNAP are not covered), or by W-2 cash when a citizen or qualified noncitizen; passes the income test only
Energy burden Manual p.41-43 has_heating_expense, or heat included in rent without rental assistance or public housing; assisted housing with heat in rent is ineligible
Heating fuel reported Brief convention heating_type UNSPECIFIED or NONE pays $0
No asset test Manual p.45; Plan 2.3 Not applied

Countable income

Item Source How modeled
Wages Manual p.46, 58, 59-60 employment_income of members 18 and older not in high school (is_in_k12_school)
Self-employment, farm, partnership, rental and royalty income Manual p.50-51, 55 Netted across sources and members and floored at zero for the household; a net loss cannot offset other income; minors' and high-school students' business earnings excluded
Unearned income Manual p.48-49, 55-58 SSI, pensions, military retirement, veterans benefits, workers' compensation, disability, alimony, child support received, strike benefits, General Relief, gambling winnings, trust income, gifts (financial_assistance), survivor benefits
Social Security and Railroad Retirement Manual p.48, 58-59 Net of medicare_part_b_premium (Part C and D premiums have no input)
Retirement account withdrawals Manual p.49, 59 Counted from age 73
Interest and dividends Manual p.47, 55 Counted per member when the combined annual amount is at least $120
W-2 cash grants Manual p.57 tanf
Court-ordered child support paid Manual p.48-49, 58-59 child_support_expense deducted
Not counted Manual p.59-63 Unemployment compensation, capital gains, bonuses, lump sums, tax refunds, FoodShare, foster and kinship payments

Benefit

  • Poverty ratio = benefit income / 2025 poverty guideline for the eligible-member size, held between 0.60 and 2.15 (Calculator Computation!C5-C12). A categorically eligible household above its 60% SMI limit uses the limit as its benefit income (Manual 2.3.1 item 1; Plan 1.6).
  • Raw benefit = (2.66667 - ratio) / 2.94269 x benefit heating cost (Computation!C15, C16, C39); truncated, capped at $2,580 and paid only when at least $30 (Computation!C17-C18, C41-C43; Manual p.26; agency plan 2.6).
  • Heat proxy = base + per-room amount x rooms, by fuel class (fuel oil, natural gas, propane, electric, wood or other) and dwelling type (HT_Proxy!A4:H28). Rooms come from bedrooms, held between one and nine (HE+ accepts 1-9; a studio counts one room); a motel or camper counts one room.
  • Benefit heating cost = 0.75 x proxy when heat is in rent or the cost is zero; otherwise the heating bill, less a 60% baseload when the fuel is electricity, held between 0.75 and 2 times the proxy (Computation!C19-C35; Manual p.27).
  • New input wi_wheap_dwelling_type (Single family, 2- to 4-unit building, Apartment or multi-unit building, Mobile home, Motel or camper/RV; default Single family). Kerosene, coal and other fuels use the wood-or-other row; solar has no row.
  • The largest FFY2026 result is $2,574, so the $2,580 cap does not bind.

Not modeled

Item Source Treatment
Prior-month test period, present-month option, extra-paycheck rule, 12-month averaging Manual p.46, 62 Annual amounts stand in
Entire-prior-month categorical receipt; W-2 group covering every member; state SSI supplement Manual p.45-46; Stat. 16.27(5)(c) Annual SPM-unit receipt and federal ssi approximate them
Section 42 (LIHTC) heat-in-rent units, shared and business meters, in-kind rent Manual p.41-43, 53, 91-92 Not identifiable from existing inputs
Room count beyond bedrooms Manual p.79 Bedrooms alone stand in for rooms
FoodShare receipt as a route to eligible-noncitizen status Manual 3.2.2.4, p.35 No person-level FoodShare input; noncitizens count through immigration status or Social Security. The model's FoodShare immigration rules in Wisconsin admit only citizens and qualified noncitizens, so no modeled FoodShare recipient is left out
Court-placed foster children, whom the application leaves out Manual 3.2.6.4 item 4, p.45 No input identifies a court placement, so these children count in household size
Regular retirement withdrawals before 73; tribal per capita payments; land contract interest; land rent threshold; SGIRF gross receipts Manual p.49, 51-52, 56, 58 No existing inputs
SSN, case-head and student case-head rules; institutions; one benefit per season; February 1 payment reduction; supplemental benefits Manual ch. 3; Stat. 16.27(4)(b) Not represented
Non-heating electric benefit, crisis, weatherization, HE+ services Manual 2.1, 2.3.1 Out of scope

FFY2027: the PY2027 manual and the final FFY2027 plan (maximum $2,784) are posted, but no FFY2027 calculator, funding factor or proxy table is public, so 2027 and later results reuse the FFY2026 values as unverified estimates. Earlier years are also unverified estimates through parameter backfilling.

Microsimulation: heating_type, the heating bills and wi_wheap_dwelling_type are not in current datasets; callers must supply them.

Test plan

56 YAML cases in 7 files (not re-run locally after the review fixes; CI validates them):

  • income limits at sizes 0, 1, 4, 8 and 20, a mixed-status household, the exact-limit boundary and the size-20 federal floor at $132,164 (eligible) and $132,165 (ineligible);

  • categorical eligibility for FoodShare, all-SSI (computed and reported receipt), mixed, W-2, and FoodShare households with an undocumented or refugee member;

  • countable income: minors' and high-school students' wages and self-employment, household business netting with an SSTB loss, Social Security and Railroad Retirement net of Part B, the $120 interest threshold, withdrawals at 72 and 73, child support received and paid, ignored unemployment, gambling and W-2;

  • heating cost inside, below and above the proxy bounds, heat in rent, electric baseload, motel rooms, the nine-room cap, kerosene and solar, all five dwelling types;

  • benefit ratio floor and cap, the categorical limit substitution, truncation before the $30 minimum, a noncitizen with Temporary Protected Status who receives Social Security, alone and with a citizen spouse, a Milwaukee family end to end.

  • YAML tests with expected values from the manual, calculator and plan

  • ruff format

  • CI passes (program tests, registry and code-health checks)

  • Changelog fragment in changelog.d/

axiom: TheAxiomFoundation/rulespec-us#1895 queued

🤖 Generated with Claude Code

hua7450 and others added 2 commits October 23, 2026 18:11
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	policyengine_us/programs.yaml
@codecov

codecov Bot commented Oct 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (909176a) to head (00b8d56).
⚠️ Report is 631 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main    #10434    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            4         8     +4     
  Lines           76       179   +103     
  Branches         2         3     +1     
==========================================
+ Hits            76       179   +103     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@DTrim99

DTrim99 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Wisconsin WHEAP regular heating benefit

PR #10434 (author: hua7450), head 00b8d56388. This is a first review. The PR adds the regular heating benefit of the Wisconsin Home Energy Assistance Program (WHEAP), the state's LIHEAP heat benefit run by DOA's Division of Energy, Housing and Community Resources, for FFY2026 (model year 2026). It adds 31 parameter files, 8 variables (one of them the new input wi_wheap_dwelling_type), 7 test files with 56 cases, a programs.yaml entry and a changelog fragment. The only comment is Codecov's. It touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/wi/doa/wheap/
  • V/ = policyengine_us/variables/gov/states/wi/doa/wheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/wi/doa/wheap/

Sources read for this review:

  • Calculator: the NEW HE+ Benefit Calculator, FFY2026. I read the cell formulas on the Benefits, Computation, HT_Proxy and SMI_Lookup sheets. The cached results are error values, so the formulas are what counts.
  • Manual: the PY27 WHEAP Manual, revised August 2026. I read 2.3.1 (pp. 26-27), 3.2.2.4 (p. 35), 3.2.5-3.2.6 (pp. 41-44), 3.3 with Tables 3.4-3.6 (pp. 45-63), 4.4.13-4.4.28 (pp. 76-88) and Appendix E (p. 167).
    • Why PY27. The PR cites the February 2026 PY26 edition through a Wayback capture. The archive rejected my automated requests, and the agency's PY26 URL now returns 404.
    • How I checked PY26. I compared each PY26 citation with the PY27 text and with the verbatim PY26 quotations in rulespec-us#1895. The passages the PR relies on read the same in PY27, except gambling winnings (G2). For the same reason, I could not check the PY26 #page= anchors.
  • Plans: the FY2026 Detailed Model Plan (Clearinghouse copy), items 1.4-1.9 and 2.1-2.6 (pp. 5-8, 12), and the FFY2027 agency-posted plan, item 2.6. The FFY2026 agency-posted plan, the source of the $2,580 maximum, is reachable only through Wayback, and I could not load it.
  • Law: Wis. Stat. 16.27 ((5)(b)-(d) and (6)) and 42 U.S.C. 8624(b)(2)(B).

Summary

What is right. The FFY2026 HE+ formula is encoded faithfully:

  • Proxy table. All 50 HT_Proxy cells (base and per-room amounts, 5 fuels × 5 dwelling rows) match the parameters.
  • SMI limits. The 20 SMI_Lookup limits are reproduced by liheap_smi_limit (floor(share × floor(0.6 × 123,148))). That includes the 8 Appendix E sizes.
  • Formula. The pieces all match the calculator: the 2.66667 and 2.94269 constants, the 0.60-2.15 ratio bounds, the 2025 guideline (15,650 + 5,500), the 60% electric baseload, the 0.75× and 2× proxy bounds, the nine-room cap, one room for a motel, truncation and the $30 minimum.
  • Income. The income rules follow the manual: Table 3.5 Medicare netting from Social Security and Railroad Retirement, and the cross-member self-employment and rental offset floored at zero (3.3.5 allows it, so the wave's per-person flooring item does not apply). Unemployment compensation is ignored (Table 3.6), and minors' and high school students' earnings are excluded.
  • Household and eligibility. Household size counts eligible noncitizens, including Social Security recipients (3.2.2.4), and still counts ineligible members' income (4.4.26.2). The federal 110% floor binds from size 20, and the size-20 cases pin both sides of it.
  • Tests and guards. I hand-checked 14 expectations, and CI ran all 56 cases green. The guards pass on the merged tree: the Tie source-conflict markers to the register shared with Axiom #10287 ratchet finds 0 matches, Integrate modeled state unemployment insurance into income flows #9558 does not apply because unemployment compensation is not counted, and Count military SBP annuities in state benefit income lists #10317 is satisfied because the list names military_retirement_income. The state-gated keys are safe (the Add New Hampshire state supplementation for 2026 #10156 pattern).

There are no critical findings and one should-address item:

  • S1. Untested sources and branches. 15 of the 23 income-source entries never take a positive value in a test. The unearned per-source floor, the W-2 citizenship condition in categorical eligibility and the heat-in-rent branch of the heating cost are not pinned either.

There are also six suggestions:

  • G1: count Wisconsin's state SSI supplement and Caretaker Supplement (item 17). wi_ssp_countable_for_snap already gives exactly the WHEAP combination. Axiom interpretations 3 and 12 wrongly say no such variable exists.
  • G2: the published PY27 manual ignores gambling winnings, and the FFY2027 plan raises the maximum (2027 is unclaimed).
  • G3: record the manual/calculator/plan choices as backlog candidates and note them at the code.
  • G4: SOLAR pays $0, although heating_expense treats it as grid-tied electric heat.
  • G5: bedrooms understate the HE+ room count, which matters for proxy-based households.
  • G6: small fixes.

Status.

  • CI: 29 pass and 3 skip; none failed. States-shard-3 ran all 56 Wisconsin cases.
  • Branch: 2 commits ahead of main and 59 behind. The only conflict is in programs.yaml (the coverage line and the VA/WI entries), a branch note.

Critical (must fix)

None.

Should address

S1. 15 of the 23 income-source entries, the unearned per-source floor and two branches have no test that would fail if they were dropped

  • Where:
    • P/income/sources/self_employment.yaml, rental.yaml and unearned.yaml;
    • the unearned loop in V/income/wi_wheap_countable_income.py;
    • the w2_member line in V/eligibility/wi_wheap_categorically_eligible.py;
    • the heat_in_rent | arm of the final where in V/payment/wi_wheap_heating_cost.py.
  • Untested entries. No case whose outputs depend on countable income gives these a positive value:
    • Self-employment group: farm_operations_income and partnership_s_corp_income (2 of 4).
    • Rental group: farm_rent_income (1 of 2).
    • Unearned: 12 of 14 entries:
      • ssi. It is positive only in the categorical-eligibility file, which asserts nothing that depends on income.
      • pension_income, military_retirement_income, veterans_benefits, workers_compensation and disability_benefits;
      • alimony_income, strike_benefits, general_assistance, estate_income, financial_assistance and survivor_benefits.
  • Tested entries: employment_income, self_employment_income, sstb_self_employment_income, rental_income, both investment sources, child_support_received and gambling_winnings. So are the separate reads of retirement_distributions, social_security, railroad_benefits, medicare_part_b_premium, tanf and child_support_expense.
  • Unpinned logic:
    • The unearned floor. No case has a negative unearned source, such as an estate_income loss. So removing the per-source max_ fails nothing, although it is what stops a trust loss from offsetting wages.
    • The W-2 condition. w2_member requires is_citizen_or_legal_immigrant. Categorical Case 4 has only citizens, so dropping the condition fails nothing.
    • The heat-in-rent arm. Every heat-in-rent case reports a $0 bill. Removing heat_in_rent | leaves those cases at 0.75 × proxy through the clip, so the arm matters only when heat is in rent and a bill is also reported, and no case does that.
  • Severity: should, as in Add Maine HEAP Fuel Assistance #10437 S4, Add Vermont Seasonal Fuel Assistance #10436 S2 and Add Nevada Energy Assistance Program Fixed Annual Credit #10432 S1 (the mutation rule).
  • Fix:
    • Every source. Add one countable-income case that gives each listed source a distinct positive amount, putting the two business entries and farm_rent_income in the household business group, and expect the sum. Add wi_ssp_countable_for_snap there too if G1 is adopted.
    • Trust loss. Add a case with wages and an estate_income loss that expects the wages alone.
    • W-2. Add a W-2 cash household with an undocumented adult that expects false.
    • Heat in rent with a bill. Add a heat-in-rent case with a positive bill that expects 0.75 × proxy.

Suggestions

G1. Count Wisconsin's state SSI supplement and Caretaker Supplement (checklist item 17), and correct Axiom interpretations 3 and 12.

  • The rule counts both.
    • The SSI row. Table 3.4's row (PY27 p. 58, also quoted from PY26 in rulespec-us#1895) says: "Enter the federal and state benefit as two separate SSI income lines. Do not count SSI-E income."
    • The Caretaker Supplement. Table 3.4 also counts the "SSI Caretaker Supplement (C-Supp)" (p. 55).
    • Categorical eligibility. Wis. Stat. 16.27(5)(c) makes recipients of "state supplemental payments under ... s. 49.77" categorical, and manual 3.3.1 item 2(c) (p. 46) says "The SSI category includes the Caretaker Supplement Income".
  • What exists on main, and in this PR's base. Add Wisconsin state supplementation for 2026 #10173 (merged 2026-10-22) added wi_ssp = wi_ssp_regular + wi_ssi_e + wi_cts. It also added wi_ssp_countable_for_snap = wi_ssp_regular + wi_cts, which is exactly WHEAP's rule: the state line and C-Supp, without SSI-E. The unearned list has ssi only.
  • The Axiom issue's premise is wrong. Interpretations 3 and 12 of rulespec-us#1895 say that the Caretaker Supplement and the state SSI supplement "have no variable at this base" and that "the state SSI line" has no input. The base 6a92d6a983 contains all four variables.
  • Example (hand trace, 2026). A disabled SSI parent with children aged 8 and 5 heats a 3-bedroom single-family home with natural gas ($900). Federal SSI is $994 a month ($11,928). The state supplement is 4 × 83.78 + 8 × 92.16 = $1,072.40, and the Caretaker Supplement is (275 + 165) × 12 = $5,280.
    • Head: 11,928 / 26,650 = 0.448, raised to 0.60, so 0.702303 × 900 = 632.08 and the payment is $632.
    • Rule: 18,280.40 / 26,650 = 0.68594, so 1.98073 / 2.94269 × 900 = 605.79 and the payment is $605.
  • Why only a suggestion. The effect is tens of dollars. The wave rates item 17 a suggestion even when the supplement is in the base (Add Maine HEAP Fuel Assistance #10437 G3 for me_ssp, Add New Mexico LIHEAP regular heating assistance #10431 G1). It is the strongest of the suggestions, though, because the manual names the state line and C-Supp explicitly.
  • Fix:
    • Income. Add wi_ssp_countable_for_snap to P/income/sources/unearned.yaml. It is monthly, and the loop already sums with options=[ADD].
    • Categorical eligibility (optional). Treat a positive wi_ssp_countable_for_snap like ssi. CTS children could also count as covered.
    • Documentation. Correct the body's "state SSI supplement" row and interpretations 3 and 12.

G2. FFY2027 (unclaimed): the published PY27 manual ignores gambling winnings, and the FFY2027 plan raises the maximum.

  • Gambling. PY27 Table 3.6 (p. 60): "Lottery winnings, Bingo, or any other income from gambling will be ignored." The PR's PY26 citation counts it, so 2026 is right. Model year 2027 (FFY2027) still counts it.
  • Maximum. FFY2027 agency plan item 2.6 (p. 9) sets the maximum at $2,784. It does not bind under the FFY2026 constants.
  • Limits. PY27 Appendix E (p. 167) prints $39,864 for one person. The shared liheap_smi_limit gives $39,863 because it floors twice. Axiom interpretation 15 notes $1 gaps at sizes 1, 5 and 6.
  • Severity. verified_years is "2026" and the body calls 2027 an unverified estimate, so this is a documented deferral (NJ S3/S4).
  • Fix: add 2026-10-01 entries that drop gambling_winnings and set the maximum to $2,784, or name both changes in the body and the programs.yaml note.

G3. Record the agency-source choices in the backlog and at the code.

  • The choices. rulespec-us#1895 records four, but no source_conflicts/candidates.yaml entry and no code comment does:
    • Categorical households above the limit (interpretation 4). Manual 2.3.1 item 1 and plan 1.6 pay them at 60% of SMI. Calculator C40 uses actual income. The PR follows the manual: 8-person Case 4 pays $306, against the calculator's $291. V/wi_wheap.py's comment does not mention the calculator.
    • Proxy bounds (interpretation 6). Manual 2.3.1 item 3 calls the proxy 75% of average cost and caps cost at two times the proxy, which is 1.5× the table value. The calculator caps at 2× the table value, and the PR follows the calculator.
    • Maximum (interpretation 5). The ACF-accepted plan (item 2.6, p. 12) prints $2,147, the agency-posted plan $2,580 and calculator C17 $2,575. P/payment/maximum.yaml cites only the agency plan. The formula's largest award is 0.702303 × 3,666 = $2,574.66, so $2,575 and $2,580 never bind. $2,147 would bind for single-family fuel oil homes with 5-9 rooms, low income and costs at twice the proxy.
    • General Relief and lump sums (interpretation 12). Plan 1.9 leaves General Assistance unchecked and checks one-time lump sums; the PR follows the manual.
  • Fix:

G4. SOLAR pays $0, although main treats a solar-heated home as grid-tied with electric supplemental heat.

  • The head. wi_wheap_heating_cost gives SOLAR no fuel class, so the cost is $0. wi_wheap_eligible still passes the household, and the award is $0.
  • The input convention. On main, heating_expense reads pre_subsidy_electricity_expense for SOLAR ("Solar-primary homes are assumed grid-tied with electric supplemental heat").
  • The cited sentence. The test cites manual 4.4.28.3 (p. 86). That sentence concerns a home whose only electricity source is solar cells ("no fuel record shall be entered"), not solar heat. HE+ would record such a home's backup fuel, electricity, as primary.
  • Example. One person with $20,000 of income and a $1,500 electric bill in a 3-bedroom single-family home. As electric heat: cost 1,500 × 0.4 = 600, inside 438.75 to 1,170 (proxy 261 + 108 × 3 = 585). Ratio 1.27796, so (2.66667 − 1.27796) / 2.94269 × 600 = $283. The head pays $0.
  • Precedent. Vermont mapped SOLAR to its electricity row (Add Vermont Seasonal Fuel Assistance #10436).
  • Fix: add SOLAR to the electricity class. Otherwise, keep the $0 but correct the comment and cite this as a modeling choice.

G5. Bedrooms understate the HE+ room count, which lowers the proxy for proxy-based households.

  • The rule. Manual 4.4.17 (pp. 78-79) counts every room except bathrooms, hallways, entryways and similar areas, so kitchens and living and dining rooms count. The PR uses bedrooms (documented in the body, the code and Axiom interpretation 8).
  • Example. A one-person household with $10,000 of income, heat in rent, in a 2-bedroom apartment heated by natural gas. The head uses 2 rooms: 0.75 × (116 + 68 × 2) = 189, so $130. With a kitchen and a living room (4 rooms): 0.75 × 388 = 291, so $200. The same gap moves the floor and cap for households that report costs.
  • Fix (optional): add a wi_wheap_rooms input that defaults to bedrooms, held between 1 and 9 as now, so that callers who know the room count can supply it. Otherwise, state the direction of the bias in the body.

G6. Small fixes.

  • Land rent. farm_rent_income sits in the business offset. PY27 Table 3.4 (p. 56) lists Land Rent as its own type, apart from the "(R)" rental and royalty type that 3.3.5 puts in the offset, and ignores it under $120. Moving it to the unearned list, or noting the choice in rental.yaml, would match the manual.
  • UNSPECIFIED fuel. The body cites a "brief convention" for paying $0. Manual 4.4.28.3 (p. 86) supports it directly: "If the applicant does not provide the fuel type to the agency within 30 days, the application will be denied." Cite that.
  • Heat-only electric meters. Calculator C22 removes the baseload only for usage "b" (both). Add a one-line note in electric_baseload_rate.yaml that heat-only meters are not identified, as Axiom interpretation 7 says.
  • The body. Tick "CI passes": all 56 cases passed in CI.

Verified correct

Values against sources:

  • Proxy table. The base (column F) and per-room (column E) amounts for fuel oil, natural gas, propane, electric heat and wood/other match HT_Proxy rows 4-28 for all five dwelling rows: 50 of 50. That includes the −13 per room for mobile-home fuel oil and the rooming-house row used for MOTEL_OR_CAMPER. Calculator dwelling codes map a/d/m/r/s to Apartment, 2-4 units, Mobile home, Rooming house and Single family.
  • Formula constants.
    • Formula and limits. Computation C15 2.66667, C16 2.94269, C10/C11 min(·, 2.15) and max(·, 0.6), C19 0.6, C30/C31 2× and 0.75×, C35 0.75× when the reported cost is zero, C41 TRUNC, and C42 $30.
    • Rooms. Benefits B27 caps rooms at 9, and C12 rejects 0 rooms, which supports the one-room floor.
    • Guideline. C6/C7 are 15,650 and 5,500, the 2025 guideline, matching fpg_year_lag = 1 and main's gov.hhs.fpg 2025 values.
  • Income limits. floor(0.6 × 123,148) = 73,888, and floor(73,888 × share) reproduces SMI_Lookup sizes 1-20 exactly (38,421 … 128,565), including Appendix E sizes 1-8 as quoted in the Axiom issue.
  • The 215% flag. Calculator C14 (HT_FPL_Not_Eligible) is referenced by no output cell (C40 reads only the SMI flag), so paying a single person at a ratio of 2.30 (T/wi_wheap.yaml Case 3) is right.

Income definition (manual 3.3 and Tables 3.4-3.6):

  • Counted. Wages of adults 18 and over (p. 58). Earnings of minors and of full- or part-time high school students are ignored (p. 60).
  • Interest and dividends. "one entry per household member" with the $120 test (pp. 46, 56); interest_income plus ordinary_dividend_income matches the 1040 lines 2a+2b and 3b the manual names.
  • Retirement withdrawals. All count at 73 or older (p. 48).
  • Court-ordered child support paid is deducted, and received support counts (p. 49).
  • Self-employment and rental. "The offset does not have to be applied to only the same household member", and losses "may not be offset by wages, interest income, government program benefits" (p. 51). The household-level netting floored at zero is exactly this.
  • Medicare. Part B, C and D premiums are deducted from Social Security, and up to the standard premium from federal or Railroad Retirement pay (Table 3.5, p. 59).
    • Inputs. medicare_part_b_premium is out of pocket and net of MSP. Part A is not in the rule. main has no Part C or D premium input.
    • One deduction per person. Netting once against Social Security plus Railroad Retirement per person avoids a double deduction.
  • Unemployment compensation is ignored (Table 3.6, p. 63).
  • W-2. Cash grants count through tanf, which includes wi_works on main.
  • No double counts. pension_income and retirement_distributions share no components, and none of the self-employment inputs double-count.
  • Pre-tax deductions (OK Add Oklahoma state supplementation for 2026 #10165 / checklist item 14): not applicable. WHEAP counts gross wages with no premium deduction (p. 48 item 5), and employment_income is gross.

Household, eligibility and housing:

  • Household size.
    • Eligible noncitizens. 3.2.2.4 (p. 35) marks a noncitizen eligible on "FoodShare or any form of monthly Social Security income", or on the listed statuses. 4.4.26.2 (p. 82) excludes ineligible noncitizens from the size but counts their income.
    • FoodShare. After Pub. L. 119-21, main's SNAP statuses (citizen, LPR, Cuban/Haitian entrant) are all within is_citizen_or_legal_immigrant, so the body's FoodShare claim holds.
  • Categorical eligibility.
    • Rule. Every member must receive W-2 cash, FoodShare or SSI (3.3.1 item 2, pp. 45-46; 16.27(5)(c)), and some-but-not-all fails, ineligible noncitizens included (2(d)).
    • FoodShare members. is_snap_excluded_member correctly leaves out the refugee and the undocumented parent.
    • Paid at the limit. Above the limit, these households are paid at 60% of SMI (2.3.1 item 1; plan 1.6).
  • Energy burden.
    • Heat in rent. It is a burden unless the household is subsidized (3.2.5.1 item 2, Table 3.2, pp. 41-43).
    • Subsidized tenants who pay the vendor have a burden (eligible Case 10).
    • Public housing. The is_in_public_housing and receives_housing_assistance arms are both pinned (Cases 7 and 8).
  • Fuels.
    • Kerosene and wood. Kerosene and wood pellets are "wood or other" (4.4.28.3).
    • Coal joins that row by inference, as documented.
    • UNSPECIFIED and NONE pay $0, consistent with the manual's denial when no fuel type is given (G6), with merged AZ/IA/KY, and with Add Vermont Seasonal Fuel Assistance #10436 G1.
  • The federal 110% floor (42 U.S.C. 8624(b)(2)(B)) uses a strict "less than", the 2025 guideline and the program size. It first binds at size 20 (128,565 against 132,165); at size 19 the limit of 126,348 is above the floor of 126,115.

Hand-checked expectations (14, all match):

  1. T/wi_wheap.yaml Case 1 (wages 39,520, size 4). r = 39,520 / 32,150 = 1.22924. Proxy 354 + 180 = 534, cost 1,000 inside 400.50 to 1,068. 1.43743 / 2.94269 × 1,000 = 488.48, so 488.
  2. Case 2 (12,000, size 3). r = 0.45, raised to 0.60. Proxy 297 + 130 = 427, cost 800. 0.702303 × 800 = 561.85, so 561.
  3. Case 3 (36,000, size 1, electric). r = 2.30, held at 2.15. Cost 900 × 0.4 = 360, inside 149.25 to 398. 0.175577 × 360 = 63.21, so 63.
  4. Case 4 (categorical, size 8). r = 101,965 / 54,150 = 1.88301. 0.78366 / 2.94269 × 1,150 = 306.25, so 306 (actual income gives 291.57).
  5. Case 5 (size 2 of 3). r = 30,000 / 21,150 = 1.41844. 1.24823 / 2.94269 × 700 = 296.93, so 296.
  6. Cases 6 and 7 (heat in rent, studio). Cost 0.75 × 184 = 138. r = 2.01278 gives 30.66, so 30. r = 2.03195 gives 29.77, so 0.
  7. Case 8 (TPS, Social Security 14,400). Size 1, r = 0.92013, cost 900 inside 355.50 to 948. 1.74654 / 2.94269 × 900 = 534.17, so 534.
  8. Case 9 (size 2, 44,400). r = 2.09929. 0.56738 / 2.94269 × 900 = 173.53, so 173.
  9. T/payment/wi_wheap_heating_cost.yaml Cases 1-9. 950 (inside 400.50-1,068), 320.25 (0.75 × 427), 1,722 (2 × 861), 138 (0.75 × 184), 480 (1,200 − 720, inside 232.50-620), 1,604 (2 × 802, one room), 3,666 (2 × 1,833, nine rooms), 1,582 (2 × 791, wood row) and 0 (solar).
  10. T/income/wi_wheap_countable_income.yaml.
    • Case 3: 18,000 + 2,400 − 5,000 = 15,400.
    • Case 4: −6,800 floors to 0, plus wages 30,000 = 30,000.
    • Case 5: (21,600 − 2,434.80) + (14,400 − 2,434.80) = 31,130.40.
    • Case 6: 35,000 + 120 = 35,120.
    • Case 8: 36,000 + 28,000 + 3,600 − 4,200 = 63,400.
    • Case 9: 650 + 3,648 = 4,298.
  11. T/eligibility/wi_wheap_income_limit.yaml Cases 1-6. 38,421, 73,888, 101,965 and 128,565 (floor(73,888 × 0.52, 1.00, 1.38, 1.74)); 50,243 at size 2 of 3; 0 at size 0.
  12. T/eligibility/wi_wheap_eligible.yaml Cases 1 and 2. 50,243 ≤ 50,243 is eligible. 50,244 is above the limit and above 1.1 × 21,150 = 23,265, so not eligible.
  13. Eligible Cases 4 and 14. 1.1 × 120,150 = 132,165. 132,164 is eligible, and 132,165 is not.
  14. The largest award. At r = 0.60, 0.702303 × 3,666 = 2,574.66, so 2,574. The maximum.yaml comment is right.

Case count. 7 + 14 + 6 + 10 + 9 + 9 + 1 = 56, as the body says. CI's per-file results match.

Mutation check (beyond S1). Each of these has a case that would fail if it were removed:

  • size > 0 (eligible Case 5);
  • the SMI test, the 110% floor and the categorical arm (Cases 1-4, 14);
  • the fuel gate (Cases 11-12) and both energy-burden arms (Cases 7-10, 13);
  • each categorical route except the W-2 citizenship condition (S1);
  • the ratio floor and cap, the categorical substitution, truncation and the $30 minimum;
  • the baseload, the room floor and cap, the motel rule, the dwelling default and the kerosene and solar rows;
  • the age-18 and high-school exclusions, the $120 threshold, the age-73 rule, the Part B netting, the child support deduction and the zero floor;
  • the Social Security route into household size.

Batch checks:

  • Add New Hampshire state supplementation for 2026 #10156 pattern (item 15). No state-gated int or enum reaches a missing parameter key. wi_wheap_dwelling_type (gated, default SINGLE_FAMILY) indexes only breakdowns that list all five values. wi_wheap_household_size (0 outside Wisconsin) reaches fpg() and liheap_smi_limit only arithmetically.
  • Item 16. military_retirement_income includes SBP annuities.
  • Item 17. See G1.
  • Integrate modeled state unemployment insurance into income flows #9558. The rule ignores unemployment compensation, and the PR reads neither raw unemployment_compensation nor the total in any parameter or formula. So the guard has nothing to flag, and no state UI drops out.
  • Count military SBP annuities in state benefit income lists #10317. The list names military_retirement_income.
  • Count each Social Security benefit once in Pennsylvania CCW countable income #10408 (the components guard). The formula reads social_security and no component, so nothing is counted twice.
  • Tie source-conflict markers to the register shared with Axiom #10287 ratchet. I applied main's CONFLICT_COMMENT_RE to all 871 added lines in the PR's parameter and variable files and found 0 matches. The PR edits no existing scanned file, so no KNOWN_UNMARKED count moves.
  • Wiring.
    • Registry. programs.yaml has an entry with a valid variable and parameter_prefix, and test_programs_registry passed. verified_years: "2026" is backed by the FFY2026 calculator, manual and plan.
    • Aggregates. No merged LIHEAP program is in a household benefit aggregate on main, so not adding one is consistent.
    • Gating. Every new variable, including the input, is gated to Wisconsin.
  • References and descriptions.
    • References. They follow CLAUDE.md: one entry per source, quoted multi-page titles ending #page 26,45,166, single-page anchors with no page in the title, and # PDF pages comments above variable hrefs.
    • Descriptions. All 31 follow the house template.
  • Changelog. changelog.d/wi-liheap-wave1d.added.md is top-level, with type added.
  • Backfill (item 3). Years before 2026 use backfilled values. They are unclaimed and documented (suggestion-level under NJ S3, so not listed separately).
  • Item 12 (IIJA). Not applicable. Wisconsin's funding factor folds in the available funds, and there is no separate supplement.
  • Main's 59 newer commits. None touches a dependency of this PR:

Axiom line assessment

Valid: axiom: TheAxiomFoundation/rulespec-us#1895 queued.

  • State: OPEN, labelled pe-parity. It was created 2026-10-23 22:16 UTC and has not been updated since.
  • It matches the head.
    • It names head 00b8d56388 and merge base 6a92d6a983.
    • It lists the 56 cases by file.
    • Its required outputs (size, limit, countable income, categorical eligibility, eligibility, heating cost, benefit) describe the head's code exactly.
  • It is dispatch-ready in the rulespec-us#1467 format:
    • proposed us-wi/ module paths, with corpus status per source;
    • verbatim text of the statute, the manual, both plan editions and the calculator cells;
    • 14 companion tests derived from the sources, not from PE output;
    • 17 interpretations, which record the categorical, proxy-bound and maximum conflicts (G3) and the PY27 gambling change (G2);
    • its blockers: no reviewed oracle revision, sources missing from the corpus, a different PY26 edition in the corpus, an unchecked signed release and billed-run approval.
  • One wrong premise. Interpretations 3 and 12 say Wisconsin's state SSI supplement and Caretaker Supplement have no variable at this base. They do (G1). Update the issue when G1 and S1 are resolved.

CI status

29 pass, 3 skipping, 0 failed. Run 40032591507 tested head 00b8d56388 (pull_request, started 2026-10-23 22:17 UTC, after #10287 and the 2026-10-21 cutoff). The three skipping entries are the parent workflows of an earlier run, 40032360988, that did not execute. I re-checked gh pr checks 10434 at the end of this review.

  • Wisconsin tests. Baseline states-shard-3 ran all 7 files: 7 + 14 + 6 + 10 + 9 + 9 + 1 = 56 passed.
  • Guards (Rest shard-1: 2,576 passed, 11 skipped):
    • test_unemployment_compensation_readers.py;
    • test_military_retirement_readers.py;
    • test_source_conflicts.py;
    • test_programs_registry.
  • Also passing: Microsimulation, Household API Partners, PR validation, Package and compatibility, Quick Feedback, Codecov (patch and project), and every baseline and contrib shard.
  • Known main-caused failure: not applicable.
  • Locally: I ran no tests or model probes. The G1, G4 and G5 examples are hand traces of the code at the head. The only local scripts read the calculator's cells, extracted the PY27 manual's text and applied the ratchet pattern.

Branch status

  • Position: 2 commits ahead of main (16bfb2d2c7) and 59 behind. The merge base is 6a92d6a983; the head's second commit merged main at that point.
  • Merge: one conflict, in policyengine_us/programs.yaml only (branch note). git merge-tree --write-tree PolicyEngine/main 00b8d56388 reports two hunks:
  • Sibling conflicts (branch note). The other open LIHEAP PRs also edit the coverage line and append entries, so whichever lands first, the rest need a one-line resolution.

0 critical, 1 should and 6 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.

Requesting changes: 0 critical, 1 should (untested income sources and branches). 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 Wisconsin WHEAP regular heating assistance

2 participants