Skip to content

Add Montana LIHEAP regular heating assistance - #10435

Open
hua7450 wants to merge 6 commits into
mainfrom
mt-liheap-wave1d
Open

hua7450 wants to merge 6 commits into
mainfrom
mt-liheap-wave1d

Conversation

@hua7450

@hua7450 hua7450 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Montana's LIHEAP regular heating benefit for FFY2026 and FFY2027, administered by the Department of Public Health and Human Services (DPHHS). The model counts citizens and qualified aliens in household size (ineligible members' income still counts), tests the gross income of every member against the greater of 60% of state median income and 150% of the poverty guideline, applies the resource test, and recognizes SNAP, SSI and TANF categorical eligibility. The benefit is the base benefit for the dwelling type, bedrooms and primary fuel times an income and heating-degree-day multiplier for the household's district and percent of poverty, rounded to the nearest dollar. Subsidized households with heat in their rent receive the modified benefit (the greater of 5% of the regular benefit and $25); other heat-in-rent households are capped at half of their rent over the seven-month season; non-traditional dwellings heated with an allowable fuel receive a flat $150. Emergency assistance (Contingency Revolving Fund) and weatherization are out of scope.

Fixes #10425

Regulatory authority

Eligibility

Requirement Source How modeled
Montana residence ARM 37.70.401 defined_for = StateCode.MT
Eligible members ARM 37.70.402(8) ((7) from May 23, 2026) mt_liheap_household_size sums is_citizen_or_legal_immigrant; size 0 is ineligible
Income at or below the published limit ARM 37.70.406; Manual Appendix A max(60% SMI via liheap_smi_limit, 150% of the prior-year FPG) reproduces all 15 FFY2026 and 15 FFY2027 published limits. ARM 37.70.406(1) words sizes 9+ as 150% of poverty; the published tables print 60% SMI at size 9 ($91,432 FFY2026, $97,942 FFY2027) and are followed (us-mt:liheap-income-limit-size-nine)
Resource test ARM 37.70.408(1)-(2); Manual Appendix A Cash, stocks and bonds (spm_unit_cash_assets) plus non-home real estate equity against the size table (FFY2026 $14,358-$29,290; FFY2027 $14,717-$31,524); business equity (household_business_assets_equity) at most $25,000 per ARM 37.70.408(2) (the manuals print $29,290 and $31,524; us-mt:liheap-business-equity-limit)
Categorical eligibility ARM 37.70.402(2)-(3); Manual 3.10-3.11 SNAP first: SNAP received and every member in the SNAP unit (is_snap_excluded_member); then SSI or TANF: every member an SSI recipient (ssi or receives_ssi), a TANF assistance-unit member, or a spouse of an SSI recipient or a head or spouse of the own tax unit of an SSI child under 18 (ARM 37.70.401(4)) (the people whose income SSA considers). Waives the income and resource tests
Obligation to pay for heat ARM 37.70.402(1) has_heating_expense or heat included in rent; heating_type UNSPECIFIED or NONE pays $0

Countable income

Item Source How modeled
Members counted ARM 37.70.401(1), 37.70.402(8) ((7) from May 23, 2026) Every member's nonexcluded income, including minors and ineligible members. ARM 37.70.401(1) counts income "received by the members of the household", and 37.70.407(1) excludes no minor's wages or benefits; manual 3.12 and 3.22 count only members 18 and older. The rule governs (us-mt:liheap-minor-member-income)
Earned income ARM 37.70.401(1); Manual 3.15 Wages, net self-employment (including SSTB), farm operations and partnership income, each floored at zero
Unearned income ARM 37.70.401(1); Manual 3.15 Unemployment, strike benefits, SSI, the Montana state supplement (mt_ssp), pensions, retirement withdrawals, military retirement, railroad, veterans, workers' compensation, disability, alimony, child support, interest, ordinary dividends, rental and farm rent, general assistance, capital gains, TANF
Social Security ARM 37.70.407(1)(d); Manual 3.15 exclusions Net of the Medicare Part A and Part B premiums (medicare_part_a_premium, medicare_part_b_premium) up to the benefit, for every household. The dependents and survivors benefits (social_security_dependents, social_security_survivors) of a member aged 18 to 22 who is a full-time student (is_full_time_student) are excluded; retirement and own-record disability benefits count, as do a minor's benefits
Gifts ARM 37.70.401(1), 37.70.407(1)(w) Through May 22, 2026 (FFY2026), financial_assistance counts in full once a member's gifts reach $50 a month (the manual describes a $500 recurring household test; the rule is followed, us-mt:liheap-gift-threshold). The amended rule effective May 23, 2026 excludes monetary gifts (a resource until spent) and 401(1) no longer lists them, so FFY2027 counts none: gift_minimum is $50 from 2025-10-01 and infinite from 2026-05-23
Health insurance premiums ARM 37.70.407(1)(bb); Manual 3.15 Every member's out-of-pocket and payroll premiums, plus Medicare premiums not paid out of Social Security (a Part B premium withheld from a railroad annuity, a Part A premium paid from a pension), deducted except for categorically eligible households. Manual 3.15 withholds the deduction from them; ARM 37.70.407(1)(bb) states no categorical exception, and the rules do not say which income sets a categorical household's multiplier, so the manual fills that gap (us-mt:liheap-categorical-premium-deduction). The exception changes results only for SNAP-categorical households, because SSI and TANF households use the standards
Not counted ARM 37.70.407; Manual 3.15 SNAP, educational grants and loans, EITC, non-recurring lump sums including lottery winnings, in-kind income, combat pay

