Skip to content

Add Tennessee LIHEAP regular energy assistance - #10430

Merged
hua7450 merged 5 commits into
mainfrom
tn-liheap-wave1d
Oct 30, 2026
Merged

hua7450 merged 5 commits into
mainfrom
tn-liheap-wave1d

Conversation

@hua7450

@hua7450 hua7450 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Tennessee LIHEAP regular energy assistance for FY2026 and FY2027, administered by the Tennessee Housing Development Agency (THDA). Tennessee pays one benefit per program year from a single matrix; the manual applies it as heating from October through May and as cooling from June through September (p.35), and the FY2026 plan dates the components October 1 to April 30 and May 1 to September 30 (item 1.1). The model counts citizens and qualified noncitizens in household size (other members' income still counts), tests gross countable income against the income limit with the federal 110% poverty floor, requires an active energy account (a bill of any fuel) or energy included in the rent, divides total annual energy cost by income to get an energy burden rounded half up to four decimals, and reads the matrix amount by size group, income group and burden column: $174 to $750 in FY2026 and $300 to $800 in FY2027. In FY2026, households with propane heat or a propane bill receive at least $500, the manual's minimum propane payment.

Fixes #10420

Regulatory authority

Eligibility

Requirement Source How modeled
Tennessee residence Plan defined_for = StateCode.TN
At least one citizen or qualified noncitizen Manual p.17-18 tn_liheap_household_size sums is_citizen_or_legal_immigrant; size 0 is ineligible
Income at or below the limit Plan 2.1; Manual p.24; 2027 plan p.9 and manual p.27 FY2026: 60% of SMI for every size (liheap_smi_limit). FY2027: 60% of SMI for sizes 1-8 and 150% of the 2026 poverty guideline, rounded down, for sizes 9 and over
Federal 110% poverty floor 42 U.S.C. 8624(b)(2)(B) Binds from size 15 in FY2026
Active energy account or energy in rent Manual p.8, 32-33 tn_liheap_energy_cost above zero (a bill of any of the seven fuels) or heat_expense_included_in_rent. A home with no heating (NONE) and an electric bill is eligible, since the one award is applied as heating or cooling. UNSPECIFIED fuel qualifies only with heat in rent: heating_type is a required input and #9754 tracks its default
Subsidized housing Manual p.32-33 No adjustment: own-name bills are processed as filed, PHA overages are entered as bills

Countable income

Item Source How modeled
Earned income Manual p.27-28; Plan 1.9 Wages and net self-employment, each floored at zero, for members 18 and older who are not tax-unit dependents (the plan excludes minors' earnings; the manual excludes earnings of dependents 18 and older and of dependents in school)
Unearned income Manual p.27 SSI, TANF (Families First, take-up gated), general assistance, unemployment, strike benefits, workers' compensation, child support, alimony, regular outside support (financial_assistance), survivor and disability benefits, dividends, rental and estate income, net gambling winnings
Social Security, railroad retirement, pensions and annuities, military retirement (with survivor annuities) and VA benefits Manual p.27; Plan 1.9 Summed per person, each floored at zero, less the member's medicare_part_a_premium and medicare_part_b_premium, floored at zero (income/sources/net_of_medicare.yaml). The manual counts these benefits net of "Medicare premiums"; railroad annuities count as government pensions, and the Railroad Retirement Board withholds Part B premiums from them
Interest Manual p.27 Counted in full only above $200 a year for each member (Section 2.13 names no unit); the withdrawal condition has no input and is assumed
Not counted Manual p.28 Capital gains, bank withdrawals, retirement-account (IRA, 401(k)) withdrawals, sale proceeds, tax refunds, gifts and lump-sum inheritances, one-time insurance, SNAP, foster care, utility allowances. The manual names no retirement-account withdrawals as income and the model treats them like bank withdrawals; plan 1.9 checks "Retirement / pension benefits", and the model follows the manual (user ruling, 2026-10-23; candidate us-tn:liheap-retirement-account-withdrawals)

Benefit

  • Energy burden = tn_liheap_energy_cost (annual electricity, gas, propane, fuel oil and kerosene, wood, coal and other bills) divided by annual countable income, rounded half up to four decimals as in the manual's example that rounds 0.0878822197 to 8.79% (p.31-32), so a burden of exactly 0.12145 starts the 0.1215 column. Columns start at 0, 0.0510, 0.0672, 0.0828, 0.0998, 0.1215, 0.1529, 0.2182 and 1.0000. Zero income uses the last column.
  • Size groups 1-2, 3-5, 6-8 and 9 or more (the FY2027 matrix runs the last group to 16; FY2026 households above 12 use the same group).
  • Income groups A-C: FY2026 tops are 71%, 87% and 117% of the 2025 guideline, rounded to cents, which reproduce all 36 printed tops; FY2027 uses the tops printed in LI-12 for sizes 1-16 (larger households use the size-16 row), because they do not follow one rate (size 12 group A, for example). Group D runs to the income limit.
  • Propane: the FY2026 manual lets an applicant whose home "uses propane as a deliverable fuel" apply a benefit under $500 to propane, at a $500 minimum payment (p.25), and allows the benefit on any active account (p.32). Households with propane heat or a propane bill (bottled_gas_expense) are assumed to take that option. A propane-heated rental with heat in the rent also receives $500, because a heat-in-rent benefit is paid on the landlord's energy vendor account named on the LI-05 agreement. The draft FY2027 manual drops the minimum.
  • No supplement (priority points no longer set amounts, workshop p.23) and no bill cap.

Not modeled and source notes

Item Source Treatment
Energy cost a landlord attributes to a tenant (heat in rent) Manual p.32 No input. These households are eligible and their burden counts only separately billed energy, so their column is a lower bound
30-day income and 12-month energy history Manual p.8, 29-30 Annual amounts stand in
Black Lung $20 disregard, royalties, severance, adoption assistance, training stipends, military allotments, irregular income, overpayment recovery and garnishment deductions Manual p.27 No existing inputs
SSN refusal, prior-year LIHEAP credit, waitlist and priority order Manual p.13, 17-18, 33 Not represented; the model returns the entitlement
FY2027 LI-12 ceilings for sizes 13-16 ($125,910-$150,660) LI-12 p.4 Below the stated 150% rule ($126,180-$151,740); the rule is used. Candidate us-tn:liheap-fy2027-large-household-limits
Printed FY2026 size 8 ceiling $85,064.50 and size 6 copies of $81,336 Matrix; workshop The 60% SMI rule gives $85,064 and $81,366. Candidate us-tn:liheap-fy2026-printed-income-ceilings
Crisis assistance and weatherization Manual Out of scope

FY2027 relies on the posted LI-12 matrix while THDA's FY2027 plan (status Saved) and manual are drafts; the program page still announces the 2025-26 season. Earlier years are unverified estimates: they backfill the FY2026 values and do not reproduce the priority-points matrix Tennessee used before FY2026 (workshop p.23).

Microsimulation: heating_type and the per-fuel bills are not in current datasets; callers must supply them.

Test plan

56 YAML cases in 4 files: tn_liheap.yaml 25, eligibility/tn_liheap_eligible.yaml 10, eligibility/tn_liheap_income_limit.yaml 7 and income/tn_liheap_countable_income.yaml 14. The 7 cases added in review round 1 and the re-pinned eligibility Case 5 have hand-derived expected values and were not run locally; CI validates them.

  • limits for FY2026 sizes 1, 8 and 13 and FY2027 sizes 8, 9 and 13, the exact-limit boundary and the size-15 floor; a home with no heating and an electric bill that is eligible ($445), and UNSPECIFIED fuel without heat in rent that is not;

  • countable income: a 17-year-old, an 18-year-old non-dependent and an adult dependent; every listed earned source ($20,000) and unearned source ($66,000) with distinct amounts; Social Security net of Part B; railroad retirement, a private pension, military retirement with a survivor annuity and VA benefits net of Part A and Part B ($22,398.40); interest at $200 and $201, child support, gambling, TANF, SSI and outside support;

  • half-up rounding of a burden of exactly 0.12145 onto the 0.1215 column, zero income, income-group edges in FY2026 (including income exactly at a printed top) and at printed FY2027 tops, size groups, a size-13 household in each year, a size-9 FY2027 household eligible only under the 150% rule, a 17-person FY2027 household on the size-16 row beside a Kentucky household ([605, 0]), electricity, fuel oil, wood, coal and other bills in one burden ($700), the propane minimum in FY2026 for propane heat, a propane bill under electric heat and propane heat in rent, none in FY2027, and heat in rent.

  • YAML tests with expected values from the plans, matrices and manual

  • ruff format

  • Changelog fragment in changelog.d/

  • CI passes

axiom: TheAxiomFoundation/rulespec-us#1891 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 (b0a8c09) to head (f1dd95f).
⚠️ Report is 39 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main    #10430    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            1         6     +5     
  Lines           15       136   +121     
  Branches         0         5     +5     
==========================================
+ Hits            15       136   +121     
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 Tennessee LIHEAP regular energy assistance

PR #10430 (author: hua7450), head 5ddb74f1cc. This is a first review. The PR adds Tennessee's LIHEAP regular assistance, run by the Tennessee Housing Development Agency (THDA), for FY2026 and FY2027 (model years 2026 and 2027). It includes:

  • 19 parameter files and 5 variables (no new inputs);
  • 4 test files (49 cases);
  • a programs.yaml entry (coverage line and state block);
  • a changelog fragment.

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

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/tn/thda/liheap/
  • V/ = policyengine_us/variables/gov/states/tn/thda/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/tn/thda/liheap/

Sources read for this review:

Summary

What is right. The tables, limits and benefit mechanics are encoded correctly:

  • Matrices. All 144 encoded cells (4 size groups by 4 income groups by 9 columns) match the 2026 matrix and LI-12 for both years. Rows are identical within the size groups 1-2, 3-5, 6-8 and 9 and over, as the PR says. The plans' $174/$750 and $300/$800 equal the lowest and highest cells.
  • Income groups. For FY2026, 71%, 87% and 117% of the 2025 guideline, rounded to cents, reproduce the printed tops (spot-checked for sizes 1, 2, 3 and 12). For FY2027, all 48 printed tops for sizes 1-16 are transcribed exactly, including the odd size-4 group B top of $28,710.51.
  • Limits. For FY2026, 60% of SMI (four-person $61,641) reproduces every printed ceiling for sizes 1-12; the matrix's $85,064.50 for size 8 is off by 50 cents. For FY2027, 60% of SMI for sizes 1-8 and 150% of the 2026 guideline for sizes 9-12 reproduce LI-12. Both draft-plan versions list 150% for sizes 9-15, and the draft manual lists sizes 9-16.
  • Federal floor. The 110% floor uses the lagged guideline and a strict <. It binds from size 15 in FY2026, and both sides of it are tested.
  • Burden. Total bills over income, rounded half up to four decimals, select the right column, and zero income takes column 9.
  • Income rules.
    • Earned income is gross, and minors' and adult dependents' earnings are excluded (plan 1.9; manual p. 28).
    • Losses are floored per source and per person.
    • Interest counts only above $200, and gambling winnings count net.
    • TANF counts through the take-up-gated tanf.
    • Mixed-status members are excluded from size, but their income counts (pp. 17-18).
  • Tests and CI. I hand-checked 18 expectations below, and CI passed all 49 cases.
  • Guards. Integrate modeled state unemployment insurance into income flows #9558, Count military SBP annuities in state benefit income lists #10317, the Count each Social Security benefit once in Pennsylvania CCW countable income #10408 pattern and the Tie source-conflict markers to the register shared with Axiom #10287 ratchet all pass on the merged tree. The NH Add New Hampshire state supplementation for 2026 #10156 key pattern is safe.

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

  • S1. Active account. Eligibility requires a bill for the primary heating fuel and excludes NONE. Tennessee requires only an active energy account and applies the same benefit as cooling assistance. A no-heat household with an electric bill gets $0 instead of $445, and eligibility Case 5 pins the $0.
  • S2. Medicare premiums. Only the Part B premium is netted, and only from Social Security. The manual nets "Medicare premiums" from Social Security, pensions and VA benefits. A railroad retiree whose Part B is withheld from the RRB annuity is counted gross: $0 instead of $250 in the example below.
  • S3. Untested entries. 18 of the 23 income-source entries and 4 of the 7 energy-cost entries never take a positive value in a test, and the size-16 table clip has no case.

There are also five suggestions:

  • G1: retirement-account withdrawals and the plan's "Retirement / pension benefits" box.
  • G2: record the source choices as backlog candidates.
  • G3: base the propane minimum on a propane bill.
  • G4: small documentation fixes.
  • G5: backfilled pre-FY2026 years.

Status.

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

Critical (must fix)

None.

Should address

S1. Eligibility requires a primary-heating-fuel bill and excludes NONE, but Tennessee requires only an active energy account and pays the same benefit as cooling assistance

  • Where:
    • V/eligibility/tn_liheap_eligible.py:34 sets responsible = has_heating_expense | heat_in_rent. On main, has_heating_expense is true only when "the primary heating fuel's bill is positive", so an electric bill counts only if electricity is the heating fuel.
    • Lines 44-46 (fuel_qualifies) exclude NONE outright, and exclude UNSPECIFIED unless heat is in the rent.
    • T/eligibility/tn_liheap_eligible.yaml Case 5 (line 114) gives a NONE household a $1,680 electric bill and pins it ineligible. Case 7 (line 155) does the same for UNSPECIFIED.
  • Sources:
    • Eligibility. Manual 2.18 (2026 p. 33): "Applicants without an energy burden (i.e., those who do not have an active energy account) are not eligible". An "Active Account" is "A current utility account that reflects billing and usage" (p. 8). No fuel requirement appears.
    • One benefit, heating or cooling.
      • Manual p. 35: "Heating Assistance is applied from October through May. Cooling Assistance is applied from June through September."
      • The FY2026 plan runs a cooling component (item 1.1, p. 4, May 1 to September 30). Section 3 (pp. 10-11) gives it the same 60% SMI limit and the same $174-$750 benefits.
      • The draft 2027 manual (p. 18): "Each approved household must apply their energy assistance benefit to one (1) utility, (primary source of heating or cooling)."
    • Burden. The burden uses "all energy usage (electric, gas, propane, etc.)" (p. 8). The matrix has no fuel dimension, so the fuel matters only for the FY2026 propane minimum.
  • Effect (hand-traced; 2026, a single adult aged 40 with wages of $15,080 and a $1,680 electric bill). The rule's award is the same in each case: burden 1,680 / 15,080 = 0.111406, rounded to 0.1114, gives column 5. $15,080 is group C (13,615.51-18,310.50), and sizes 1-2 group C column 5 is $445.
    • (a) No heating (NONE), the facts of Case 5. Head: fuel_qualifies is false, so $0. Rule: an active electric account, so $445, applied as cooling or to the electric account.
    • (b) Wood heat from gathered wood (wood_expense 0) and the electric bill. Head: has_heating_expense reads only the wood bill, so $0. Rule: $445.
    • (c) UNSPECIFIED with the electric bill, the facts of Case 7. Head: $0. The wave has accepted UNSPECIFIED at $0 where a program needs the fuel. Tennessee's award does not need it, so I recommend the same fix, but this finding rests on (a) and (b).
  • Precedent. Arizona LIHEAP (Add Arizona LIHEAP Standard benefit #10293, merged) keeps NONE eligible "because the benefit covers cooling". New Mexico (Add New Mexico LIHEAP regular heating assistance #10431 S1, cooling-only households at $0) and Maine (Add Maine HEAP Fuel Assistance #10437 S3, a fuel gate the rule does not impose) were both rated should.
  • Severity: should. This is a wrong result in a claimed year with existing inputs, and a test pins it.
  • Fix:
    • Define an energy cost from energy_cost_sources (for example, a tn_liheap_energy_cost variable that tn_liheap also reads). Set responsible = (energy_cost > 0) | heat_in_rent.
    • Drop the NONE and UNSPECIFIED clauses. A no-heat household with no bill and no heat in rent stays ineligible through the energy-cost test.
    • Re-pin eligibility Cases 5 and 7, and add case (b).
    • Update the body's eligibility row, the comment at lines 31-43, and Axiom interpretation 8 and companion test 9(a)-(b).

S2. Medicare premiums are netted only as Part B, and only from Social Security; the manual nets Medicare premiums from Social Security, pensions and VA benefits

  • Where:
    • V/income/tn_liheap_countable_income.py:36-43 computes max_(social_security - medicare_part_b_premium, 0) per person.
    • railroad_benefits, pension_income, military_retirement_income and veterans_benefits are summed gross from P/income/sources/unearned.yaml.
    • The body ("Net of medicare_part_b_premium") and Axiom interpretation 13 ("premiums withheld from pensions or VA benefits ... are not subtracted") describe this, but neither cites a source for the narrowing.
  • Sources:
    • Manual 2.13 (2026 p. 27; draft 2027 p. 29, the same words): "Net income from Social Security, pensions (private and government, including military retirement pay), and VA benefits (excluding Medicare premiums, overpayment recovery, or garnishment payments)". The same page lists "railroad retirement" beside Social Security among the regular payments.
    • Plan 1.9 (FY2026 p. 6 and the FY2027 draft): SSA benefits "Excluding MediCare deduction".
    • RRB withholding. The RRB's 2026 release says the monthly Part B premium "will be 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 never nets.
    • "Medicare premiums" is general. The wave precedent says Part A matters where the rule covers "Medicare premiums" generally. medicare_part_a_premium is on main, and Arizona LIHEAP (Add Arizona LIHEAP Standard benefit #10293, merged) subtracts both premiums.
  • Effect (hand-traced; 2026, a single person aged 70 with railroad_benefits of $33,000, medicare_part_b_premium of $2,434.80 (the input Case 5 uses), no Social Security and a $1,680 electric bill):
    • Head: countable income 33,000 + max(0 − 2,434.80, 0) = 33,000, above the 32,053 limit and the 17,215 floor, so $0.
    • Rule: 33,000 − 2,434.80 = 30,565.20 is eligible and in group D. Burden 1,680 / 30,565.20 = 0.054965, rounded to 0.0550, gives column 2: $250.
    • Away from the limit, the overstatement of $2,434.80 a year can still move the income group or burden column.
  • Severity: should, as in the wave's Medicare-netting 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 PR nets part of what the state's rule nets, and the gap is material for RRB annuitants.
  • Fix:
    • Per person, take social_security, railroad_benefits, pension_income, military_retirement_income and veterans_benefits out of the plain sum. Count max(their total − medicare_part_a_premium − medicare_part_b_premium, 0) instead, and remove those entries from the unearned list.
    • Case 5 is unchanged, because its second person has no other income.
    • Add the RRB case above and a Part A case.
    • Update the body and Axiom interpretation 13.

S3. 18 of the 23 income-source entries, 4 of the 7 energy-cost entries and the size-16 table clip have no test that would fail if they were dropped

  • Where: P/income/sources/earned.yaml:9-13, P/income/sources/unearned.yaml:4-28, P/payment/energy_cost_sources.yaml and V/tn_liheap.py:79.
  • Untested entries. No case gives these a positive value:
    • Earned (3 of 5): sstb_self_employment_income and farm_operations_income never appear. partnership_s_corp_income appears only as −$1,500 (countable Case 1), which floors to 0, the same result as dropping it.
    • Unearned (15 of 18):
      • general_assistance, railroad_benefits, total_unemployment_compensation, strike_benefits and workers_compensation;
      • veterans_benefits, alimony_income, pension_income, military_retirement_income, survivor_benefits and disability_benefits;
      • ordinary_dividend_income, farm_rent_income and estate_income;
      • rental_income, which appears only as −$1,800.
    • Energy cost (4 of 7): fuel_oil_expense, wood_expense, coal_expense and other_heating_fuel_expense never appear. Axiom companion test 9(f) describes a fuel-oil award that the YAML does not have.
  • Tested:
    • Sources: wages, self_employment_income, ssi, child_support_received and financial_assistance, plus Social Security, interest, gambling and tanf outside the lists.
    • Floors: the earned and unearned per-source floors are pinned (Case 1 expects 18,080, not 14,780), and so are the Social Security and gambling per-person floors (Cases 5 and 8).
    • Bills: electricity, gas and propane.
  • Branch. clip(size, 1, max_table_size) sends FY2027 households above 16 to the size-16 row. No case has 17 members, so removing the upper clip (which would raise a missing-key error) fails nothing.
  • Severity: should, as in Add Maine HEAP Fuel Assistance #10437 S4, Add Vermont Seasonal Fuel Assistance #10436 S2, Add Nevada Energy Assistance Program Fixed Annual Credit #10432 S1 and Add Wisconsin WHEAP regular heating benefit #10434 S1 (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 burden case with fuel oil, wood, coal and other bills.
    • Add a 17-person FY2027 case.
    • Add the new cases to the rulespec-us#1891 companion tests.

Suggestions

G1. Retirement-account withdrawals.

  • The head. P/income/sources/unearned.yaml:31-35 and countable Case 7 exclude IRA and 401(k) withdrawals "like bank withdrawals".
  • The sources.
    • Plan 1.9 checks "Retirement / pension benefits" as countable (FY2026 p. 6; the FY2027 draft too).
    • Manual p. 9 defines household income as "Total annual gross cash receipts from all household members, before taxes, excluding amounts listed in the LIHEAP State Plan".
    • The exclusion names only "Withdrawals from a bank account" (p. 28).
  • Why it is only a suggestion. The PR's reading follows the older Census money-income convention ("assets drawn down"), and the Maine and Add Louisiana LIHEAP regular heating assistance #10429 siblings were accepted with the same exclusion.
  • Effect (a single retiree aged 70: Social Security $12,000, Part B $2,434.80, IRA withdrawals $20,000, electricity $1,680):
    • Head: 9,565.20 is group A; burden 0.1756 gives column 7: $710.
    • Counting the withdrawals: 29,565.20 is group D; 0.0568 gives column 2: $250.
  • Fix: ask THDA, count retirement_distributions, or record the choice as a backlog candidate (G2).

G2. Record the source choices as backlog candidates.

G3. Base the FY2026 propane minimum on a propane bill.

  • The rule. Manual p. 25: "where an approved applicant receives a benefit under $500, but the home uses propane as a deliverable fuel, the applicant will have the option to apply their benefit to propane". The text says "uses", not "primary".
  • The head. V/tn_liheap.py:90-92 keys the minimum on heating_type == PROPANE, which misses two cases:
    • (i) Propane but not propane heat. An electric-heat household with a propane bill (for a range or water heater) could apply its benefit to propane. Wages $20,000, electricity $1,000 and propane $200 give column 2, group D: the head pays $250, against $500.
    • (ii) Propane heat in the rent. A tenant with propane heat included in the rent and no propane account of its own gets $500 in the head. The matrix amount is $174 (wages $15,080, column 1, group C).
  • Fix: use bottled_gas_expense > 0, or document both assumptions.

G4. Small documentation fixes.

  • (a) Seasons.
    • The plans. Item 1.1 of both plans (p. 4) runs heating from October 1 to April 30 and cooling from May 1 to September 30.
    • The manual. Page 35 says October-May and June-September.
    • The head. The tn_liheap documentation and the body use the manual's months. Cite both, or use the plan's.
  • (b) Interest. The body applies the $200 threshold "per person", but the manual says neither per person nor per household. State the basis.
  • (c) Newer plan file. A newer accessible copy of the FY2027 draft plan (LIHEAP_DETAILED-MODEL-PLAN-LIHEAP_10_01_2026-002.pdf, 57 pages, also status Saved) is posted. It has the same 2.1 table and the same $300/$800 benefits, so re-citing it is optional.
  • (d) Mixed-state case. An optional two-state case would pin item 15 (see Verified).

G5. Earlier years.

  • The head. Every parameter starts on 2025-10-01, so model years before 2026 backfill FY2026 values. The registry and the body correctly claim only 2026-2027.
  • The old program. Before FY2026, Tennessee used a priority-points matrix (the FY2025 matrix, "Priority Points and Benefit Levels Matrices"; workshop p. 23: "Priority Points will no longer determine placement or benefit amount"). So backfilled results do not approximate the old program.
  • Fix: none needed (NJ S3 precedent). Optionally say so in the documentation.

Verified correct

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

Test Hand computation Expected
tn_liheap Case 1 top A 15,650 × 0.71 = 11,111.50 (income at top, group A); 1,680 / 11,111.50 = 0.1512, column 6 560
tn_liheap Case 2 11,111.51, group B; 0.1512, column 6 555
tn_liheap Case 4 18,310.51 > 15,650 × 1.17, group D; 2,580 / 18,310.51 = 0.1409, column 6 540
tn_liheap Case 5 2,429 / 20,000 = 0.12145, rounded half up to 0.1215, column 6, group D 540
tn_liheap Case 6 1,018.80 / 20,000 = 0.05094, 0.0509, column 1 174
tn_liheap Case 7 zero income: column 9, group A 750
tn_liheap Case 10 size 6, top A 43,150 × 0.71 = 30,636.50; 2,280 / 30,160 = 0.0756, column 3; sizes 6-8 A 565
tn_liheap Case 11 size 13, guideline 81,650: tops 57,971.50 and 71,035.50, so 68,000 is group B; 0.06, column 2; 9+ B 525
tn_liheap Case 12 1,200 / 20,000 = 0.06, column 2, group D 250; max(250, 500) 500
tn_liheap Case 13 2,880 / 15,080 = 0.1910, column 7, group C 700 ≥ 500 700
tn_liheap Case 17 (2027) size 4, printed B top 28,710.51 (income at top, group B); 2,600 / 28,710.51 = 0.0906, column 4; sizes 3-5 B 505
tn_liheap Case 18 (2027) size 13, printed tops 59,597.50 and 73,027.50, so 70,000 is group B; 0.06, column 2; 9+ B 605
tn_liheap Case 20 size 3, top C 26,650 × 1.17 = 31,180.50 (rounded to cents, group C); 2,880 / 31,180.50 = 0.0924, column 4; sizes 3-5 C 400
tn_liheap Case 21 (2027) size 9: 1.5 × 61,400 = 92,100; 60% SMI 63,196 × 1.41 = 89,106; C top 71,838.50, so group D; 0.0467, column 1 92,100 / 540
eligibility Cases 3 and 4 limit floor(61,641 × 1.59) = 98,009; floor (15,650 + 5,500 × 14) × 1.1 = 101,915; 101,914 < 101,915 but 101,915 is not true / false
income limit Case 6 (2027) 1.5 × (15,960 + 5,680 × 12) = 126,180 126,180
countable Case 5 (24,000 − 2,434.80) + max(1,800 − 2,434.80, 0) 21,565.20
countable Case 11 994 × 12 + 1,200 13,128

Batch checks:

  • 1. Plan and manual first. All cells, tops and limits were checked as above. The FY2026 and FY2027 timing (October 1 parameter dates; model year = federal FY) is right.
    • FY2027 values. They come from the posted LI-12, whose $300/$800 range equals both draft-plan versions' item 2.6, and the stated 9-and-over rule appears in both drafts. The registry's verified_years: "2026-2027" is backed by published values (contrast Add Vermont Seasonal Fuel Assistance #10436 S3).
  • 2. Income.
  • 3. Backdating. Only 2026-2027 are claimed (see G5).
  • 4. Arrangements.
    • Housing. Heat in rent is eligible and gets the separately billed burden (documented lower bound). Subsidized tenants meet no added condition (2.18).
    • Eligibility rules. There is no categorical eligibility (plan 1.4, "No"), no asset test and no supplement.
    • Fuel. See S1.
  • 5. Tests. Periods are plain. The edges are covered: the exact limit, the floor on both sides, income at printed tops, the rounding tie and sizes 1, 2, 3, 6, 8, 9, 13 and 15.
  • 6. Descriptions. All 19 follow the house template.
  • 7. References. Every anchor I opened shows its value:
    • 2026 manual: p. 8 (Child, Annual Average), p. 9 (Household Income), pp. 17-18 (mixed status), p. 24 (2.10), p. 25 (propane), pp. 27-28 (2.13), pp. 31-32 (2.17) and p. 34 (Benefit Levels).
    • Plans: the FY2026 plan's pp. 5-7 (1.8-1.9), p. 8 (2.1) and p. 9 (2.5-2.6); the FY2027 draft's p. 9 (2.1) and p. 12 (2.5-2.6).
    • Draft 2027 manual: p. 18 (deliverable fuels), p. 27 (2.10), p. 29 (2.13) and p. 30 (exclusions).
    • Workshop: pp. 28-30.
    • 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.TN or tn_liheap_eligible.
  • 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. Every eligibility and benefit branch other than the size-16 clip is pinned: both uses_fpg_rates branches, the propane minimum in both years, zero income, size 0 and the floor.
  • 14. Health premiums. Tennessee deducts none from wages, and neither does the head.
  • 15, NH Add New Hampshire state supplementation for 2026 #10156. For non-residents, tn_liheap_household_size is 0, but every key stays valid:
    • size_group.calc(0) is 1;
    • clip(size, 1, 16) keeps the table row valid;
    • the column comes from a bracket calc or amounts[-1];
    • fpg and liheap_smi_limit use size arithmetically, not as a key.
  • 16, Wire military retirement pay and survivors into federal and state tax (#10181) #10182. The list reads military_retirement_income.
  • 17. State SSP. Tennessee "does not provide optional supplementation" (SSA), and main has no tn_ssp. SSI is counted, so nothing is missing.
  • Federal floor. 42 U.S.C. 8624(b)(2)(B) is applied with the 2025 guideline in FY2026 and the 2026 guideline in FY2027.
  • Count each Social Security benefit once in Pennsylvania CCW countable income #10408 pattern. social_security is read once, in Python, and no list names it or a component. survivor_benefits and disability_benefits are non-Social Security inputs.
  • Changelog. A top-level changelog.d/tn-liheap-wave1d.added.md.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#1891 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 source text, required outputs and companion tests computed from the matrices, plans and manual.
  • The blocker. There is no reviewed oracle revision; the workshop deck, the draft FY2027 manual and plan, and the IM are missing from the corpus; and billed runs need approval.
  • The match. It describes head 5ddb74f1cc and the 49 cases (21/10/7/11), with total_unemployment_compensation and military_retirement_income in the unearned list.
  • After the fixes. Refresh the head SHA and case counts; interpretation 8 and companion test 9(a)-(b) (S1); interpretation 13 and companion test 12(b) (S2); the new companion tests (S3, including the fuel-oil award 9(f) already described); and interpretation 10 if G3 is taken.

CI status

gh pr checks 10430 at head 5ddb74f1cc: 29 pass and 3 skipping (the superseded run 40032341205). Run 40032551559 covers the head:

  • Coverage. All baseline and contrib shards, the guard tests, the household partner suite, microsimulation and Codecov pass.
  • Failures. None.

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, 3 should (fuel-bill eligibility excludes households Tennessee serves; Medicare premium netting narrower than manual 2.13; untested income sources and branches). 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 tn-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 fef2e3f259; 5fc125e88a merges current main and keeps TN on the LIHEAP coverage line in programs.yaml.

  • S1, active account. New tn_liheap_energy_cost.py adds the seven bills in payment/energy_cost_sources.yaml, and tn_liheap.py reads it. eligibility/tn_liheap_eligible.py now requires tn_liheap_energy_cost above zero or heat in rent; the NONE bar and the has_heating_expense test are gone. Eligibility Case 5 (no heating, $1,680 electric bill, wages $15,080) is now eligible and pays $445 (was $0). Case 7 drops the unread has_heating_expense input and stays ineligible. Your case (b), wood heat with no wood bill and the same electric bill, takes the same branch and also gives $445, so it has no separate YAML case; LIHEAP wave 1d: encode Tennessee LIHEAP regular heating assistance TheAxiomFoundation/rulespec-us#1891 lists it under companion test 9(a). Part (c) is declined: UNSPECIFIED still qualifies only with heat in rent, because callers must send heating_type (Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754 tracks the default).
  • S2, Medicare premiums. New income/sources/net_of_medicare.yaml lists railroad_benefits, veterans_benefits, pension_income and military_retirement_income, and income/sources/unearned.yaml drops them. income/tn_liheap_countable_income.py adds each member's Social Security and those four sources, each floored at zero, subtracts medicare_part_a_premium and medicare_part_b_premium, and floors the result at zero. By hand, your railroad retiree (age 70, $33,000 annuity, $2,434.80 Part B, $1,680 electric bill) now counts $30,565.20 and receives $250 (was $0). Countable Case 14 pins Part A and Part B across all four sources at $22,398.40 (was $31,000). Countable Case 5 stays $21,565.20, because Part A is premium-free at the default 40 quarters. The 2026 RRB release states: "The Railroad Retirement Board withholds Part B premiums, Part B income-related adjustments, and Part D income-related adjustments from benefit payments it processes."
  • S3, untested entries. Countable Case 12 gives each of the five earned sources a distinct amount ($20,000). Case 13 does the same for the 11 unearned entries no other case covers, including unemployment_compensation and positive rental income ($66,000). Case 14 covers the four net-of-Medicare sources, with military_retirement_pay_survivors. tn_liheap.yaml Case 22 adds fuel oil, wood, coal and other bills to electricity (tn_liheap_energy_cost $2,700; benefit $700). Case 23 is a 17-person FY2027 household on the size-16 row: limit $160,260, group B, column 2, $605.
  • G1, retirement-account withdrawals. Declined. The user ruled on 2026-10-23 that Tennessee keeps IRA and 401(k) withdrawals excluded, following the manual's income list over plan 1.9's "Retirement / pension benefits" box. Countable Case 7 is unchanged. us-tn:liheap-retirement-account-withdrawals (state ruled) records the choice with your example: $710 excluded, $250 counted.
  • G2, candidates. source_conflicts/candidates.yaml adds us-tn:liheap-fy2026-printed-income-ceilings (the matrix's $85,064.50 and the workshop's $81,336 against 60% of SMI) and us-tn:liheap-fy2027-large-household-limits (LI-12's size 13-16 ceilings against the 150% rule), both unreviewed, plus the G1 entry. All three use follows: gap-fill and code: [] and list this PR.
  • G3, propane minimum. (i) tn_liheap.py applies the FY2026 minimum when heating_type is PROPANE or bottled_gas_expense is positive. tn_liheap.yaml Case 24 (electric heat, $1,000 electric and $200 propane bills, wages $20,000) pays $500 (was $250). (ii) Kept and documented in payment/propane_minimum.yaml and tn_liheap.py: a heat-in-rent benefit is paid on the landlord's energy vendor account named on the LI-05 agreement, so propane heat in the rent can take the propane payment. Case 25 pins it at $500 (matrix amount $174).
  • G4. (a) The tn_liheap.py documentation cites the manual's months (page 35) and the FY2026 plan's component dates (heating October 1 to April 30, cooling May 1 to September 30), and the plan reference now points to page 4 (# PDF pages 4, 9). (b) income/interest_threshold.yaml and the comment in income/tn_liheap_countable_income.py say Section 2.13 names no unit for the $200 threshold and the model applies it to each member's interest; the body says the same. (c) Declined: the cited 10_01_2026 draft plan file still loads (HTTP 200 on 2026-10-28) and has the same 2.1 table and $300/$800 range. (d) Done in Case 23: a one-person Kentucky household runs the formula at size 0 beside the 17-person Tennessee household and receives $0, so removing either side of clip(size, 1, p_group.max_table_size) raises a missing-key error.
  • G5, earlier years. The tn_liheap.py documentation now says earlier years are unverified estimates that backfill the FY2026 values and do not reproduce the priority-points matrix Tennessee used before FY2026; the body says the same.

The PR body and TheAxiomFoundation/rulespec-us#1891 now describe this head, including the new companion tests and the 56 cases (tn_liheap.yaml 25, tn_liheap_eligible.yaml 10, tn_liheap_income_limit.yaml 7, tn_liheap_countable_income.yaml 14).

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

@DTrim99

DTrim99 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Add Tennessee LIHEAP regular energy assistance

PR #10430 (author: hua7450), head 5fc125e88a. Round 1 reviewed 5ddb74f1cc on 2026-10-27 and requested changes: 0 critical, 3 should and 5 suggestions. Since then the author has pushed one fix commit, fef2e3f259, and a merge of main at c55b806541 that resolved two conflict blocks by hand. They replied item by item and re-requested review. The PR touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/tn/thda/liheap/
  • V/ = policyengine_us/variables/gov/states/tn/thda/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/tn/thda/liheap/

Summary

All three should items are fixed. Every suggestion was either taken or declined with a reason I accept.

  • S1 (fixed). Eligibility now asks for an active energy account: a positive tn_liheap_energy_cost (a bill of any of the seven fuels) or heat in the rent. The NONE bar and the primary-fuel has_heating_expense test are gone. Eligibility Case 5 (no heating, an electric bill) now pays $445 instead of $0. Part (c), UNSPECIFIED paying $0, was declined. Round 1 did not rest the finding on it, and the wave convention (Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754) accepts it.
  • S2 (fixed). Each member's Social Security, railroad retirement, pensions, military retirement (with SBP annuities) and VA benefits are summed and netted of medicare_part_a_premium and medicare_part_b_premium, floored at zero. This is the computed, net-of-MSP pattern the wave accepted for UT, ND and NH. Countable Case 14 pins it at $22,398.40; the round-1 code gives $31,000.
  • S3 (fixed). New cases give every income-source entry and every bill a distinct positive amount, and a 17-person FY2027 household pins both bounds of the size-16 clip. All 23 income entries and all 7 bills now have a test that fails without them.
  • Suggestions.
    • G2, G4(a), (b) and (d), and G5 are done, and so is G3(i).
    • G1 is declined on a recorded ruling (state: ruled).
    • G3(ii) is kept and documented, with the LI-05 agreement as support.
    • G4(c) is declined; it was optional.

Merge resolutions. Both hand-resolved blocks are correct.

  • programs.yaml. The coverage line keeps all 31 of the merge base's entries and adds TN.
  • candidates.yaml. It keeps main's three new South Dakota SSP entries and the three Tennessee entries, each once.

No new path can go below zero. The benefit has no subtractive term: every matrix cell is at least $174, and the only adjustment is max_ with the propane minimum. The one new subtraction, Medicare premiums from benefit income, is floored at zero per person, and Case 5 pins that floor. This avoids the Maine #10437 r2 failure, where an OTHER fuel scoring 0 pushed a benefit negative.

Axiom, CI and branch.

  • Axiom. rulespec-us#1891 was updated to this head.
  • CI. 35/35 checks pass at this head. Baseline states-shard-1 ran all 56 Tennessee cases.
  • Branch. Alaska Add Alaska LIHEAP regular heating assistance #10341 has merged since. Its coverage line conflicts with this PR's, which needs a one-line union (branch note).

New findings: 0 critical, 0 should and 1 suggestion.

  • G-N1. Housekeeping: tick the body's "CI passes" box.

Prior findings

Finding Status Evidence
S1. Eligibility required a primary-heating-fuel bill and excluded NONE; Tennessee needs only an active energy account Fixed ((a) and (b)); (c) declined, accepted Code. V/tn_liheap_energy_cost.py:21 adds the seven bills in P/payment/energy_cost_sources.yaml, and V/tn_liheap.py:40 now reads it. V/eligibility/tn_liheap_eligible.py:41-46 sets responsible = (has_energy_cost & known_fuel) | heat_in_rent and no longer reads has_heating_expense or tests NONE. The comment at lines 31-40 cites 2.18, the page 8 definition and 2.17.
Tests. T/eligibility/tn_liheap_eligible.yaml Case 5 (lines 114-136: NONE, electricity $1,680, wages $15,080) now expects true and $445. The round-1 code returns false, so the case fails without the fix.
Each new term is pinned:
  • has_energy_cost by Case 8 (ELECTRICITY, $0 bill, no heat in rent, false);
  • known_fuel by Case 7 (UNSPECIFIED with a $1,680 electric bill, false);
  • heat in rent by Case 6 (true) and tn_liheap.yaml Cases 14 and 25.
(b), wood heat with no wood bill. It takes the same branch as Case 5, because the fuel's own bill is no longer read. Axiom companion test 9(a) carries it, so a separate YAML case adds nothing.
(c) declined. UNSPECIFIED still qualifies only with heat in the rent, because heating_type is a required input (#9754). Round 1 said the finding rested on (a) and (b), and the wave treats UNSPECIFIED at $0 as convention, at most a suggestion (ND N-G1, MO r2). Accepted.
Docs. The body's eligibility row and Axiom interpretation 8 match.
S2. Only Part B was netted, and only from Social Security; manual 2.13 nets "Medicare premiums" from Social Security, pensions and VA benefits Fixed Code. P/income/sources/net_of_medicare.yaml lists railroad_benefits, veterans_benefits, pension_income and military_retirement_income, and P/income/sources/unearned.yaml drops all four. V/income/tn_liheap_countable_income.py:41-47 computes, per member, max(social_security + the four sources, each floored − medicare_part_a_premium − medicare_part_b_premium, 0). On main, both premiums are computed out-of-pocket amounts, net of MSP and gated to medicare_enrolled: the accepted UT/ND/NH pattern.
Test. T/income/tn_liheap_countable_income.yaml Case 14 (lines 361-397) expects 22,398.40. The round-1 code gives 16,000 + 4,000 + 10,000 + 1,000 = 31,000, so the case fails without the fix. Each of the four entries and the Part A input is a distinct amount, so dropping any one changes the total.
Unchanged case. Countable Case 5 stays 21,565.20, because Part A is premium-free at the default quarters; CI confirms it. It still pins the zero floor: without the floor it would be 20,930.40.
Round-1 RRB example. By hand at the head: 33,000 − 2,434.80 = 30,565.20, which is at or below 32,053, group D. 1,680 / 30,565.20 = 0.054965, rounded to 0.0550, is column 2: $250 (was $0). It is Axiom companion test 12(k); the railroad netting itself is pinned by Case 14.
Docs. The body and Axiom interpretation 13 match.
S3. 18 of 23 income entries, 4 of 7 energy entries and the size-16 clip had no test that would fail without them Fixed Earned (5 of 5). Countable Case 12 (line 301): wages 10,000, self-employment 1,000, SSTB 2,000, farm 3,000 and partnership_income 4,000 (through partnership_s_corp_income) give 20,000.
Unearned (14 of 14). Countable Case 13 (line 327) sets the 11 entries no other case covers to $1,000-$11,000, giving 66,000. They include reported unemployment_compensation (through total_unemployment_compensation), qualified_dividend_income (through ordinary_dividend_income) and positive rental_income. ssi, child_support_received and financial_assistance were already pinned (Cases 7 and 11).
Net of Medicare (4 of 4). Case 14, including military_retirement_pay_survivors.
Bills (7 of 7). tn_liheap.yaml Case 22 (line 546) outputs tn_liheap_energy_cost 2,700 from electricity 1,200, fuel oil 800, wood 400, coal 200 and other 100, and pays $700.
Clip. Case 23 (line 574) puts a 17-person FY2027 Tennessee unit beside a one-person Kentucky unit. Core runs the formula for both before applying defined_for. Dropping the upper bound of clip(size, 1, 16) (V/tn_liheap.py:84) asks for row 17, and dropping the lower bound asks Kentucky's size 0 for row 0. Both raise missing-key errors.
Axiom. Companion tests 9(f), 12(h)-(j) and 14(g) carry the new cases.
G1. Retirement-account withdrawals Declined with a recorded ruling; accepted Ruling. The author cites a ruling of 2026-10-23 that follows the manual's income list. candidates.yaml records us-tn:liheap-retirement-account-withdrawals as state: ruled, with the round-1 example ($710 excluded, $250 counted). I re-derived both amounts.
Why it is enough. The choice is between a plan checkbox and the manual, not a regulation. A reviewed ruled entry satisfies the wave rule.
G2. Record the source choices as candidates Fixed us-tn:liheap-fy2026-printed-income-ceilings and us-tn:liheap-fy2027-large-household-limits (both unreviewed) and the G1 entry use follows: gap-fill and code: [], and list this PR. They sit between us-sd: and us-tx:, with no duplicate ids. I checked their examples:
  • size 8 FY2026: 4,000 / 85,064.50 = 0.0470, column 1, sizes 6-8 group D = $300;
  • size 13 FY2027: 4,200 / 126,000 = 0.0333, column 1, above the $98,209.50 group C top, 9+ group D = $540.
G3. Base the propane minimum on a propane bill (i) Fixed; (ii) kept and documented, accepted (i) V/tn_liheap.py:99-103 applies the FY2026 minimum when heating_type is PROPANE or bottled_gas_expense > 0. Case 24 (line 655: electric heat, electricity $1,000, propane $200, wages $20,000) expects $500, against $250 without the bill trigger.
(ii) Propane heat in the rent keeps the minimum. Case 25 (line 679) pins $500 (matrix $174) and is also the only case that pins the propane_heat term alone: Cases 12 and 13 have propane bills.
Support. Manual p. 32 lets the benefit go to "a different energy vendor upon the applicant's request ... provided the account is active". The LI-05 form (one page, revised April 2026) records the landlord's "Energy Vendor" and "Energy Bill Account #" and reduces rent "until the approved benefit is depleted". The reading is plausible, and P/payment/propane_minimum.yaml:2-9, the body and Axiom interpretation 10 document it.
G4. Small documentation fixes (a), (b) and (d) fixed; (c) declined, accepted (a) The tn_liheap documentation (V/tn_liheap.py:11-23) gives the manual's months (p. 35) and the plan's component dates, and the plan href is #page=4 with # PDF pages 4, 9.
(b) P/income/interest_threshold.yaml and the formula comment say 2.13 names no unit and the model applies $200 to each member.
(c) Optional in round 1; the cited draft plan still loads with the same 2.1 table.
(d) Case 23, as under S3.
G5. Earlier years Fixed The tn_liheap documentation and the body now say that earlier years backfill FY2026 values and do not reproduce the pre-FY2026 priority-points matrix. verified_years stays "2026-2027".

New findings

Critical

None.

Should address

None.

Suggestions

G-N1. Housekeeping: tick "CI passes". The body's test-plan checklist still has "CI passes" unchecked, but all 35 checks are green at this head. Its line saying that the round-1 cases were "not run locally; CI validates them" is now confirmed.

Verified

Hand-checks of every new or changed expectation. FY2026 size-1 tops are A 11,111.50, B 13,615.50 and C 18,310.50 (0.71, 0.87 and 1.17 × 15,650), and the limit is 32,053.

  1. Eligibility Case 5. 1,680 / 15,080 = 0.111406, rounded to 0.1114, is column 5 (0.0998-0.1214). 15,080 is group C. Sizes 1-2 group C column 5 = 445, eligible.
  2. Countable Case 12. 10,000 + 1,000 + 2,000 + 3,000 + 4,000 = 20,000.
  3. Countable Case 13. 1,000 + 2,000 + … + 11,000 = 66,000. SSI and TANF are 0 for a childless 50-year-old with this income.
  4. Countable Case 14.
    • Person 1: 16,000 + 4,000 − (0 + 2,434.80) = 17,565.20.
    • Person 2: (8,000 + 2,000) + 1,000 − (3,732 + 2,434.80) = 4,833.20.
    • Total 22,398.40.
  5. tn_liheap Case 22. The bills sum to 2,700. 2,700 / 15,080 = 0.179045, rounded to 0.1790, is column 7 (0.1529-0.2181). Group C column 7 = 700.
  6. Case 23 (FY2027).
    • Size 17 and 0.
    • Limit: floor(1.5 × (15,960 + 5,680 × 16)) = floor(1.5 × 106,840) = 160,260.
    • Energy cost 1,800 + 3,000 = 4,800.
    • Income 80,000 sits between the size-16 printed tops A 71,312.50 and B 87,382.50, so group B. 4,800 / 80,000 = 0.06 is column 2. The 9+ group B column 2 FY2027 cell is 605.
    • Kentucky: 0, 0, 0 and 0.
  7. Case 24. 1,200 / 20,000 = 0.06 is column 2. 20,000 > 18,310.50, so group D: 250, raised to 500.
  8. Case 25. Cost 0, so the burden is 0 and the column is 1. Group C gives 174; propane heat raises it to 500. Eligible through heat in the rent.
  9. Unchanged but re-traced under the new code.
    • Countable Case 5 (21,565.20; Part A $0).
    • Eligibility Case 7 (false, through known_fuel).

Fix-commit checks:

Merge resolutions (both blocks):

  • programs.yaml coverage line.
    • The head's line is the merge base's 31 entries (AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MS, NC, ND, NE, NH, NJ, NY, OH, OK, OR, SD, RI, TX, UT, VA, WA, WY) with TN inserted before TX. No sibling was dropped or duplicated.
    • Against the merge base, the only programs.yaml changes are this line and the TN state_implementations entry, between SD and TX.
  • source_conflicts/candidates.yaml.
    • The head keeps main's three us-sd:ssp-* entries, identical to the merge base, followed by the three us-tn: entries.
    • Against the merge base, the file's only change is the 98 added Tennessee lines. Entry counts are 283 at the base, 286 at the head and 294 on the tree merged with current main (291 + 3). There are no duplicate ids.

Other checks:

  • Sources. I re-read the 2026 manual pages behind the new text:
    • p. 8: "Active Account - A current utility account that reflects billing and usage";
    • p. 25: the propane option and the $500 minimum;
    • p. 32: energy in rent remains eligible with the landlord/tenant agreement, and the benefit can go to another active vendor account;
    • p. 33: no active account, not eligible.
    • The LI-05 is a one-page form, so its reference needs no page anchor.
  • References. The new and changed titles follow the house form: "#page 25,32" and "#page 8,32-33", with # PDF pages comments on the variable tuples.
  • Main since the merge base. None of main's 8 newer commits touches an input or guard this PR reads. They are Alaska LIHEAP, the Maine EITC age gate, the owners-draw SECA reform (which changes ss_aime only under that reform) and the self-employed health insurance deduction limit.
  • Case count. 25 + 10 + 7 + 14 = 56, matching the body, the Axiom issue and CI.
  • Body. It describes this head: the energy-account test, the net_of_medicare netting, the propane bill and heat-in-rent minimum, the three candidates, the backfill note and 56 cases.

Axiom

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

  • State. OPEN, labelled pe-parity, last updated 2026-10-29 13:44 UTC, after the fix commit.
  • It matches the head:
    • It names head 5fc125e88a, fix commit fef2e3f259 and merge base c55b806541, and lists the 56 cases by file.
    • Its required outputs give the new eligibility test (energy cost > 0 with a known fuel, or heat in rent), tn_liheap_energy_cost, the per-member net-of-Medicare sum and the propane trigger.
    • Interpretations 8, 10 and 13 record the S1, G3 and S2 readings, and 2, 3 and 13 name the three candidates.
    • Companion tests 9(a), 9(f), 9(h), 10(c), 12(h)-(k) and 14(g) carry the new cases with the same expectations, plus the round-1 RRB award.
  • Dispatch-ready.
    • It has module paths with corpus status, verbatim source text, required outputs and companion tests from the matrices, plans and manual.
    • Its blockers are listed: there is no reviewed oracle revision, and the workshop deck, draft FY2027 plan and manual, and IM are not in the corpus.

CI

35 pass, 0 fail, 0 pending. Run 40792560693 (pull_request) tested head 5fc125e88a, which includes main at c55b806541. I re-checked gh pr checks 10430 at the end of this review.

  • Tennessee tests. Baseline states-shard-1 ran all four files: 10 + 7 + 14 + 25 = 56 cases, all passing. Quick Feedback ran the same 56.
  • Guards. Rest (Python shard 3) passed 3,082 tests and skipped 11, including the source-conflict ratchet and candidate checks, the unemployment, military and Social Security guards, and the programs registry test.
  • Main-caused failures. None on this run.
  • Locally. I ran no tests or model probes. The examples are hand traces of the head. The only local work was the static comment-pattern scan and reading the manual and the LI-05 form.

Branch

  • Position. 4 commits ahead of main (1976c4b600) and 8 behind; the merge base is c55b806541. GitHub reports the PR as conflicting.
  • Merge: one conflict, the programs.yaml LIHEAP coverage line (branch note). git merge-tree --write-tree PolicyEngine/main 5fc125e88a reports only this hunk.
    • main added AK there (Add Alaska LIHEAP regular heating assistance #10341), and this PR adds TN.
    • The resolution is the union: AK, AR, AL, AZ, DC, GA, Riverside County, MA, FL, IL, IA, IN, KS, KY, MD, MS, NC, ND, NE, NH, NJ, NY, OH, OK, OR, SD, RI, TN, TX, UT, VA, WA, WY.
    • The head's own merge was correct when it was made: AK was not on main at c55b806541.
  • Auto-merges.
    • The state_implementations entries merge cleanly, with AK after AL and TN after SD, each once.
    • candidates.yaml also merges cleanly, with main's seven us-ak:hap-* entries and the three Tennessee entries and no duplicate ids.
  • Siblings. Other open LIHEAP PRs, such as Nevada Add Nevada Energy Assistance Program Fixed Annual Credit #10432 and Vermont Add Vermont Seasonal Fuel Assistance #10436, also edit the coverage line, so whichever lands next needs the same one-line union.

0 critical, 0 should and 1 suggestion are open at this head.

Verdict: APPROVE

@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.

Approving round 2: 0 critical, 0 should. All three round-1 shoulds are fixed and pinned, and no benefit path can go below zero. Full report in the comment above. Branch note: AK #10341 merged, so the coverage line needs AK + TN before landing.

…p-wave1d

# Conflicts:
#	policyengine_us/programs.yaml
@hua7450
hua7450 merged commit cce379e into main Oct 30, 2026
35 checks passed
@hua7450
hua7450 deleted the tn-liheap-wave1d branch October 30, 2026 04:02
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 Tennessee LIHEAP regular heating assistance

2 participants