Benefit

  • Base benefit (Manual Appendix A 9.1; same matrix both years): single family, multi-family and mobile home by bedrooms 1-4 (0 counts as one, more than four as four) and fuel (natural gas, electricity, propane, fuel oil, wood, coal); kerosene uses the fuel-oil column. Non-traditional dwellings: $150 for each allowable fuel, with no multiplier (Manual 3.36; ARM 37.70.601), stored once as payment.non_traditional_benefit; the fuel matrices hold no non-traditional row. Manual 8.1 Fuel Types limits heating assistance to propane, wood, coal, fuel oil, natural gas and electricity and excludes solar, so SOLAR and OTHER pay $0 in every dwelling type. A final fuel-column check also withholds from them the non-traditional $150 and the $25 minimum for subsidized heat-in-rent households. The model reads 3.36's "regardless of heat type" as any allowable fuel.
  • Multiplier (Manual Appendix A 9.2): ten sub-grantee districts by county x nine percent-of-poverty bands (0-25, 26-50, 51-74, 75-100, 101-125, 126-150, 151-175, 176-200, 201+), a new table for FFY2027. Percent of poverty = benefit income / prior-year poverty guideline for the eligible-member size, rounded to a whole percent (the manual's examples round 70.9% to 71%). A categorically eligible household above its limit takes the lowest multiplier (Manual 3.11; Plan 1.6).
  • Benefit income: countable income, except that SSI or TANF categorical households use only the current standard amounts (the federal SSI rate in effect at the start of the season, the couple rate for a married pair on SSI, and the Montana TANF payment standard for the assistance unit), as in the manual's Appendix A examples. The annual SSI standard is 12 times the monthly rate; Appendix A 9.5 prints three of its four annual figures $1 to $5 higher.
  • Regular benefit = base x multiplier, rounded to the nearest dollar ($1,503 x 0.83 = $1,247 in the manual's example).
  • Subsidized (Section 8 or public housing) heat-in-rent households: max(5% x regular, $25) (ARM 37.70.402(7), numbered (6) from May 23, 2026; 37.70.601(3)). Other heat-in-rent households: min(regular, 50% x 7 months of rent charged, pre_subsidy_rent) (ARM 37.70.607(3)).
  • New input mt_liheap_dwelling_type (Single family, Multi-family, Mobile home, Non-traditional dwelling; default Single family).

Not modeled

Item Source Treatment
Proration for movers and new residents ARM 37.70.602 Full-season benefit
Monthly heat-in-rent receipts ARM 37.70.607(3) Annual rent approximates steady monthly rent and receipts for all seven months
Five-year modified-benefit eligibility; subsidized households paying only a base-load electric bill without heat in rent ARM 37.70.402(7) ((6) from May 23, 2026) No inputs
Wood and coal reimbursement mechanics; reimbursement capped at paid costs when the account is not in a member's name; credit balances ARM 37.70.607 Not represented
Prior-month income annualized (from May 23, 2026, calculated through CHIMES with 12 months of self-employment receipts or the prior-year tax return); self-employment depreciation add-back and the 60% gross-receipts option ARM 37.70.401(1), (12); Manual 3.16 Annual net inputs stand in
Student OASDI exclusion when only a total social_security input is given ARM 37.70.407(1)(d) The exclusion reads the dependents and survivors inputs; a total social_security input without the per-type breakdown counts in full
Indian per capita, lease and trust income above $2,000; plasma sales; contract-for-deed payments Manual 3.15 No existing inputs
Shelters and excluded group homes; students claimed by an outside taxpayer (FFY2026 only; repealed May 23, 2026); tribal LIHEAP service population; SSN refusal ARM 37.70.402(4)-(6), (9) before May 23, 2026; (4)-(5), (8) after Not represented
Emergency assistance (CRF), weatherization Manual ch. 4-5 Out of scope

Source choices

Each choice is a source_conflicts/candidates.yaml entry whose code: field lists the files that carry it.

Candidate Sources Model follows
us-mt:liheap-minor-member-income ARM 37.70.401(1) counts every member's income; manual 3.12, 3.15 and 3.22 leave out members under 18, and plan item 1.9 leaves out a child's earned income The rule
us-mt:liheap-business-equity-limit ARM 37.70.408(2): $25,000; manuals: $29,290 (2025-2026) and $31,524 (2026-2027) The rule
us-mt:liheap-gift-threshold ARM 37.70.407(1)(w): $50 a month per member, and from May 23, 2026 no monetary gifts; manual 3.15: recurring gifts of $500 or more per household The rule in force
us-mt:liheap-income-limit-size-nine ARM 37.70.406(1): 150% of poverty at size 9; the incorporated table prints the 60% SMI cell The published table
us-mt:liheap-incorporated-table-editions The rules incorporate the 2022-2023 tables until May 22, 2026 and the 2025-2026 tables from May 23, 2026; no rule names the 2026-2027 tables The 2025-2026 tables for FFY2026 and the 2026-2027 manual tables for FFY2027
us-mt:liheap-categorical-premium-deduction ARM 37.70.407(1)(bb): out-of-pocket health insurance premiums reduce income, with no categorical exception; manual 3.15: premiums are not deducted for categorically eligible households The manual, as a gap-fill: the rules do not define the income that sets a categorical household's multiplier. A SNAP-categorical retiree with a $16,000 railroad annuity and a $2,434.80 Part B premium in a two-bedroom gas-heated house in Yellowstone County receives $533 in FFY2026, or $602 with the premium deducted

Earlier years are unverified estimates through parameter backfilling.

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

Test plan

62 YAML cases in 8 files: mt_liheap.yaml 15, mt_liheap_household_size.yaml 2, eligibility/mt_liheap_categorical_eligibility.yaml 11, eligibility/mt_liheap_eligible.yaml 6, eligibility/mt_liheap_income_limit.yaml 2, income/mt_liheap_countable_income.yaml 13, payment/mt_liheap_benefit_income.yaml 6 and payment/mt_liheap_multiplier.yaml 7. The 54 cases before review round 1 passed locally on 2026-10-23. Round 1 added 8 cases, re-pinned 2 and removed an 18-year-old's wages case, which now falls in the same class as a minor's wages. CI passed on those 61 cases at the round-1 head f0f876a9c1. Review round 2 added mt_liheap.yaml Case 15, which pins the fuel-column check; its values are hand-derived and were not run locally, and CI on the new head is pending.

  • published income limits at sizes 1, 8, 9 and 10 for both years;

  • eligibility boundaries for income, non-business resources and business equity; categorical households over both limits; heating obligation and fuel;

  • categorical status for SNAP (all members and with a SNAP-excluded member), single SSI, married couple with one recipient, child on SSI, sibling not on SSI, a separately filing adult sibling, TANF unit, reported SSI receipt, the working parent of an 18-year-old SSI recipient (not categorical), and a dependent grandmother outside the TANF unit (not categorical);

  • countable income: a 17-year-old's wages counted, an ineligible member's income, every listed earned and unearned source including mt_ssp, Social Security net of Part B, Part B above Social Security not deducted for a SNAP categorical household, a 23-year-old full-time student's survivors benefits counted, the $50 gift threshold in FFY2026 and a $600-a-month gift excluded in FFY2027, premiums deducted and not deducted, capital gains, TANF, a self-employment loss;

  • benefit income standards for SSI individuals, couples, unmarried pairs, TANF units and mixed households;

  • multipliers at band edges by whole-percent rounding, two districts, FFY2026 versus FFY2027, over-limit categorical, a categorical household exactly at its limit (keeps its band), unknown county;

  • the manual's $1,247 example, bedroom end rows, every dwelling type, fuel columns, the modified benefit and its $25 floor, the heat-in-rent rent cap, a Missoula County family in FFY2027, a Yellowstone County household with a separately filing adult sibling (ineligible; the SSI child's income counts), a Yellowstone County parent with $36,000 of wages and an 18-year-old full-time student with $9,600 of survivors benefits ($394; counting the benefit would make the household ineligible), a railroad retiree whose Part B premium is deducted ($247), a public-pension retiree who pays Part A and Part B premiums ($317), a parent with a 10-year-old's $9,600 of survivors benefits ($463), and two minimum-wage workers in Yellowstone County: one in a solar-heated yurt (non-traditional) and one in a HUD-assisted apartment whose rent includes heat from another fuel ($0 each; without the fuel-column check, $150 and $25).

  • YAML tests with expected values from the manuals, rules and plan

  • ruff format

  • policyengine-core test on the program folder before review round 1 (54 passed); registry and code-health checks passed, 2026-10-23

  • Review round 1: ruff format, ruff check, YAML parse and candidates checks; the new cases were not run locally

  • Review round 2: YAML parse and candidates checks (code paths exist and are tracked, ids sorted); Case 15 was not run locally

  • Changelog fragment in changelog.d/

  • CI passes

axiom: TheAxiomFoundation/rulespec-us#1896 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
@DTrim99

DTrim99 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Montana LIHEAP regular heating assistance

PR #10435 (author: hua7450), head 3c2c8df050. This is a first review. The PR adds Montana's LIHEAP regular heating benefit, run by the Department of Public Health and Human Services (DPHHS), for FFY2026 and FFY2027 (model years 2026 and 2027). It includes:

  • 44 parameter files and 10 variables, one of them a new input (mt_liheap_dwelling_type);
  • 8 test files (54 cases);
  • a programs.yaml entry (coverage line and state block);
  • a changelog fragment.

The PR has no reviews and no comments. It touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/mt/dphhs/liheap/
  • V/ = policyengine_us/variables/gov/states/mt/dphhs/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/liheap/

Sources read for this review:

  • Rules. Admin. R. Mont. 37.70.401, .402, .406, .407, .408, .601 and .607, both the versions in force before May 23, 2026 and the versions amended by MAR Notice No. 2026-532 (the rules.mt.gov PDFs the PR cites).
  • 2025-2026 manual. The LIHEAP Policy Manual 2025-2026 (Wayback capture), pages 24-30, 35-44, 57-59, 68, 71, 74 and 122-125.
  • 2026-2027 manual. The LIHEAP Policy Manual 2026-2027 (October 2026), pages 22-44, 57-59, 74 and 124-131.
  • Plan. The FY2026 Detailed Model Plan, pages 5-12. The text extraction drops the checkbox states, so for item 1.9 I rely on the PR's axiom issue's reading.
  • Federal and other. 42 U.S.C. 8624(b)(2)(B); the HHS guideline and SMI parameters on main; and the RRB's Part B release.

Summary

What is right. The tables and mechanics are encoded accurately:

  • Matrices. All 72 base-benefit cells (3 dwelling types × 4 bedroom rows × 6 fuels) match both manuals, which print the same matrix. The $150 non-traditional row is right.
  • Multipliers. All 180 multiplier cells match the printed tables (10 districts × 9 bands × 2 years). Band starts are at 0, 26, 51, 75, 101, 126, 151, 176 and 201, and whole-percent rounding follows the manuals' examples. The district-to-county lists match the DPHHS offices page.
  • Limits. max(floor(floor(0.6 × SMI) × size factor), 1.5 × prior-year guideline) reproduces all 30 published limits (sizes 1-15 in both years) to the dollar. "At or below" is <=.
  • Resources. Both resource tables match. The $25,000 business-equity limit follows ARM 37.70.408(2) and plan item 2.3 over the manuals' figures.
  • Federal floor. The limit is never below 150% of the guideline, so the 110% floor holds at every size.
  • Other mechanics. These follow the rules and manuals:
    • categorical eligibility (SNAP first, then SSI/TANF with the people whose income SSA or TANF considers);
    • the SSI/TANF standards for benefit income;
    • the lowest multiplier for over-limit categorical households;
    • the modified benefit for subsidized heat-in-rent tenants and the half-of-rent cap for other heat-in-rent tenants;
    • the student OASDI exclusion;
    • the gift rule as it applied in each year.
  • Tests and CI. I hand-checked 16 expectations below, and CI is green.
  • Guards. Integrate modeled state unemployment insurance into income flows #9558, Count military SBP annuities in state benefit income lists #10317 and the Tie source-conflict markers to the register shared with Axiom #10287 ratchet pass on the merged tree, and the Count each Social Security benefit once in Pennsylvania CCW countable income #10408 pattern does not apply. The NH Add New Hampshire state supplementation for 2026 #10156 key pattern is safe: a two-state probe (Montana plus New Hampshire) ran cleanly.

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

  • S1. Medicare premiums. Only the Part B premium is netted, and only from Social Security. ARM 37.70.407(1)(bb) deducts any out-of-pocket health insurance premium. A railroad retiree whose Part B comes out of the RRB annuity gets $0 instead of $247. A Part A buyer gets $247 instead of $317.
  • S2. Members under 18. Their income is excluded, following the manual. ARM 37.70.401(1) counts "all nonexcluded income ... received by the members of the household", and no exclusion covers minors. The PR follows the rule over the manual for gifts and business equity, but here it follows the manual without recording the conflict. A child's $9,600 survivors benefit moves a household from $463 to $602.
  • S3. Untested entries. 4 of the 5 earned and 16 of the 18 unearned entries never take a positive value in any test. The student age cap, the Part B floor and the TANF-unit condition are also unpinned.
  • S4. Non-traditional dwellings. They get $0 when heated with SOLAR or OTHER, although manual 3.36 pays them $150 "regardless of heat type".

There are also four suggestions:

  • G1: mt_ssp (item 17).
  • G2: record the source choices.
  • G3: add a Wayback capture for the 2026-2027 manual anchors.
  • G4: small documentation points.

Status.

  • CI: 27 pass and 3 skipping.
  • Branch: 2 commits ahead of main and 61 behind. The only conflict is the programs.yaml coverage line (branch note).

Critical (must fix)

None.

Should address

S1. Medicare premiums are netted only as Part B, and only from Social Security; ARM 37.70.407(1)(bb) deducts any out-of-pocket health insurance premium

  • Where:
    • V/income/mt_liheap_countable_income.py:52-53 computes max_(social_security - medicare_part_b_premium, 0) per person.
    • railroad_benefits (P/income/sources/unearned.yaml:17) is summed gross.
    • P/income/deductions.yaml lists health_insurance_premiums_without_medicare_part_b and pre_tax_health_insurance_premiums. Neither holds a Medicare Part B premium paid from an RRB annuity or a Part A premium, so those premiums are never deducted.
  • Sources:
    • ARM 37.70.407(1)(bb) (unchanged on May 23, 2026, page 3) excludes or deducts "proof of out-of-pocket health insurance premiums paid by a member of the household for a household member or members". It is not limited to Social Security or to Part B.
    • Manual 3.15 (both editions, p. 39): "Insurance premiums paid directly by the client ... do not count as income. ... Similarly, Medicare premiums paid out of a Social Security benefit will not be counted as income." "Medicare premiums" is general, so it covers Part A.
    • RRB withholding. The RRB release says the Part B premium is "taken out of your monthly Social Security or Railroad Retirement Board (RRB) benefit automatically". For a railroad annuitant, the premium comes out of the RRB check, which the head counts gross.
  • Effect (FFY2026; a single person aged 70 in Yellowstone County; single-family home, 2 bedrooms, natural gas):
    • (a) Railroad retiree: railroad_benefits $34,000 and medicare_part_b_premium $2,434.80 (the 2026 standard premium the PR's tests use).
      • Head (confirmed with a probe at the head): countable income $34,000 > $33,719, so not eligible: $0.
      • Rule: 34,000 − 2,434.80 = $31,565.20 is eligible. 31,565.20 / 15,650 = 201.7%, rounded to 202%, gives District 7's >201% factor of 0.32. $772 × 0.32 = $247.04: $247.
    • (b) Part A buyer: Social Security $36,000, Part B $2,434.80, and a reduced Part A premium of $3,732 ($311 a month on main, medicare_part_a_premium).
      • Head: $33,565.20 is 214.5% of the guideline, factor 0.32: $247.
      • Rule: $29,833.20 is 190.6%, rounded to 191%, so 176-200%, factor 0.41. $772 × 0.41 = $316.52: $317.
  • Severity: should, as in Add Tennessee LIHEAP regular energy assistance #10430 S2 and the wave's Medicare precedents (VA Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303, AZ Add Arizona LIHEAP Standard benefit #10293, IA Add Iowa LIHEAP regular heating assistance #10292). The head nets part of what the state's rule deducts.
  • Fix:
    • Per person, net medicare_part_a_premium + medicare_part_b_premium from the sum of social_security and railroad_benefits (floored at 0), and remove railroad_benefits from the plain list.
    • Alternatively, since (bb) covers all out-of-pocket premiums, deduct the Medicare premiums with the other premiums. That needs care because the manual (p. 40) withholds the premium deduction from categorical households but nets Medicare from Social Security for everyone.
    • Add cases (a) and (b), and update the body and Axiom interpretation 11.

S2. Income of members under 18 is excluded, following the manual, although ARM 37.70.401(1) counts all nonexcluded income of household members

  • Where:
    • V/income/mt_liheap_countable_income.py:31 and :60 zero all income of members under adult_age (18, from P/income/adult_age.yaml, which cites only the manuals).
    • T/income/mt_liheap_countable_income.yaml Case 1 pins a 17-year-old's $4,000 of wages as excluded.
    • T/mt_liheap.yaml Case 10 excludes a 10-year-old's SSI in a non-categorical household.
  • Sources:
    • The rule. ARM 37.70.401(1), both versions (amended, page 1), defines annual gross income as "all nonexcluded income including ... social security retirement and disability payments, supplemental security income payments ... received by the members of the household".
      • A "child" (401(4)) is a household member (401(14)).
      • ARM 37.70.407(1)'s 28 exclusions include none for minors. The only age-based one, (d), excludes OASDI for 18- to 22-year-old full-time students.
    • The manual. Manual 3.12 (p. 30): "Only income from people in the household 18 years old or older will be counted". 3.15 and 3.22 say the same.
    • The plan. Plan item 1.9, as the PR's axiom issue reads it, leaves "Earned income of a child under the age of 18" unchecked, but it checks SSA benefits and SSI with no age limit.
  • Why this is a conflict, and the PR is inconsistent about it.
    • CLAUDE.md ("Conflicting sources") binds the code to the rule over a manual, form or stated practice. The PR applies that principle to gifts (P/income/gift_minimum.yaml: "The model applies the administrative rule") and to business equity (P/eligibility/resources/business_equity_limit.yaml).
    • Here it follows the manual. The rule's text is plain, and neither the body nor the code records the conflict. The body cites only "Manual 3.12, 3.15".
  • Effect (FFY2026; Yellowstone County; a parent aged 40 with wages of $20,000 and a child aged 10 with social_security_survivors of $9,600; single-family home, 2 bedrooms, natural gas):
    • Head: $20,000 / $21,150 = 94.6%, rounded to 95%, gives District 7's 75-100% factor of 0.78. $772 × 0.78 = $602.16: $602.
    • Rule: $29,600 / $21,150 = 139.95%, rounded to 140%, gives the 126-150% factor of 0.60. $772 × 0.60 = $463.20: $463.
    • Other directions. A working 17-year-old's wages can also move a household over its limit.
  • Severity: should. Authoritative sources conflict (the rule against the manual and, for earnings, the plan), and the choice is unrecorded. A rule-backed source is omitted with a material effect, as in Maine Add Maine HEAP Fuel Assistance #10437 S1.
  • Fix (recommended):
    • Count the nonexcluded income of all members, as ARM 37.70.401(1) requires and as the PR already does for gifts and business equity. The 407 exclusions, including the student OASDI exclusion, still apply.
    • Mark the deciding line # Source conflict us-mt:<id> with a register entry.
    • The categorical test at V/eligibility/mt_liheap_categorical_eligibility.py:59 reuses p.adult_age for SSA's "minor child" rule (20 CFR 416.1160(d)). Give it its own parameter so this fix does not change it.
    • If the team keeps the manual's practice, take it to the project lead as CLAUDE.md requires, and record the conflict either way.
    • Re-pin countable Case 1 and mt_liheap Case 10, and update the body, the axiom issue's scope line and interpretation 9.

S3. 20 of the 23 income-source entries and three branches have no test that would fail if they were dropped

  • Where: P/income/sources/earned.yaml, P/income/sources/unearned.yaml, V/income/mt_liheap_countable_income.py:42 and :52-53, and V/eligibility/mt_liheap_categorical_eligibility.py.
  • Untested entries. No case gives these a positive value:
    • Earned (4 of 5):
      • sstb_self_employment_income, farm_operations_income and partnership_s_corp_income never appear.
      • self_employment_income appears only as −$5,000 (countable Case 8), which floors to 0, the same result as dropping it.
    • Unearned (16 of 18):
      • total_unemployment_compensation, strike_benefits, pension_income, retirement_distributions, military_retirement_income and railroad_benefits;
      • veterans_benefits, workers_compensation, disability_benefits, alimony_income and child_support_received;
      • interest_income, ordinary_dividend_income, rental_income, farm_rent_income and general_assistance.
  • Tested:
    • Sources: employment_income; ssi (mt_liheap Case 7: $264 + $8,352 = $8,616); and capital_gains (countable Case 6).
    • Other reads: Social Security, Part B, gifts in both years, both premium inputs and tanf, outside the lists.
    • Logic: the per-source loss floor (Case 8).
  • Unpinned branches:
    • Student age cap. age < p.student_age_limit (23): the only student case is aged 18, so removing the cap fails nothing.
    • Part B floor. No case has a Part B premium larger than Social Security, so removing max_(…, 0) at lines 52-53 fails nothing. That floor is what stops an excess premium from offsetting wages.
    • TANF-unit condition. Every TANF case has only assistance-unit members (or an SSI recipient). No case has a TANF unit living with an adult outside it, so dropping & in_tanf_unit fails nothing.
  • Severity: should, as in Add Nevada Energy Assistance Program Fixed Annual Credit #10432 S1, Add Wisconsin WHEAP regular heating benefit #10434 S1 and Add Tennessee LIHEAP regular energy assistance #10430 S3 (the mutation rule).
  • Fix:
    • Add one countable-income case that gives every listed source a distinct positive amount. Enter unemployment_compensation and military_retirement_pay_survivors so the two totals are exercised.
    • Add a 23-year-old full-time student, a Part B premium above Social Security, and a TANF unit with a non-unit adult (expected NONE).
    • Add the new cases to the rulespec-us#1896 companion tests.

S4. Non-traditional dwellings heated with SOLAR or OTHER get $0, although manual 3.36 pays them $150 regardless of heat type

  • Where:
    • V/mt_liheap.py:100 returns where(np.any(fuel_columns, axis=0), amount, 0), so the $150 non-traditional amount (lines 64-68) is lost whenever the fuel is outside the six matrix columns.
    • The comment at line 32 and the body ("$150 for every matrix fuel") describe the limit but cite no source for it.
  • Sources:
    • Manual 3.36 (2026-2027 p. 74; 2025-2026 p. 74, the same words): "Eligible LIHEAP applicants living in non-traditional dwellings will receive a flat rate benefit of $150 regardless of heat type or electrical connection*. The benefit will not need to be calculated." Its scenarios make "connected to applicants' own equipment such as solar panel or generator" eligible.
    • The rule. ARM 37.70.601(1) incorporates the matrix (six fuel columns, each $150 for non-traditional dwellings) and is silent on other fuels. CLAUDE.md lets the manual fill that gap.
  • Effect (FFY2026; Yellowstone County; a camper (NON_TRADITIONAL) with heating_type OTHER and other_heating_fuel_expense of $600, or SOLAR with an electric bill; any eligible income):
    • Head: eligible (a reported fuel and a bill), but $0.
    • Manual: $150.
    • UNSPECIFIED fuel. The same applies to a non-traditional household with an unspecified fuel. The wave has accepted UNSPECIFIED at $0 where the amount needs the fuel, but this amount does not (as in Add Tennessee LIHEAP regular energy assistance #10430 S1(c)). This finding rests on SOLAR and OTHER.
  • Severity: should, as in Add Tennessee LIHEAP regular energy assistance #10430 S1, Add New Mexico LIHEAP regular heating assistance #10431 S1 and Add Maine HEAP Fuel Assistance #10437 S3 (households the state serves get nothing). The axiom issue calls this choice "settled", but the PR gives no source for departing from 3.36.
  • Fix:
    • Pay p.non_traditional_benefit to eligible non-traditional households before the fuel-column gate. For example, gate on np.any(fuel_columns, axis=0) | (dwelling == dwellings.NON_TRADITIONAL).
    • Add a non-traditional SOLAR or OTHER case ($150). mt_liheap Case 4's single-family SOLAR $0 stays.
    • Update the body and interpretation 12.

Suggestions

G1. State SSP (item 17).

  • What is missing. mt_ssp is on main (and was at this PR's base), and the SNAP and HUD unearned lists include it. The PR's unearned list has ssi only, and Montana counts SSI.
  • Effect.
    • For SSI/TANF categorical households, benefit income uses the SSI standard, so nothing changes.
    • In a non-categorical unit, a supplement recipient's $94 a month (assisted living, group or community home), $52.75 (adult foster care) or $26 (transitional living) drops out of countable income, up to $1,128 a year. An example is a foster adult the family chooses to include (manual 3.5).
  • Why it is only a suggestion. The head is correct against its base and there is no guard (Add Maine HEAP Fuel Assistance #10437 G3, Add New Mexico LIHEAP regular heating assistance #10431 G1, Add Wisconsin WHEAP regular heating benefit #10434 G1).
  • Fix: add mt_ssp to P/income/sources/unearned.yaml.

G2. Record the source choices. None of these comments matches the #10287 ratchet's pattern, so the guard does not prompt for an entry. CLAUDE.md still asks for a register entry and a # Source conflict us-mt:<id> marker for each:

  • (a) Size 9.
    • ARM 37.70.406(1), both versions, says nine or more members are eligible "only if" income is at or below 150% of the guidelines. That is $89,475 in FFY2026 and $92,100 in FFY2027.
    • The head uses the printed 60% SMI cells, $91,432 and $97,942. In fairness, 406(2) incorporates a table that prints $91,432, and the 2026-2027 footnote moves the guideline rule to "ten (10) and greater".
  • (b) Stale incorporations.
    • The rules in force until May 22, 2026 pin "150 percent of the 2022 ... poverty guidelines" (406(1)) and incorporate the 2022-2023 income, resource and matrix tables (406(2), 408(4), 601(1)). The head uses the 2025-2026 tables. The amended 601(1) supports this: "The 2025-2026 benefit matrix will be used for the 2025-2026 heating season."
    • For FFY2027, the amended rules still incorporate the 2025-2026 tables. The head uses the October 2026 manual's tables, a gap-fill I agree with.
  • (c) Gifts. The rule's $50 rule (FFY2026) and its exclusion (FFY2027) are applied over the manual's $500 recurring test.
  • (d) Business equity. $25,000 is applied over the manual's $29,290 and $31,524.
  • (e) S2's choice, once it is resolved.
  • Precedent: Add Tennessee LIHEAP regular energy assistance #10430 G2, Add Nevada Energy Assistance Program Fixed Annual Credit #10432 G1 and Add Maine HEAP Fuel Assistance #10437 G4.

G3. Revision drift.

  • The risk. The 2026-2027 manual is cited at the live dphhs.mt.gov/assets/hcsd/liheap/LIHEAPmanual.pdf URL. That URL already rotated once, from the 2025-2026 edition the PR cites by Wayback, and the axiom issue notes the corpus manifest's source_url now serves the new edition. All 2026-2027 anchors will drift when the 2027-2028 manual replaces it.
  • Fix: add a Wayback capture beside each live-URL reference, as the 2025-2026 references already do. Precedent: Add Maine HEAP Fuel Assistance #10437 G5.

G4. Small documentation points.

  • (a) The 201% gap. The printed bands run "176-200%" then ">201%", leaving exactly 201% unassigned. The 201 threshold puts it in the top band (multiplier Case 1 pins 200.6% to 0.32), which I agree with. Say so in the multiplier comments.
  • (b) Appendix annual amounts. Appendix A 9.5 prints annual SSI standards of $11,605 and $17,405 (2025) and $11,929 (2026), which differ by $1-5 from 12 × the monthly rate. The head's 12 × monthly is right (any difference is absorbed by whole-percent rounding), but a comment would forestall the question.

Verified correct

Hand-checked expectations (16, all match the head):

Test Hand computation Expected
mt_liheap Case 1 size 2 (ineligible member excluded), income 15,000; 15,000 / 21,150 = 70.9%, 71%; District 7 FY26 51-74%: 0.83; 1,503 × 0.83 = 1,247.49 1,247
mt_liheap Case 2 0%: 0.92; 531 × 0.92 = 488.52; 1,447 × 0.92 = 1,331.24 489 / 1,331
mt_liheap Case 3 872 × 0.92 = 802.24; 968 × 0.92 = 890.56; non-traditional flat 802 / 891 / 150
mt_liheap Case 4 coal 671 × 0.92 = 617.32; fuel oil and kerosene 1,794 × 0.92 = 1,650.48; solar no column 617 / 1,650 / 1,650 / 0
mt_liheap Case 5 802 × 5% = 40.10; 378 × 0.92 = 347.76, rounded to 348; 5% = 17.40, so the $25 minimum 40.10 / 25
mt_liheap Case 6 District 12 FY26 0-25%: 1.06; 1,503 × 1.06 = 1,593.18; cap 0.5 × 7 × 5,400 / 12 = 1,575; with 10,800, cap 3,150 1,575 / 1,593
mt_liheap Case 7 (2027) 994 × 12 = 11,928; 11,928 / 15,960 = 74.7%, 75%; District 9 FY27 75-100%: 0.88; 841 × 0.88 = 740.08 740
mt_liheap Case 8 (2027) 42,000 − 2,400 = 39,600 ≤ 69,463; 39,600 / 33,000 = 120%; District 11 FY27 101-125%: 0.71; 1,052 × 0.71 = 746.92 747
mt_liheap Case 11 36,000 (student OASDI excluded) ≤ 44,095; 170.2%, 170%; 151-175%: 0.51; 772 × 0.51 = 393.72 394
income limit Cases 1-2 floor(floor(0.6 × 108,077) × 0.52) = 33,719; ×1.38 = 89,487; ×1.41 = 91,432 (above 150% × 59,650 = 89,475); 1.5 × 65,150 = 97,725. FY27: 69,463 × 0.52 = 36,120.76, so 36,120; 95,858; 97,942; 1.5 × 67,080 = 100,620 as expected
eligibility Case 2 8,000 + 4,000 + 2,358 = 14,358 ≤ 14,358; then 14,359 true / false
countable Case 2 18,000 − 2,434.80 15,565.20
multiplier Case 1 /15,650: 25.4%, 25% (0.92); 25.6%, 26% (0.87); 200.6%, 201% (0.32) 0.92 / 0.92 / 0.87 / 0.32
multiplier Cases 3-4 9,300 / 15,650 = 59.4%, District 11 FY26 0.85; 9,300 / 15,960 = 58.3%, FY27 0.81 0.85 / 0.81
multiplier Case 5 94,762.50 / 54,150 = 175%: 0.51; SNAP categorical over 89,487: lowest, 0.32 0.32 / 0.51
benefit income Case 5 (2027) (575 + 994) × 12 18,828

Batch checks:

  • 1. Plan and manual first.
    • Cells. All 252 matrix and multiplier cells, all 30 income limits and both resource tables were checked against the printed tables, as above.
    • Timing. Parameters dated October 1 with model year = FFY match the rules' heating season.
    • Lags. The prior-year guideline and the season-start SSI rates are right (manual 3.20's $21,640 example and 9.5).
  • 2. Income.
    • Gross basis. Income is gross (plan 1.8; ARM 37.70.401(1)), and capital gains are counted as the rule names them.
    • Losses. They are floored per source and per person.
    • Period. Annual amounts stand in for the annualized month, as documented.
    • Medicare and minors. See S1 and S2.
  • 3. Backdating. Only 2026-2027 are claimed (registry verified_years: "2026-2027", the variable documentation and the body). The FFY2027 values come from the published October 2026 manual, so the claim is backed (contrast Add Vermont Seasonal Fuel Assistance #10436 S3).
  • 4. Arrangements.
    • Housing. Heat in rent counts as an obligation (402(1)). Subsidized heat-in-rent tenants get the modified benefit (402(7)/(6); 601(3)), and the half-of-rent cap with steady rent equals 607(3)(a)'s monthly test.
    • Categorical eligibility. It waives the income and resource tests (402(2)-(3)).
    • Fuel. NONE is excluded, which is consistent with 402(1) for a heating-only program. UNSPECIFIED pays $0 where the fuel is needed (the accepted pattern).
  • 5. Tests. Periods are plain. The edges are covered: the exact income, resource and business-equity limits; band edges on both sides; and sizes 0, 1, 8, 9 and 10 of the limit table in both years.
  • 6. Descriptions. All 44 follow the house template.
  • 7. References. Every anchor I opened shows its value:
    • 2025-2026 manual (Wayback): pp. 30, 35-40, 57-58, 68, 74 and 122-125.
    • 2026-2027 manual: pp. 27-31, 35-42, 57-58, 68, 71, 74 and 124-127.
    • Plan: pp. 5, 6 and 9-10.
    • Rules: page 1-3 anchors.
    • Form. Multi-page titles use #page a-b or #page a,b, and the variable tuples carry # PDF pages comments.
  • 8. Wiring.
    • The programs.yaml entry is complete. No merged LIHEAP state feeds a household aggregate, so none is needed.
    • Every variable is gated by StateCode.MT or mt_liheap_eligible.
    • The subsidized-housing proxy (receives_housing_assistance or is_in_public_housing) matches the merged AL, AR, FL, GA and IA siblings.
  • 9. Axiom. See below.
  • 11, Integrate modeled state unemployment insurance into income flows #9558. The list reads total_unemployment_compensation.
  • 12. IIJA. There is no IIJA supplement.
  • 13. Mutation. See S3. The eligibility and benefit branches are otherwise pinned:
    • categorical status (all ten Appendix-style cases);
    • the over-limit lowest factor and the at-limit band;
    • the modified benefit and its floor, the rent cap, both bedroom clips and all four dwelling types;
    • kerosene, solar, an unknown county and size 0.
  • 14, Document disjoint health premium inputs and correct consumer treatment #10046. Montana deducts payroll premiums (manual p. 39: "insurance premiums paid by the employee but automatically deducted from an employee paycheck do not count as income"). The head deducts pre_tax_health_insurance_premiums (countable Case 4).
  • 15, NH Add New Hampshire state supplementation for 2026 #10156.
    • The keys. mt_liheap_dwelling_type defaults to SINGLE_FAMILY, a valid key, and bedrooms is clipped to 1-4.
    • Other inputs. Size feeds a scale calc, fpg(max_(size, 1)) and SMI arithmetic.
    • The probe. A probe at the head with a Montana non-traditional household, a New Hampshire household and a Montana household in one simulation returned $150, $0 and the expected values without error.
  • 16, Wire military retirement pay and survivors into federal and state tax (#10181) #10182 / Count military SBP annuities in state benefit income lists #10317. The list reads military_retirement_income.
  • 17. State SSP. See G1.
  • Federal floor. 42 U.S.C. 8624(b)(2)(B) holds: the limit is at least 150% of the guideline.
  • Wyoming Add Wyoming LIHEAP regular heating assistance #10346 checks.
    • Band edges. Whole-percent rounding (the manuals round 70.9% to 71% and 0.577 to 0.58) and integer band starts tile the printed bands, apart from G4(a).
    • Limits. All 30 are exact to the dollar, with no $1 drift.
  • Count each Social Security benefit once in Pennsylvania CCW countable income #10408 pattern. social_security is read once, in Python. The dependents and survivors components are read only to subtract them for students, and no list names any of them.
  • Changelog. A top-level changelog.d/mt-liheap-wave1d.added.md.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#1896 queued is valid.

  • The issue. It is OPEN and labelled pe-parity, and it is dispatch-ready: proposed module paths, corpus citations and status, verbatim rule, manual and plan text, required outputs and companion tests computed from the sources.
  • The blocker. It is recorded: there is no reviewed oracle revision, the 2026-2027 manual, both ARM versions, the offices page and other sources are missing from the corpus, and billed runs need approval.
  • The match. It describes head 3c2c8df050 and the 54 cases (11/2/10/6/2/10/6/7), with total_unemployment_compensation and military_retirement_income in the unearned list.
  • After the fixes. Refresh the head SHA and case counts, plus:
    • interpretation 11 and companion test 13(b) (S1);
    • the scope line, interpretation 9 and companion tests 13(a) and 14(c) (S2);
    • the new companion tests (S3);
    • interpretation 12 and companion test 11 (S4);
    • G1, if mt_ssp is added.

CI status

gh pr checks 10435 at head 3c2c8df050: 27 pass and 3 skipping.

Branch status

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, 4 should (Medicare premium netting narrower than ARM 37.70.407(1)(bb); under-18 income excluded against ARM 37.70.401(1); untested income sources and branches; SOLAR/OTHER non-traditional dwellings pay $0). Full report in the comment above.

hua7450 and others added 2 commits October 28, 2026 20:10
Apply DTrim99's round 1 review: tests that pin every income source, the
rule and documentation fixes, and source-conflict candidates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…into mt-liheap-wave1d

# Conflicts:
#	policyengine_us/programs.yaml
#	source_conflicts/candidates.yaml
@hua7450

hua7450 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, David. Round 1 is in b5adaaf119; f0f876a9c1 merges current main.

  • S1. Medicare premiums. income/mt_liheap_countable_income.py now nets medicare_part_a_premium plus medicare_part_b_premium from each member's Social Security, up to the benefit, for every household (manual 3.15). Any premium that Social Security does not cover joins the ARM 37.70.407(1)(bb) deduction, which non-categorical households take with their other premiums. I took your second option, so railroad_benefits stays in the unearned list and a Part B premium withheld from an RRB annuity is deducted under (bb); income/deductions.yaml explains the split. New mt_liheap.yaml Case 12 is your railroad retiree: $247 (was $0). New Case 13 pins Part A at $317. It uses a retired public employee with a $36,000 pension and 35 quarters ($311 a month of Part A) instead of a Social Security recipient, because Social Security retirees have the work credits for premium-free Part A. The household reaches the same $29,833.20 and $317 as your example; the old code gave it $0.
  • S2. Members under 18. The rule now governs: every member's nonexcluded income and premiums count under ARM 37.70.401(1), and source_conflicts/candidates.yaml records us-mt:liheap-minor-member-income (follows statute). income/adult_age.yaml is gone. The SSA minor-child test in eligibility/mt_liheap_categorical_eligibility.py reads its own categorical/minor_child_age.yaml (18), so no categorical result changes. New income/student_minimum_age.yaml (18) keeps the 407(1)(d) exclusion at ages 18 to 22, because is_full_time_student defaults to true at ages 5 to 17. Countable Case 1 now counts the 17-year-old's $4,000 ($34,000, was $30,000). mt_liheap.yaml Case 10 now counts the child's $6,000 of SSI ($71,000, was $65,000), still above $64,846, so $0. New mt_liheap.yaml Case 14 is your survivors example: $463 (was $602). The old countable Case 9, an 18-year-old's wages, now falls in the same class as Case 1, so I removed it, and the FFY2027 gifts case became Case 9.
  • S3. Untested entries. New cases in income/mt_liheap_countable_income.yaml:
    • Case 10 gives the five earned sources $5,000 down to $1,000: $15,000.
    • Case 11 gives the 18 unearned sources $100 up to $1,800, entering unemployment_compensation and military_retirement_pay under the two totals, plus mt_ssp $1,128: $18,228.
    • Case 12 has a 23-year-old full-time student with $4,800 of survivors benefits: $34,800 ($30,000 if the cap were <=).
    • Case 13 pins the floor: a SNAP-categorical 67-year-old with $1,800 of Social Security and a $2,434.80 Part B premium keeps $20,000 of wages ($19,365.20 if the excess offset wages).
    • Categorical Case 11 adds a dependent grandmother outside the TANF unit: NONE (SSI_TANF without & in_tanf_unit).
    • Eligibility Case 3 already gave self_employment_income $18,000, but dropping it changed nothing there; countable Case 10 now pins it.
  • S4. Non-traditional dwellings: declined. SOLAR and OTHER stay at $0. Manual 8.1 Fuel Types (2025-2026 p. 112; 2026-2027 p. 114) reads: "LIHEAP heating assistance may be provided for only these allowable fuel types: Propane, Wood, Coal, Fuel Oil, Natural Gas and Electricity. Does not include portable generators, batteries, solar, gasoline, or diesel fuel." I read 3.36's "regardless of heat type" within that list, as any allowable fuel. The solar-panel line sits under 3.36's "Electrical connection scenarios" and concerns the dwelling's power, not its heating fuel; ARM 37.70.601(1) is silent. mt_liheap.py now cites section 8.1 (pages 112 and 114 added), and payment/non_traditional_benefit.yaml says "for each allowable fuel". UNSPECIFIED also stays at $0, because callers must send heating_type (tracked in Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754).
  • G1. State SSP. income/sources/unearned.yaml lists mt_ssp after ssi, and countable Case 11 counts $1,128 ($94 a month).
  • G2. Source choices. source_conflicts/candidates.yaml has five entries, each with code: [] until the code is on main, so no # Source conflict markers yet:
    • (a) us-mt:liheap-income-limit-size-nine: the published 60% SMI cell (ruled 2026-10-21).
    • (b) us-mt:liheap-incorporated-table-editions: the 2025-2026 tables for FFY2026 and the October 2026 manual's tables for FFY2027 (ruled 2026-10-21).
    • (c) us-mt:liheap-gift-threshold and (d) us-mt:liheap-business-equity-limit: the rule.
    • (e) us-mt:liheap-minor-member-income: the rule (S2).
  • G3. Revision drift. All 53 files that cited the live 2026-2027 manual now cite https://web.archive.org/web/20261015032646/https://dphhs.mt.gov/assets/hcsd/liheap/LIHEAPmanual.pdf with the same page anchors, and the two new parameter files cite it too. The capture's CDX digest equals the SHA-1 of our 2026-10-21 download, so it is the same edition. No live hrefs remain.
  • G4. Documentation. (a) payment/mt_liheap_multiplier.py says a household at exactly 201% takes the top band. (b) payment/mt_liheap_benefit_income.py notes Appendix A 9.5's $11,605, $17,405 and $11,929 against 12 times the monthly rate, which the model uses.
  • Branch. f0f876a9c1 merges main at c55b806541; the coverage line keeps main's list with MT between MS and NC.

The PR body and TheAxiomFoundation/rulespec-us#1896 now describe this head, including 61 cases (mt_liheap.yaml 14, household size 2, categorical 11, eligible 6, income limit 2, countable income 13, benefit income 6, multiplier 7) and the updated interpretations and companion tests.

I did not run the tests locally; CI passed every check at f0f876a9c1.

@DTrim99

DTrim99 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Add Montana LIHEAP regular heating assistance

PR #10435 (author: hua7450), head f0f876a9c1. Round 1 (2026-10-27, head 3c2c8df050) requested changes: 0 critical, 4 should, 4 suggestions. Since then the author pushed:

  • b5adaaf119, the round-1 fix commit (60 files: 2 new parameters, 1 removed, 3 formulas, 3 test files, 5 register candidates, and Wayback hrefs);
  • f0f876a9c1, a merge of main at c55b806541 with two hand-resolved blocks.

The PR touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/mt/dphhs/liheap/
  • V/ = policyengine_us/variables/gov/states/mt/dphhs/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/liheap/

Sources re-read for this round:

  • 2026-2027 manual. The 2026-10-15 Wayback capture the PR now cites. I downloaded it and read pages 29-30, 35, 39-40, 58, 74, 114 and 119. It is the October 2026 edition.
  • 2025-2026 manual. The Wayback capture was rate-limited today, so I did not re-open its new anchor (p. 112). The axiom issue quotes the same 8.1 sentence there, and round 1 found the two editions' 3.36 text identical.
  • ARM 37.70.407, as amended, effective May 23, 2026 (rules.mt.gov), for (1)(bb).

Summary

What changed. The fix commit resolves S1-S3 and all four suggestions. Each S1-S3 fix is pinned by a test that fails without it:

  • S1 (Medicare). Medicare Part A plus Part B premiums are now netted from Social Security up to the benefit. Any remainder joins the (bb) premium deduction. The railroad retiree gets $247 (was $0) and the Part A buyer $317 (was $0 for the author's pension variant, and $247 for my Social Security variant).
  • S2 (minors). Every member's income now counts, and the choice is recorded as us-mt:liheap-minor-member-income (follows the rule, ruled). The SSI minor-child test has its own parameter, and a new 18-year lower bound keeps the student exclusion off school-age children.
  • S3 (untested entries). Every listed source now takes a distinct positive amount. The student cap, the premium floor and the TANF-unit condition each have a pinning case.
  • Suggestions. All four are done: mt_ssp, five register candidates, Wayback hrefs everywhere, and both documentation notes.

S4 is declined, and I accept the reason. Manual 8.1 limits heating assistance to the six matrix fuels and excludes solar. Read with that, 3.36's "regardless of heat type" means any allowable fuel.

One new should. The line that carries the declined reading is the final fuel-column gate, V/mt_liheap.py:102. No test pins it, so it can be deleted with no failure. Round 1 wrongly listed solar as pinned (N1).

One new suggestion. Categorical households get no (bb) deduction, following manual p. 40. The choice is not recorded (N-G1).

Merge, axiom and CI.

  • Merge blocks. Both hand-resolved blocks are correct.
  • Negative values. No new countable-income or benefit path can go negative.
  • Axiom. The axiom issue describes this head.
  • CI. 33 of 33 checks pass.
  • Branch. AK Add Alaska LIHEAP regular heating assistance #10341 has merged since. The head's coverage line now conflicts with main's, and that is a branch note only.

Status: 0 critical, 1 should, 1 suggestion open.

Prior findings

Finding Status Evidence
S1. Only Part B netted, only from Social Security Fixed V/income/mt_liheap_countable_income.py:55-58 nets medicare_part_a_premium + medicare_part_b_premium up to Social Security (min_). :64 adds Social Security net of what it covers. :76 puts the remainder into the (bb) premiums, which non-categorical households deduct (:79-84).
Tests. T/mt_liheap.yaml Case 12 (railroad retiree): $247, and with the remainder line removed it is $0. Case 13 (Part A at 35 quarters): $317, and without Part A it is $247. T/income/mt_liheap_countable_income.yaml Case 13 pins the min_ floor ($20,000 against $19,365.20).
My round-1 example (b). Social Security $36,000 with Part A $3,732 and Part B $2,434.80 also gives $29,833.20 and $317 by hand at the head.
Other inputs. health_insurance_premiums_without_medicare_part_b is documented as non-Medicare, so there is no double deduction (the same pattern as AZ and VA on main).
S2. Under-18 income excluded, conflict unrecorded Fixed adult_age is gone and :27-29 count every member.
Register. source_conflicts/candidates.yaml has us-mt:liheap-minor-member-income (follows: statute, state: ruled). Its example ($463 against $602) matches the tests.
Categorical test. It reads its own P/categorical/minor_child_age.yaml (18). Categorical Cases 5 and 10 still pin it, as SSI_TANF for a child aged 9 and NONE for a recipient aged 18.
Student lower bound. P/income/student_minimum_age.yaml (18) is needed because is_full_time_student = college or K-12, which is true at ages 5-17.
Tests. Countable Case 1 is $34,000 (was $30,000). mt_liheap Case 10 is $71,000 against the $64,846 limit, so $0. mt_liheap Case 14 asserts is_full_time_student: [false, true] and $463; with the lower bound deleted, the child's survivors benefit would be excluded and the result would be $602.
Manual support. Manual 3.11 (2026-2027 p. 30) also has "the UNEARNED income of all members ... including those under 18" entered for categorical households.
S3. 20 of 23 entries and 3 branches unpinned Fixed Earned (countable Case 10). The five earned sources take $5,000 down to $1,000, total $15,000.
Unearned (countable Case 11). The 18 unearned sources take $100 to $1,800, plus mt_ssp $1,128, total $18,228. Every amount is distinct, so dropping any entry changes the total. The two totals (total_unemployment_compensation, military_retirement_income) are also held by main's #9558 and #10317 guards.
Student cap (countable Case 12). A full-time college student aged 23 gives $34,800, and $30,000 if the cap were <=.
Premium floor (countable Case 13). See S1.
TANF unit (categorical Case 11). A dependent grandmother aged 58 gives NONE. Without & in_tanf_unit all three members are covered, giving SSI_TANF.
S4. Non-traditional SOLAR/OTHER paid $0 Declined, reason accepted The manual. 2026-2027 manual 8.1 (p. 114, verified): "FUEL TYPES – LIHEAP heating assistance may be provided for only these allowable fuel types: Propane, Wood, Coal, Fuel Oil, Natural Gas and Electricity. Does not include portable generators, batteries, solar, gasoline, or diesel fuel."
3.36's scenarios. They concern electricity access ("not accessing electricity from another living unit"), not the heating fuel.
Matrix and rule. The matrix prints the $150 only under the six fuel columns, and ARM 37.70.601(1) is silent. Reading "regardless of heat type" as any allowable fuel harmonizes two sections of the same manual, so no rule is set aside and no register entry is needed. The code comment (V/mt_liheap.py:31-34), P/payment/non_traditional_benefit.yaml and the body now cite 8.1.
Caveat. The behavior the decline defends is not pinned (N1).
G1. mt_ssp Fixed P/income/sources/unearned.yaml lists mt_ssp after ssi, and countable Case 11 counts $1,128.
G2. Record the source choices Fixed Five candidates are in source_conflicts/candidates.yaml: size nine, table editions, gifts, business equity and minor-member income.
State. Each is ruled. The two that follow the published tables over 406(1) carry follows: conflicting-source with a dated ruling.
Code field. Each has code: [] and a "code not yet on main" note, the register's convention for an open PR.
G3. Revision drift Fixed 55 hrefs now cite web.archive.org/web/20261015032646/...LIHEAPmanual.pdf, and no live manual href remains. The capture serves the October 2026 edition (page footers "October 2026").
G4. Documentation Fixed (a) V/payment/mt_liheap_multiplier.py:37-39 states that exactly 201% takes the top band.
(b) V/payment/mt_liheap_benefit_income.py:41-43 notes 9.5's figures against 12 × the monthly rate: $11,604 against $11,605, $17,400 against $17,405, and $11,928 against $11,929.

New findings

Critical

None.

Should address

N1. The fuel-column gate that carries the declined S4 reading is unpinned: deleting it fails no test

  • Where.
    • The gate. V/mt_liheap.py:102 returns where(np.any(fuel_columns, axis=0), amount, 0).
    • When it matters. For a matrix dwelling with SOLAR or OTHER, base_benefit is already 0 (select(..., default=0), :49-60), so the regular benefit is $0 with or without the gate. The gate changes the result in only two cases:
      • (a) Non-traditional dwellings. regular is the flat p.non_traditional_benefit whatever the fuel (:67-71), so without the gate it is $150.
      • (b) Subsidized heat-in-rent households. modified = max_(0.05 × 0, $25) (:81), so without the gate it is $25.
    • Reachable. Both are eligible households, because mt_liheap_eligible accepts SOLAR and OTHER as reported fuels.
  • Tests.
    • The only SOLAR case is T/mt_liheap.yaml Case 4. It is a single-family home without heat in rent, so it is $0 either way.
    • Case 3's non-traditional household heats with PROPANE.
    • Case 5's subsidized households use ELECTRICITY and NATURAL_GAS.
    • No test has OTHER. Deleting the gate, which is the change round 1's S4 asked for, therefore leaves every test passing.
  • Why it matters now.
    • The decline rests on this line. The fix commit's new comment ("SOLAR and OTHER pay $0, in non-traditional dwellings too"), the body ("SOLAR and OTHER pay $0 in every dwelling type") and companion test 11 all state the behavior, but no test holds it.
    • Round 1 listed "solar" among the pinned branches. That was my error: the Case 4 row does not exercise the gate.
  • Severity. Should. This is an unpinned branch under the mutation rule, the same class as round 1's S3 (the premium floor and the TANF-unit condition), and the gate decides a claimed outcome.
  • Fix.
    • Add a non-traditional household with SOLAR or OTHER and a heating bill (mt_liheap_eligible: true, mt_liheap: 0). Without the gate the result is $150.
    • Optionally, add a subsidized heat-in-rent household with SOLAR: $0, against $25 without the gate.
    • Add the case to rulespec-us#1896 companion test 11 and to the body's test plan.

Suggestions

N-G1. Categorical households' exception from the (bb) deduction is not recorded

  • Where. V/income/mt_liheap_countable_income.py:79-84 withholds the whole premium deduction from categorical households. That now includes the Medicare remainder: a Part B premium withheld from a railroad annuity, or a Part A premium paid from a pension.
  • Sources.
    • The manual. Manual p. 40 (2026-2027, verified): "Out-of-pocket insurance premiums are not deducted when an applicant is determined categorically eligible."
    • The rule. ARM 37.70.407(1)(bb) has no such exception: premiums "will reduce the income when supplied at the time of application or prior to application eligibility determination."
  • Why it is only a suggestion.
    • Categorical households are financially eligible automatically (402(2)). The rules do not say which income sets their multiplier, so I read the manual as filling that gap, not as overriding the rule.
    • Round 1 proposed this structure. It changes only the band for SNAP-categorical households, since SSI/TANF households use the standards.
  • Example (FFY2026, Yellowstone): a SNAP-categorical railroad retiree with an RRB annuity of $28,000 and a Part B premium of $2,434.80.
    • Head: 28,000 / 15,650 = 178.9%, rounded to 179%. That gives 0.41, and $772 × 0.41 = $317.
    • With the deduction: $25,565.20 is 163%. That gives 0.51, and $772 × 0.51 = $394.
  • Fix: add a gap-fill candidate, or a comment naming the choice, so CLAUDE.md's "record every such choice" is met.

Hand-checked new and changed expectations

All match the head. District 7 FFY2026 factors and limits are as verified in round 1.

Test Hand computation Expected
countable Case 1 (re-pinned) 30,000 + the 17-year-old's 4,000 34,000
countable Case 9 (renumbered from 10) FFY2027 gifts excluded; wages only 12,000
countable Case 10 5,000 + 4,000 + 3,000 + 2,000 + 1,000 15,000
countable Case 11 100 × (1 + … + 18) = 17,100; + mt_ssp 1,128 18,228
countable Case 12 aged 23, outside 407(1)(d): 30,000 + 4,800 34,800
countable Case 13 SNAP categorical; min(2,434.80, 1,800) = 1,800 nets Social Security to 0; 634.80 not deducted SNAP / 20,000
categorical Case 11 grandmother aged 58 is not a TANF payment-eligible child or parent, so not covered NONE
mt_liheap Case 10 (re-pinned) 40,000 + 25,000 + 6,000 SSI = 71,000 > 64,846 71,000 / false / 0
mt_liheap Case 12 34,000 − 2,434.80 = 31,565.20 ≤ 33,719; / 15,650 = 201.7%, rounded to 202%: 0.32; 772 × 0.32 = 247.04 31,565.20 / 0.32 / 247
mt_liheap Case 13 Part A 311 × 12 = 3,732 (2026 reduced premium, 30-39 quarters); 36,000 − 3,732 − 2,434.80 = 29,833.20; / 15,650 = 190.6%, rounded to 191%: 0.41; 772 × 0.41 = 316.52 3,732 / 29,833.20 / 0.41 / 317
mt_liheap Case 14 20,000 + 9,600 = 29,600 ≤ 44,095; / 21,150 = 139.95%, rounded to 140%: 0.60; 772 × 0.60 = 463.20 [false, true] / 29,600 / 0.6 / 463

The body and the axiom issue both count 61 cases: 14, 2, 11, 6, 2, 13, 6 and 7. The removed 18-year-old wages case tested only the deleted adult_age boundary, so it carried no distinct coverage.

Verified

Axiom

axiom: TheAxiomFoundation/rulespec-us#1896 queued is valid.

  • The issue. It is OPEN and labelled pe-parity, and was updated 2026-10-29 13:42 UTC.
  • The match. It describes head f0f876a9c1 (the merge of c55b806541 into b5adaaf119):
    • the 61 cases and the 45 parameters;
    • the 5 earned and 19 unearned sources with mt_ssp;
    • the A+B netting formula with the (bb) remainder;
    • interpretation 10 (minor-member candidate, student lower bound) and interpretation 11 (8.1 Fuel Types);
    • companion tests 6(k), 13(a) and (i)-(l), and 14(c)-(f), which match the YAML values.
  • Blockers. They are recorded: no reviewed oracle revision, and missing corpus sources.
  • After N1. Add the non-traditional SOLAR or OTHER case to companion test 11, and N-G1's choice if it is recorded.

CI

gh pr checks 10435 at f0f876a9c1: 33 of 33 pass.

  • Coverage. All 11 baseline shards, all 9 contrib shards, Microsimulation, all 6 Python Rest shards, the household partner suite, PR validation, Package and compatibility, and Quick Feedback.
  • Failures. None. The run includes main at c55b806541, after the MT AGI and core-3.33.13 fixes.

Branch

  • Distance. 4 commits ahead of main and 8 behind.
  • Merge check. git merge-tree --write-tree PolicyEngine/main f0f876a9c1 reports one conflict, the LIHEAP coverage: line in programs.yaml.
    • AK Add Alaska LIHEAP regular heating assistance #10341 has since merged, and main's line now begins AK, AR, AL, … without MT. The head's line has MT without AK.
    • The fix is to keep main's line and insert MT between MS and NC.
    • The MT state_implementations block and candidates.yaml merge cleanly, with unique ids.
    • This is a sibling conflict and a branch note only.

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, round 2: 0 critical, 1 should. S1-S3 are fixed and pinned, and the S4 decline is accepted (manual 8.1 limits fuels to the six matrix columns). New: the fuel-column gate that carries that decline (mt_liheap.py:102) has no test; add a non-traditional SOLAR or OTHER household expecting $0 (it would be $150 without the gate). Full report in the comment above.

hua7450 and others added 2 commits October 29, 2026 21:59
Apply DTrim99's round 2 review: a test for the fuel-column gate, a
candidate for the categorical premium deduction, and candidate code paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p-wave1d

# Conflicts:
#	policyengine_us/programs.yaml
@hua7450
hua7450 requested a review from DTrim99 October 30, 2026 02:08

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 Montana LIHEAP regular heating assistance

2 participants