Skip to content

Add Louisiana LIHEAP regular heating assistance - #10429

Merged
hua7450 merged 7 commits into
mainfrom
la-liheap-wave1d
Oct 30, 2026
Merged

hua7450 merged 7 commits into
mainfrom
la-liheap-wave1d

Conversation

@hua7450

@hua7450 hua7450 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Louisiana LIHEAP regular heating assistance for FY2026, administered by the Louisiana Housing Corporation (LHC). The model counts citizens and qualified noncitizens in household size (undocumented members' income still counts), tests gross countable income against 60% of state median income with the federal 110% poverty floor, divides the household's total energy cost by its income to get an energy burden, and pays the FFY2026 matrix amount for that burden band and household size ($200 to $700), plus one $100 supplement when a counted member is 60 or older, disabled, or age five or younger. A member who receives Social Security disability benefits or SSI falls in the disability group, because the guide accepts proof of disability from the Social Security Administration and SSI goes only to people who are 65 or older, blind or disabled. Households with zero income receive the maximum for their size.

la_liheap is Louisiana's heating-season award. Louisiana also pays a separate summer cooling award from the same matrix, and the FY2026 plan gives cooling 45.5% of funds against 25% for heating. A household may receive one heating and one cooling award each program year, so its program-year regular assistance can be twice la_liheap. The cooling award is out of scope: the all-state LIHEAP implementation covers heating assistance only.

Fixes #10419

Regulatory authority

Eligibility

Requirement Source How modeled
Louisiana residence Plan defined_for = StateCode.LA
At least one citizen or qualified noncitizen Guide p.33-34 la_liheap_household_size sums is_citizen_or_legal_immigrant; size 0 is ineligible
Income at or below 60% of SMI Plan 2.1; Guide p.29, 33; Notice LHP-2025-01 la_liheap_income_limit uses liheap_smi_limit, which reproduces all ten published rows; larger sizes follow the federal size factors
Federal 110% poverty floor 42 U.S.C. 8624(b)(2)(B) 2025 guidelines in FY2026; binds from size 14
Responsibility for energy costs Guide p.35 has_heating_expense or heat_expense_included_in_rent; NONE is ineligible, UNSPECIFIED only with heat in rent
Subsidized housing Guide p.39-40; Plan 2.3 A household is subsidized when receives_housing_assistance or is_in_public_housing is true. The HUD utility allowance is subtracted from its energy cost unless a counted member is 60 or older (the guide's "60 years or older", followed over plan 2.3's "over 60"), and it needs a positive energy cost

Countable income

Item Source How modeled
Earned income Guide p.29-30; Appendix E p.82, 85 Wages and net self-employment, each floored at zero, for members 18 and older who are not in high school (is_in_k12_school)
Unearned income Guide p.29-30; Appendix E p.81, 83-86 Pensions and annuities, military retirement (with survivor annuities), railroad, survivor, veterans, workers' compensation, disability, unemployment, strike benefits, alimony, interest, dividends, rental, estate and trust income, and regular contributions (financial_assistance), for members 18 and older (the guide counts "any income of a household member age 18 and older")
Social Security and SSI Guide p.30; Appendix E p.85; FAQ A4; Plan 1.9 Counted for members of any age (Appendix E counts benefits "paid to minors"); Social Security net of medicare_part_b_premium, floored at zero per person
Gambling Appendix E p.84 Winnings less losses, floored at zero, for members 18 and older
FITAP Guide p.29 Take-up-gated tanf
Not counted Guide p.30; Appendix E Child support, capital gains, bank and retirement-account withdrawals, tax refunds, SNAP, one-time lump sums

Benefit

Energy burden Sizes 1-3 Sizes 4+
9.9% and less $200 $250
10% to 17.9% $350 $400
18% to 24.9% $500 $550
25% and greater $650 $700
  • Energy burden is total energy cost over countable income (Guide p.41; Plan 2.4). Total energy cost adds every residential energy bill (electricity, gas, propane, fuel oil and kerosene, wood, coal, other fuels) less the utility allowance where it applies (Guide p.37-40).
  • The burden is compared unrounded with cutoffs at 10%, 18% and 25%, so 9.95% stays in the lowest band. LHC does not publish its rounding.
  • One $100 priority supplement per household (Matrix; Guide p.41). The range is $200 to $800, matching Plan 2.6. There is no cap at the bill.
  • A counted member is in the disability group when is_disabled is true, when social_security_disability is above zero, or when the member receives SSI (ssi above zero or a reported receives_ssi). Guide 4.2 A says disability "can be proven through the Social Security Administration", and SSI goes only to people who are 65 or older, blind or disabled (42 U.S.C. 1382c(a)(1)(A)). Aged recipients already qualify by age, and every SSI recipient under 60 is blind or disabled, including those who keep SSI while working under section 1619(a). Receipt of veterans' benefits or workers' compensation does not by itself place a member in the group.

Not modeled

Item Source Treatment
Landlord-stated heating cost for heat included in rent Guide p.35-36; Appendix L No input. These households are eligible and their burden counts only separately billed energy, so their band is a lower bound
Application-month bill and 30-day income Guide p.31-32, 37 Annual amounts stand in for the application-month bill. For electric bills they overstate a heating-season month in Louisiana: residential electric revenue in January-March and November-December averaged 0.857 (2024) and 0.863 (2025) of the annual monthly average (EIA Form EIA-861M). For gas bills they understate it, because residential gas use peaks in winter (EIA Louisiana residential gas consumption)
Bill line items (customer charges, franchise fees, arrears, deposits) Guide p.37-38 Inputs are bill totals
Louisiana utility allowance schedule Guide p.39-40 hud_utility_allowance has no Louisiana schedule and is zero unless supplied; a supplied allowance should cover the heating and cooling utilities only
Credit balances, one award per season, discretionary supplemental payments Guide p.38, 42 Not represented
Royalties, jury pay, military allotments, training stipends, informal income Guide p.29-30 No existing inputs
SSN verification and the April 2026 household-member exclusions Guide p.29; April 2026 guide p.33 No inputs
Summer cooling award, crisis, weatherization Plan 1.1-1.2; Guide p.42 Out of scope; the all-state LIHEAP implementation covers heating assistance only. Cooling is the larger regular component (45.5% of FY2026 funds against 25% for heating) and uses the same matrix

FFY2027: LHC has posted only a proposed FFY2027 plan (its matrix is unchanged); no adopted plan or income notice exists yet, so 2027 results are unverified estimates. Earlier years are unverified estimates through parameter backfilling.

The FY2026 plan's section 1.9 checks one-time lump sums and leaves commissions and cash gifts unchecked; the Service Delivery Guide, which governs case processing, counts only net gambling lump sums, counts commissions and regular contributions, and is followed. source_conflicts/candidates.yaml records this choice as us-la:liheap-plan-income-checklist and the age-60 utility allowance exemption as us-la:liheap-utility-allowance-age; each entry lists the files that implement it.

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

Test plan

55 YAML cases in 5 files: la_liheap.yaml 17, la_liheap_energy_cost.yaml 5, eligibility/la_liheap_eligible.yaml 15, eligibility/la_liheap_income_limit.yaml 5 and income/la_liheap_countable_income.yaml 13. CI passed the other 54 cases at the previous head, a85f9738ae. la_liheap Case 17, added after the second review, has a hand-derived expected value ($750) and was not run locally; CI has not yet run at the current head, 57b8cfdc24. No earlier expected value changed; la_liheap Case 16 changed only in its comments.

  • income limits at sizes 1, 4, 10 and 14, the 110% floor at sizes 13 and 14, and the exact-limit boundary;

  • countable income: minor and high-school earnings, a minor's Social Security and SSI counted while the minor's interest and dividends are not, Social Security net of Part B, net gambling, FITAP, SSI, regular contributions, excluded sources, undocumented members' income, and a distinct positive amount for every listed earned and unearned source of an adult (SSTB, farm and partnership income; military survivor annuities, reported unemployment, railroad, veterans, disability, survivor, workers' compensation, alimony, rental, strike, farm rent, estate, dividends and interest);

  • energy cost across fuels, the utility allowance with and without a 60-year-old, a negative energy cost;

  • every burden band edge, sizes 3 and 4, zero income, each priority group once, the supplement without is_disabled for a Social Security disability recipient, an SSI recipient, and a blind member whose SSI receipt is reported through receives_ssi with no SSI amount, heat in rent, subsidized households, and a public housing tenant whose utility allowance exceeds the bill.

  • YAML tests with expected values from the plan, matrix, notice and guide

  • ruff format

  • Changelog fragment in changelog.d/

  • CI passes

axiom: TheAxiomFoundation/rulespec-us#1890 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 (de00f2a).
⚠️ Report is 67 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main    #10429    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            1         6     +5     
  Lines           15       124   +109     
  Branches         0         2     +2     
==========================================
+ Hits            15       124   +109     
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 26, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Louisiana LIHEAP regular heating assistance

PR #10429 (author: hua7450), head 7c35019e79. This is a first review. The PR adds the Louisiana Housing Corporation (LHC) LIHEAP heating-season award for FY2026: 12 parameters, 6 variables, 5 test files (49 cases), a programs.yaml entry (coverage line and state list) and a changelog fragment. The only comment is codecov's, and the PR touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/la/lhc/liheap/
  • V/ = policyengine_us/variables/gov/states/la/lhc/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/la/lhc/liheap/

Sources read for this review:

Summary

What is right. The published FY2026 tables and rules are encoded correctly:

  • Income limits. floor(floor(0.6 × $98,137) × size factor) reproduces all ten rows of Notice LHP-2025-01. It also reproduces all ten FFY2025 rows from $95,801, so the unclaimed 2025 backfill is right on limits.
  • Benefits. The matrix is right: 8 cells, bands at 10%, 18% and 25%, sizes 1–3 and 4 or more. So is the single $100 priority supplement (60 or older, disabled, five or younger), which with the matrix reproduces plan 2.6's $200–$800. Zero income gets the size maximum.
  • Total energy cost. Every bill is added, electricity included, so New Mexico's Should 1 does not recur. The utility allowance is subtracted for subsidized households, and applicants 60 or older are exempt. A total energy cost of zero or less makes a subsidized household ineligible.
  • Countable income follows the guide and Appendix E:
    • gross wages and net business income for members 18 and older not in high school;
    • unearned income for adults;
    • Social Security net of Part B and SSI for every member ("benefits paid to minors"; FAQ A4);
    • net gambling, FITAP through tanf, per-source loss floors, and undocumented members' income.
  • Federal floor. The 110% floor is applied strictly ("less than"); it first binds at size 14.
  • Tests and batch checks. I hand-checked all 49 cases below, and CI passed them all. The guards all pass: 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 (on the merged tree too).

Scope. Louisiana runs two regular components on the same rules and matrix:

  • Heating runs November 15 to March 15 and gets 25% of FY2026 funds.
  • Cooling runs April 1 to September 30 and gets 45.5%.
  • A household may receive one award from each. The PR models the heating award and documents cooling as out of scope. That is the wave's documented-deferral pattern, so I rate it a suggestion (G1), though cooling is the larger component.

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

  • S1. Untested income sources. 17 of the 21 income-source entries never take a positive counted value in any test. Neither does the public-housing branch of eligibility.

There are also four suggestions:

  • G1: model or flag the larger cooling component.
  • G2: the "winter bills usually exceed the annual average" claim is backwards for all-electric homes.
  • G3: count disability-based benefit receipt toward the supplement.
  • G4: record the plan-vs-guide income choices in the backlog.

Status.

  • CI: 29 pass and 3 skipping, with none failed. All 49 Louisiana cases passed in states-shard-4.
  • Branch: 2 commits ahead of main and 39 behind. It merges cleanly.
  • Siblings: this PR adds LA to the LIHEAP coverage line, so it conflicts with 15 open LIHEAP siblings in programs.yaml only (branch note).

Critical (must fix)

None.

Should address

S1. 17 of the 21 income-source entries, and the public-housing branch of eligibility, have no test that would fail if they were dropped

  • Where:
    • P/income/sources/earned.yaml (5 entries) and P/income/sources/unearned.yaml (16 entries), read at V/income/la_liheap_countable_income.py:34-38;
    • V/eligibility/la_liheap_eligible.py:53-54 (| is_in_public_housing).
  • Untested entries. No case gives these a positive counted value:
    • Earned: sstb_self_employment_income, farm_operations_income and partnership_s_corp_income. The last is set only to −$1,500 in countable Case 1, which floors to 0, so dropping the entry changes nothing.
    • Unearned:
      • military_retirement_income, railroad_benefits, survivor_benefits, veterans_benefits, workers_compensation, disability_benefits, total_unemployment_compensation, strike_benefits, alimony_income, farm_rent_income and estate_income;
      • rental_income, set only to −$1,800, which floors to 0;
      • interest_income and ordinary_dividend_income, set only for the 16-year-old in countable Case 11, whose unearned income is excluded.
  • Tested entries: wages, self-employment ($3,000), pension_income and financial_assistance. Social Security, SSI, gambling and tanf are also pinned.
  • The floors and age rules are pinned. Removing the earned or unearned per-source max_ fails countable Case 1. Removing the gambling or Social Security floor fails Cases 6 and 5. Removing the adult multiplier fails Cases 2 and 11.
  • The public-housing branch. is_in_public_housing is pinned only in la_liheap_energy_cost.yaml Case 3. If it were dropped from la_liheap_eligible, a public-housing tenant whose allowance exceeds the bill would become eligible: the burden floors at 0, so band 1 pays $200 instead of $0. No case would fail.
  • Severity: should, as in Add New Mexico LIHEAP regular heating assistance #10431 S3, Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 S4 and Add Alabama LIHEAP regular heating assistance #9967 S3 (the mutation rule).
  • Fix:
    • Add one countable-income case that gives every untested source a distinct positive amount for an adult, including reported unemployment and military_retirement_pay_survivors.
    • Re-pin partnership_s_corp_income and rental_income with positive amounts.
    • Add an interest or dividend amount for an adult.
    • Add a la_liheap_eligible (or la_liheap) case with is_in_public_housing and an allowance above the bill, expecting false (or $0).

Suggestions

G1. Model the cooling award too, or say plainly that la_liheap is the smaller of Louisiana's two regular awards.

  • The program.
    • Two components. Plan 1.1 (p. 4) runs Heating assistance 11/15/2025–03/15/2026 and Cooling assistance 04/01/2026–09/30/2026.
    • Funding. Item 1.2 allocates 25.00% to heating and 45.50% to cooling.
    • One award from each. The guide says "Applicants may be eligible to receive one Heating and one Cooling LIHEAP benefit each Program year" (4.2 B, p. 42; April 2026 edition, p. 48).
    • The same rules. Section 3 of the plan (pp. 11–12) repeats the 60% limit, the utility allowance rule, the energy burden, the $100 supplement and $200–$800. Both seasons use the single "Heating and Cooling" matrix.
    • FFY2027. The proposed plan keeps the 25% / 45.5% split and says "Clients are eligible once for each of the 4 components."
  • Effect (2026, la_liheap.yaml Case 7: age 60, pension $14,400, electricity $1,740). The heating award is $350 + $100 = $450, which the model returns. The same household's cooling award is another $450, so the program-year regular benefit is $900.
  • Why only a suggestion. Each household's heating award is right. Cooling is a separate component, and the body, the la_liheap documentation and the registry note all say it is out of scope, as merged KY does. No aggregate reads la_liheap.
  • Fix, either:
    • add a la_liheap_cooling that reuses the eligibility, income, energy cost and matrix. Without the heating-fuel gates, a NONE household with an electric bill would qualify there, which is right for cooling.
    • or extend the registry note and body to say cooling is the larger component (45.5% of funds against 25%) and that the program-year regular benefit can be twice la_liheap.

G2. The body's "winter bills usually exceed the annual average" is backwards for Louisiana's all-electric households.

  • Where.
    • The body. Its "Not modeled" row reads "Annual amounts stand in; winter bills usually exceed the annual average". Axiom interpretation 1 says the same.
    • The parameter comment. P/payment/energy_cost_sources.yaml:2-3 says the annual ratio "gives the same burden". That holds only when the application-month bill equals the annual average.
  • The source. The guide uses the monthly TEC from bills "not more than thirty (30) days old" (4.1, p. 37). For the heating award, those are November–March bills.
  • The data. In EIA's Louisiana residential revenue, November–March averaged 0.86 of the annual monthly average in both 2024 ($259.2M against $302.8M) and 2025 ($285.4M against $330.7M). July and August ran 1.34–1.40. Gas bills peak in winter, so the claim holds for gas-heated homes but not for all-electric ones.
  • Example (2026, single adult aged 40, wages $15,080, all-electric, $1,740 a year):
    • Model: 1,740 / 15,080 = 11.54%, so band 2 and $350.
    • A typical heating-season bill: 0.86 × $145 = $124.70 against $1,256.67 a month is 9.92%, "9.9% and Less", so $200.
  • Why only a suggestion. The annual approximation itself is accepted wave practice (Add New Mexico LIHEAP regular heating assistance #10431's highest-bill measure). Only the stated direction of its error is wrong, and the claim does hold for gas-heated homes.
  • Fix: reword the row, the comment and interpretation 1. For example: "annual averages approximate the application-month bill; for electric bills they overstate a heating-season month in Louisiana (EIA 2024–2025: about 0.86 of the annual average), and for gas bills they understate it."

G3. Count disability-based benefit receipt toward the priority supplement.

  • Where: V/la_liheap.py:53 reads only is_disabled. Axiom interpretation 8 says it "stands in for documented disability".
  • The source. The guide gives the $100 for "Persons who are disabled" and says "Disability can be proven through the Social Security Administration, Veteran Affairs, Worker Compensation, or any other medical approved documentation" (4.2 A, p. 41; April 2026 p. 48). An SSDI award letter is that SSA proof.
  • Example (2026, age 45, SSDI $14,400, is_disabled unset, in the Medicare waiting period so Part B is $0, electricity $1,740). 1,740 / 14,400 = 12.08%, so band 2:
    • Head: $350.
    • With SSDI counted: $450.
  • Why only a suggestion. Louisiana names a status, "persons who are disabled", which is_disabled encodes directly, and the guide lists proofs rather than adopting a benefit-receipt test. Add New Mexico LIHEAP regular heating assistance #10431 S4 (New Mexico, where receipt alone entitles) and Add Oklahoma LIHEAP regular heating assistance #9970 S3 were should items for that reason. Merged FL also reads is_disabled alone.
  • Fix: use is_disabled | (social_security_disability > 0) | ((ssi > 0) & is_ssi_disabled), as va_eap_disabled does. Do not use is_usda_disabled, which also counts aged SSI. Add an SSDI case without is_disabled.

G4. Record the plan-vs-guide income choices in source_conflicts/candidates.yaml.

  • The choices. The body and the comments in V/income/la_liheap_countable_income.py:43-46 follow the guide over plan 1.9 (p. 6) in four places:
    • Lump sums. The plan checks one-time lump sums; the guide counts only net gambling and lottery winnings.
    • Cash gifts. The plan leaves cash gifts unchecked; the guide counts regular contributions.
    • Commissions. The plan leaves them unchecked; the guide counts them as earned income.
    • The utility allowance exemption. Plan 2.3 says "over 60"; the guide says "60 years or older".
  • What the PR says. Axiom interpretation 12 says no candidate was added "since no statute is on either side".
  • The precedent. main already records the same kind of conflict without a statute. us-al:liheap-lump-sums-and-refunds (manual vs plan 1.9, "No statute is supplied") and us-sd:liheap-cash-gifts are candidates.
  • Fix: add one or two us-la:liheap-... candidates, with pe_us.follows, the code paths and prs listing this PR. The guide is the right choice, so no code change is needed.

Verified correct

Values against sources:

  • Income limit (plan 2.1, 60% SMI, p. 9; guide 3.6 C.1, p. 29; April 2026 p. 34):
    • FFY2026. floor(0.6 × 98,137) = 58,882, then × 0.52 / 0.68 / 0.84 / 1.00 / 1.16 / 1.32 / 1.35 / 1.38 / 1.41 / 1.44, floored, gives 30,618, 40,039, 49,460, 58,882, 68,303, 77,724, 79,490, 81,257, 83,023 and 84,790. All ten match Notice LHP-2025-01.
    • FFY2025 from $95,801: 29,889, 39,086, 48,283, 57,480, 66,676, 75,873, 77,598, 79,322, 81,046 and 82,771. All ten match LHP-2024-01.
    • The comparison. "Must not exceed" is <=.
  • Federal 110% floor (42 U.S.C. 8624(b)(2)(B); IM 2025-02 Att. 2, $17,215 for one person):
    • strict <, on the counted size;
    • size 13: limit 90,089 > floor 89,815;
    • size 14: floor 95,865 > limit 91,855;
    • the floor binds from size 14 up, as the body says.
  • Matrix: 200 / 250, 350 / 400, 500 / 550 and 650 / 700 for sizes 1–3 / 4+, plus $100 once for 60 or older, disabled, or five or younger. The maximum is $800, matching plan 2.6 and the matrix footnote.
    • Zero income. The size maximum, per guide 4.2 (p. 41) and plan 2.4 (p. 10).
    • Bands. The lower-inclusive cutoffs (truncating the percent to one decimal) are a documented reading, Axiom interpretation 2.
  • Total energy cost (guide 4.1, pp. 37-38; April 2026 pp. 43-44):
    • All seven bill inputs are added, electricity included. Fuel type is not a benefit variable (plan 2.5 leaves "Fuel type" unchecked).
    • Water and sewer are excluded.
    • Line items cannot be separated from bill totals; documented.
  • Utility allowance (plan 2.3; guide 3.6 E.1 and 4.1 C, pp. 35, 40; April 2026 pp. 41, 46-47):
    • It is deducted when subsidized (receives_housing_assistance or is_in_public_housing), and a member 60 or older exempts the household.
    • A negative TEC is ineligible ("If the TEC is a negative amount, the household is not eligible"). A TEC of zero is read as "no energy burden".
    • Documented readings, accepted: no input names the applicant, although the April 2026 guide defines one (p. 33); the guide's "60 years or older" is followed over plan 2.3's "over 60"; a supplied allowance should cover heating and cooling only.
  • Electricity and the New Mexico failure modes (hand traces, 2026, single adult aged 40, wages $12,000, electricity $1,080):
    • (a) A subsidized tenant with gas heat in the rent who pays the electric bill: responsible through heat in rent, TEC 1,080 > 0, 9.0%, $200.
    • (c) The same tenant unsubsidized: also $200.
    • (b) A NONE household is excluded, which is right for a heating award; it belongs to the cooling component (G1).
  • Eligibility:
    • no categorical eligibility (plan 1.4 "No", p. 5);
    • no assets test (2.3);
    • at least one citizen or qualified noncitizen, with size counting only those members while undocumented members' income still counts (guide 3.6 D and D.3, pp. 33-34; April 2026 pp. 40-41);
    • responsibility through a primary-fuel bill or heat in rent (3.6 E);
    • UNSPECIFIED only with heat in rent. heating_type is a required input, settled in Add Virginia EAP Fuel Assistance (LIHEAP regular heating) #10303 G2.
  • Countable income (guide 3.6 C.1, pp. 29-30; Appendix E, pp. 81-86; plan 1.8-1.9, pp. 5-7, rendered):
    • Gross basis. Gross income (1.8). Wages gross. Self-employment net and floored per source.
    • Unearned list. Pensions and annuities, military retirement (with SBP), railroad (guide: "Gross Railroad retirement"), survivor, veterans, workers' compensation, employment disability, unemployment, strike, alimony, interest, dividends, net rental, estates and trusts, and regular contributions.
    • Exclusions. Child support, capital gains, bank and retirement-account withdrawals, tax refunds and SNAP are excluded. General assistance is not counted (plan box unchecked, not in the guide).
    • Minors and students. Earnings of minors and of 18-year-olds in high school are excluded (Appendix E p. 82; April 2026 p. 89). Social Security and SSI count at any age: the Appendix E row "Social Security Benefits (SS, SSD, SSI)" includes "benefits paid to minors" (p. 85, rendered), and FAQ A4 says "count the social security income that the parent is receiving on behalf of their minor child".
    • Social Security net of Part B is correct here. The guide counts "Net Social Security Administration (SSA) benefits", and plan 1.9 checks "Excluding MediCare deduction" (p. 6, rendered). medicare_part_b_premium is the enrollee's out-of-pocket premium net of MSP, floored per person. That matches the wave's Medicare precedent, and the Part B-only reading follows the Iowa precedent for check-withheld deductions.
    • FITAP counts once through the take-up-gated tanf, which includes la_fitap.
    • No inputs exist for royalties, jury pay, military allotments, training stipends or KCSP; documented.
  • References (item 7):
    • Every anchor I opened shows its value: plan pp. 5-7, 9 and 10; 2024 guide pp. 29-30, 33, 35, 37-38, 40-41, 82 and 84-85; April 2026 guide pp. 34-35, 39-41, 43-44, 46-48, 89 and 91-92; FAQ p. 2; IM Att. 2 p. 1.
    • Multi-page titles use the quoted #page 29,82,85 form with the href on the first page, and variables use # PDF pages comments.
    • The two guide editions are separate entries, as separate documents.
  • Descriptions (item 6): all 12 end "under the Low Income Home Energy Assistance Program." with nothing after it.
  • Years:
    • 2026 is verified.
    • 2025 is an unclaimed backfill, and the published FY2025 items match: the plan's 60% SMI, $200–$800, the $100 supplement and zero-income maximum, and all ten LHP-2024-01 rows. The FY2025 matrix is not on the Clearinghouse.
    • 2027 carries forward. Only a proposed FFY2027 plan exists (status "Saved -- with Errors"); it keeps 60% SMI and says "No Changes." to the matrix, and LHC links no FFY2027 income notice. The body says all of this, and the registry claims only 2026.

Hand-checked expectations (all 49 cases). All are 2026, with a size-1 limit of 30,618:

  • Eligibility Cases 1–2: 30,618 ≤ 30,618 is true; 30,619 is above the limit and the $17,215 floor, so false.
  • Eligibility Cases 3–5: size 13, 90,090 exceeds limit 90,089 and floor 89,815, so false. Size 14, 95,864 < 95,865 = 1.1 × (15,650 + 5,500 × 13), so true. At 95,865, false.
  • Eligibility Cases 6–9: an electric bill of $0 with heat not in rent is false. NONE with heat in rent is false. UNSPECIFIED with heat in rent is true; without it, false.
  • Eligibility Cases 10–12: TEC 1,740 − 1,200 = 540 gives true. 1,740 − 1,740 = 0 gives false. Age 60 is exempt, so TEC 1,500 gives true.
  • Income-limit Cases 1–5: 30,618 (size 1, the undocumented member not counted); 58,882; floor(58,882 × 1.44) = 84,790; floor(58,882 × 1.56) = 91,855; size 0 gives 0.
  • Countable Case 1: 15,080 + 3,000 + max(−1,500, 0) + max(−1,800, 0) = 18,080.
  • Countable Cases 2–3: 30,000 + 6,000 = 36,000 (the 17-year-old excluded); 30,000 (the 18-year-old in high school excluded).
  • Countable Case 4: 15,080 + 12,000 (the child's Social Security; no Part B under 65) = 27,080.
  • Countable Case 5: (24,000 − 2,434.80) + 6,000 + max(1,800 − 2,434.80, 0) = 27,565.20.
  • Countable Cases 6–8: 20,000 + 1,500 + 0 = 21,500; 15,080 (child support, capital gains and the IRA withdrawal excluded); tanf 5,808.
  • Countable Cases 9–11: 15,080 + 20,000 = 35,080; 11,928 + 1,200 = 13,128; 30,000 + 6,000 SSI = 36,000 (the minor's interest and dividends excluded).
  • la_liheap.yaml Cases 1–2: 1,799 / 18,000 = 0.09994, band 1, $200; 0.10, band 2, $350 (age 59 adds nothing).
  • Cases 3–4: (1,200 + 2,400) / 20,000 = 0.18, band 3: $500 at size 3 (age 6 adds nothing) and $550 at size 4.
  • Cases 5–6: 2,000 / 8,000 = 0.25 gives $650; zero income at size 4 gives $700.
  • Cases 7–9: 1,740 / 14,400 = 0.1208 gives 350 + 100 = $450. 2,400 / 11,928 = 0.2012 gives 500 + 100 = $600 (size 1, income 11,928, TEC 2,400). 1,740 / 15,080 = 0.1154 at size 2, with the 5-year-old, gives $450.
  • Case 10: 1,740 / 19,080 = 0.0912 gives $200; the undocumented 65-year-old adds no member and no supplement.
  • Case 11: (900 + 1,740) / 6,000 = 0.44 at size 4 gives 700 + 100 once = $800.
  • Cases 12–14: heat in rent with no bills gives burden 0 and $200. TEC 2,400 − 1,440 = 960, and 960 / 15,080 = 0.0637, gives $200. TEC −300 is ineligible, so $0.
  • Energy cost Cases 1–5: 600 + 1,740 + 200 = 2,540 (water and sewer excluded, no allowance unsubsidized); 540; public housing gives 1,500 − 1,800 = −300; a member of 60 gives 1,740; an undocumented 65-year-old exempts nothing, so 540.
  • Case count: 14 + 5 + 11 + 14 + 5 = 49, as the body says.

Batch checks:

Wiring and housekeeping:

  • Registry. The LA entry sits between KY and MD, and LA is added to the coverage line. verified_years: "2026". test_programs_registry passed.
  • Aggregates and take-up. There is no aggregate wiring, which matches every merged LIHEAP state. There is no take-up beyond tanf's.
  • Gating. defined_for = StateCode.LA is on 5 variables, and la_liheap is gated by la_liheap_eligible.
  • Changelog. changelog.d/la-liheap-wave1d.added.md is top level and typed added.
  • Tests. The files are variable-named and every period is plain. Every case sends heating_type.

Checklist results (LIHEAP batch):

  1. State plan and manual: every published cell matches (limits, matrix, supplement, allowance rule).
  2. Income definition: pass. Gross wages, Social Security net of Part B per the guide and plan 1.9, per-source floors, adults only apart from Social Security and SSI, annual amounts.
  3. Backdating: pass for 2026. 2025 matches the published FY2025 items, and 2027 is a documented carry-forward.
  4. Household and arrangement rules: pass. Heat in rent, the subsidized allowance, the age-60 exemption and undocumented members are each tested apart from the public-housing branch (S1). Cooling is G1.
  5. Tests: 49 cases, all hand-checked, all periods plain. Source coverage is S1.
  6. Descriptions: pass.
  7. References: pass.
  8. Wiring: pass.
  9. Axiom: valid (DC TANF maximum monthly income #1890).
  10. Branch and CI: a clean merge with main; 29 pass and 3 skipping.
  11. Integrate modeled state unemployment insurance into income flows #9558: pass (unpinned, S1).
  12. IIJA / FY2027: not applicable / proposed only.
  13. Mutation check: 17 source entries and the public-housing eligibility branch are unpinned (S1).
  14. Document disjoint health premium inputs and correct consumer treatment #10046: not applicable.
  15. State-gated index defaults: safe.
  16. Wire military retirement pay and survivors into federal and state tax (#10181) #10182 / Count military SBP annuities in state benefit income lists #10317: pass.
  17. Merged state SSPs: la_oss exists, but its recipients live in medical facilities outside the LIHEAP household; no change needed.

Axiom line assessment

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

  • State: OPEN, labelled pe-parity, created 2026-10-23 22:16 UTC.
  • It matches the head. It names head 7c35019e79 and merge base 6a92d6a983. It lists the 12 parameters (5 earned and 16 unearned sources, 7 bills), the 6 variables and the 49 cases by file. Its required outputs (size, countable income, limit, total energy cost, eligibility and benefit) describe the head's code.
  • It is dispatch-ready in the rulespec-us#1467 format:
    • proposed us-la/ module paths, with corpus status per source;
    • verbatim text of the plan items, both guide editions, Appendix E, the matrix, both notices, FAQ A4 and the federal tables;
    • 13 companion tests derived from the sources, not from PE output;
    • 14 interpretations, including the cooling scope, the band edges, the plan-vs-guide choices and the federal floor;
    • its blockers: sources missing from the corpus, an unchecked signed release, billed-run approval and no reviewed oracle revision.
  • Note. S1 adds cases, so update the case counts. G2 changes interpretation 1, G3 interpretation 8, and G4 interpretation 12. Update the issue with the head that resolves them.

CI status

29 pass, 3 skipping, 0 failed. Run 40032541588 tested head 7c35019e79 (pull_request, created 2026-10-23 22:16 UTC, after #10287 and the 2026-10-21 cutoff). The three skipping entries belong to the "Pull request" parent workflow of run 40032336811, which did not execute. I re-checked gh pr checks 10429 at the end of this review.

  • Louisiana tests. States-shard-4, batch 5/13 (states/la), ran all 5 files: 14 + 5 + 11 + 14 + 5 = 49 passed.
  • Guards:
    • in Rest shard-1: the four test_military_retirement_readers.py tests and the test_source_conflicts.py tests, including the ratchet and the candidate checks;
    • in the second Rest job: the four test_unemployment_compensation_readers.py tests, test_members_are_state_gated and test_programs_registry.
  • Also passing: Microsimulation, Household API Partners, PR validation, Package and compatibility, Quick Feedback, codecov, and every baseline and contrib shard.
  • Known main-caused failure: not applicable.
  • Locally: I ran no tests or model probes. The S1 and G1–G3 examples are hand traces of the code at the head. The only local scripts were the static comment-pattern scan and the EIA monthly ratios.

Branch status

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

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The tables check out: all 10 FFY2026 (and FFY2025) limits, the 8-cell matrix, the $100 supplement, the utility allowance, electricity counted in total energy cost, Social Security net of Part B, and the 110% floor from size 14. All 49 cases hand-check, and rulespec-us#1890 is valid. One should-address item: 17 of 21 income sources never take a positive counted value in a test, and the is_in_public_housing branch is unpinned. Four suggestions, including that cooling, LA's larger component, is out of scope and the body's 'winter bills exceed the average' claim runs backwards for all-electric homes. 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 la-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 5bdd6c159e; a85f9738ae merges current main, which settles the LIHEAP coverage line in programs.yaml with LA on it.

  • S1. Untested income sources and the public-housing branch. Three new cases pin them:
    • Earned list. income/la_liheap_countable_income.yaml Case 12 gives a 40-year-old $4,000 of sstb_self_employment_income, $2,000 of farm_operations_income and $1,000 of partnership_s_corp_income. It expects $7,000, so partnership income now counts with a positive amount, and dropping any entry changes the total.
    • Unearned list. Case 13 gives a 55-year-old a distinct amount from every remaining source and expects $10,500 (1,400 + 1,300 + ... + 100). The sources are military_retirement_pay_survivors $1,400, reported unemployment_compensation $1,300, railroad_benefits $1,200, veterans_benefits $1,100, disability_benefits $1,000, survivor_benefits $900, workers_compensation $800, alimony_income $700, rental_income $600, strike_benefits $500, farm_rent_income $400, estate_income $300, ordinary_dividend_income $200 and interest_income $100. With Cases 1, 5 and 10, every one of the 5 earned and 16 unearned entries now takes a positive counted value. Case 1 still pins the loss floors.
    • Public housing. eligibility/la_liheap_eligible.yaml Case 15 has a 40-year-old public housing tenant with no other housing assistance, $15,080 of wages, a $1,500 electric bill and a $1,800 allowance. The energy cost is 1,500 - 1,800 = -300, so the household is ineligible. Without the is_in_public_housing branch, the case would return true and pay your $200.
  • G1. Cooling. I declined modeling la_liheap_cooling, because the all-state LIHEAP work covers regular heating assistance only, and took your second option instead. The la_liheap.py documentation now says Louisiana also pays a separate summer cooling award from the same matrix, with 45.5% of FY2026 funds against 25% for heating, so program-year regular assistance can be twice la_liheap. The programs.yaml note now says "The larger summer cooling award, crisis and weatherization assistance are out of scope." Your la_liheap.yaml Case 7 household still gets $450 for heating, and its $450 cooling award stays unmodeled.
  • G2. Annual bills. The payment/energy_cost_sources.yaml comment no longer says the annual ratio "gives the same burden". It now says annual amounts give the guide's burden only when the application-month bill equals the annual monthly average, and that Louisiana's heating-season electric bills run below that average and gas bills above it. The PR body's "Not modeled" row and interpretation 1 in the rulespec issue give the EIA-861M figures: January-March and November-December residential revenue averaged 0.857 (2024) and 0.863 (2025) of the annual monthly average. The annual approximation stays. Your all-electric example still returns $350 (1,740 / 15,080 = 11.54%), where a typical heating-season bill would give $200.
  • G3. Disability by benefit receipt. Section 4.2 A accepts proof of disability from the Social Security Administration. la_liheap.py now places a counted member in the disability group in three cases: is_disabled is true, social_security_disability is above zero, or the member receives SSI (ssi or receives_ssi) and is_ssi_disabled is true. I used is_ssi_disabled rather than is_usda_disabled, so aged SSI alone does not count. Two new cases cover it:
    • SSDI. la_liheap.yaml Case 15 is your example: age 45, SSDI of $14,400 in the Medicare waiting period (no Part B premium), no is_disabled, and a $1,740 electric bill. 1,740 / 14,400 = 12.08%, so it pays $350 + $100 = $450 (was $350).
    • SSI. Case 16: age 45, meets_ssi_disability_criteria, no earnings, no is_disabled, SSI of $11,928 and a $2,400 bill. 2,400 / 11,928 = 20.12%, so it pays $500 + $100 = $600 (was $500).
  • G4. Plan-vs-guide choices. source_conflicts/candidates.yaml adds two entries. Both record follows: gap-fill and state: ruled under the 2026-10-06 ruling to follow the manual over the plan, and both list this PR. The code does not change.
    • us-la:liheap-plan-income-checklist covers lump sums, cash gifts and commissions: guide 3.6 C.1 and Appendix E against plan 1.9.
    • us-la:liheap-utility-allowance-age covers the age-60 exemption: guide 4.1 C "60 years or older" against plan 2.3 "over 60".

The PR body and TheAxiomFoundation/rulespec-us#1890 now describe this head. They cover the disability group, the cooling award, the annual-bill wording, both candidate ids and 54 cases: la_liheap.yaml 16, la_liheap_energy_cost.yaml 5, eligibility/la_liheap_eligible.yaml 15, eligibility/la_liheap_income_limit.yaml 5 and income/la_liheap_countable_income.yaml 13.

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

@DTrim99

DTrim99 commented Oct 29, 2026

Copy link
Copy Markdown
Collaborator

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

PR #10429 (author: hua7450), head a85f9738ae. Round 1 reviewed 7c35019e79 on 2026-10-26 and requested changes, with 0 critical, 1 should and 4 suggestions open. Since then the author has pushed two commits:

  • 5bdd6c159e, the round-1 fixes (8 files, 323 lines);
  • a85f9738ae, a merge of main at c55b806541 with two hand-resolved blocks, in programs.yaml and source_conflicts/candidates.yaml. Both are correct (Branch).

The author replied item by item on 2026-10-29. The PR body and rulespec-us#1890 were both rewritten for this head. The PR still touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/la/lhc/liheap/
  • V/ = policyengine_us/variables/gov/states/la/lhc/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/la/lhc/liheap/

Summary

S1 is fixed. Every one of the 21 income-source entries and the public-housing branch of eligibility now has a test that fails if it is deleted:

  • countable Case 12 covers the three remaining earned sources;
  • countable Case 13 covers the 14 remaining unearned sources, each with a distinct amount;
  • eligibility Case 15 is a public-housing tenant whose allowance exceeds the bill.

All four suggestions are adopted:

  • G1. Cooling is now described as the larger component in the documentation, the registry note, the body and DC TANF maximum monthly income #1890. Modeling it was declined with a reason that matches the wave's documented-deferral pattern.
  • G2. The annual-bill wording is corrected everywhere, with EIA figures.
  • G3. Social Security disability benefits and SSI paid on the basis of disability now place a member in the priority group.
  • G4. Two us-la:liheap-... candidates record the plan-versus-guide choices.

New findings: no critical and one should-address item.

  • N1. The G3 fix adds four terms, and two of them can be deleted with no test failing: the reported-receipt alternative (receives_ssi) and the is_ssi_disabled condition.

There are also two housekeeping suggestions:

  • R2-G1. The candidates' empty code lists and "Code not yet on main" notes will be stale once this PR merges.
  • R2-G2. The CI wording in the body and DC TANF maximum monthly income #1890 is stale.

Status:

  • CI: 35 of 35 checks pass at a85f9738ae. States-shard-4 ran all five Louisiana files, 54 cases.
  • Branch: 4 commits ahead of main and 8 behind. Alaska (Add Alaska LIHEAP regular heating assistance #10341) merged after the author's merge, so the LIHEAP coverage line now conflicts with main. That needs a one-line resolution keeping both AK and LA (branch note).

Prior findings

Finding Status Evidence
S1. 17 of the 21 income-source entries and the public-housing branch of eligibility were unpinned Fixed Earned. Countable Case 12 (T/income/la_liheap_countable_income.yaml:306) gives a 40-year-old $4,000 of sstb_self_employment_income, $2,000 of farm_operations_income and $1,000 of partnership_s_corp_income, and expects $7,000. Dropping any of the three entries gives $3,000, $5,000 or $6,000, so each fails. partnership_s_corp_income is now pinned with a positive amount.
Unearned. Countable Case 13 (:331) gives a 55-year-old 14 distinct amounts from $1,400 down to $100 and expects $10,500. Dropping an entry lowers the total by its own amount. For example, without military_retirement_income (fed here by military_retirement_pay_survivors) the total is $9,100; without total_unemployment_compensation (reported, $1,300) it is $9,200; without interest_income it is $10,400.
Already pinned: employment_income and self_employment_income (Case 1), pension_income (Case 5) and financial_assistance (Case 10). That makes all 5 earned and 16 unearned entries. Case 1 still pins the per-source loss floors.
Public housing. Eligibility Case 15 (T/eligibility/la_liheap_eligible.yaml:328) is a 40-year-old public-housing tenant with receives_housing_assistance: false, wages of $15,080, a $1,500 electric bill and a $1,800 allowance. It expects false. Deleting | is_in_public_housing from V/eligibility/la_liheap_eligible.py:53-55 makes the unit unsubsidized, so energy_cost_eligible is true. The other conditions hold (size 1, 15,080 ≤ 30,618, a positive electric bill, ELECTRICITY), so the case returns true and fails. Deleting the copy in V/la_liheap_energy_cost.py:27-29 gives a total energy cost of $1,500, which also fails it (and energy-cost Case 3).
G1. Model or flag the larger cooling award Adopted (second option; modeling declined with a reason) Documentation (V/la_liheap.py:10-22): "Louisiana also pays a separate summer cooling award from the same matrix, with 45.5% of FY2026 funds against 25% for heating, so program-year regular assistance can be twice this amount".
Registry note (programs.yaml:1110): "The larger summer cooling award, crisis and weatherization assistance are out of scope."
Body and #1890 say the same.
The reason. The author says the all-state LIHEAP work covers heating only. That matches merged KY and the wave's documented-deferral pattern.
G2. "Winter bills usually exceed the annual average" was backwards for all-electric homes Adopted Parameter comment (P/payment/energy_cost_sources.yaml:2-5): annual amounts "give the guide's burden only when that month's bill equals the annual monthly average; Louisiana's heating-season electric bills run below that average and gas bills above it."
Body row and #1890 interpretation 1: electric bills are overstated and gas bills understated, with the EIA-861M ratios 0.857 (2024) and 0.863 (2025). Our round-1 figures, from rounded totals, were 0.856 and 0.863. The old wording no longer appears in the code, the body or #1890.
G3. Count disability-based benefit receipt toward the supplement Adopted, but two of the new terms are unpinned (N1) Code (V/la_liheap.py:56-71): disabled = is_disabled | (social_security_disability > 0) | (receives_ssi & is_ssi_disabled), where receives_ssi is ssi > 0 or reported receives_ssi.
Pinned:
  • la_liheap Case 15 (T/la_liheap.yaml:366) is our SSDI example and expects $450. Without the SSDI term the result is $350.
  • Case 16 (:391) gives SSI of $11,928 with meets_ssi_disability_criteria and expects $600. Without the SSI term the result is $500.
Aged SSI. is_usda_disabled was avoided, so aged SSI alone does not count.
G4. Record the plan-vs-guide income choices Adopted Entries: source_conflicts/candidates.yaml:2779 (us-la:liheap-plan-income-checklist: lump sums, cash gifts and commissions; guide 3.6 C.1 and Appendix E against plan 1.9 p. 6) and :2809 (us-la:liheap-utility-allowance-age: guide 4.1 C p. 40 "60 years or older" against plan 2.3 p. 9 "over 60").
Fields: follows: gap-fill, state: ruled with the reason "follow the manual over the plan", and prs listing this PR.
Examples: the $16,280 / $15,080 contribution example matches countable Case 10's treatment, and the subsidized 60-year-old example matches eligibility Case 12.
No code change, as asked. The code lists are empty (R2-G1).

New findings

Critical

None.

Should address

N1. The reported-SSI alternative and the is_ssi_disabled condition in the new disability test can be deleted with no test failing
  • Where: V/la_liheap.py:59-66.

  • What is pinned. Of the four terms the G3 fix adds, two are pinned:

    • social_security_disability > 0, by Case 15;
    • the SSI term as a whole, by Case 16.
  • What is not. No Louisiana test sets receives_ssi, so the second half of
    receives_ssi = (add(person, period, ["ssi"]) > 0) | (add(person, period, ["receives_ssi"]) > 0)
    can be deleted with no test failing.

  • Why the is_ssi_disabled condition is also unpinned. The only la_liheap cases with SSI above zero are Case 8 (is_disabled: true) and Case 16 (is_ssi_disabled true). Deleting & person("is_ssi_disabled", period) therefore changes no expected value.

  • Effect if deleted.

    • Without the receives_ssi term: a disabled member whose SSI receipt is reported but whose ssi amount is 0 loses the $100. For example: age 45, receives_ssi, meets_ssi_disability_criteria, ssi 0, pension $14,400 and electricity $1,740. That is 1,740 / 14,400 = 12.08%, band 2: $450 at the head and $350 without the term.
    • Without the condition: every SSI recipient under 60 gains the $100. In practice the condition has no Louisiana role for aged SSI, because recipients 65 or older already qualify through elderly_age (60). It bites only on two groups:
      • Blind recipients. is_ssi_disabled does not cover blindness. For example: age 45, is_blind, SSI $11,928 and electricity $2,400. That is 20.12%, band 3: $500 at the head and $600 without the condition.
      • Recipients working above SGA under section 1619(a). is_ssi_disabled applies the SGA screen.

    Both groups hold an SSA determination, which guide 4.2 A accepts as proof.

  • Severity: should. This follows the wave's rule for a fix that adds a term no test pins (UT r2 N1, ND r2 N1, AK r3 N2) and checklist item 13.

  • Fix:

    • The receives_ssi term. Add the reported-receipt case above (receives_ssi: true works with an annual period, as in the Pell Grant tests). Expect $450.
    • The is_ssi_disabled condition. Choose one of these:
      • (a) Keep it and pin it. Add the blind-recipient case expecting $500, and say in the documentation that blind recipients need is_disabled.
      • (b) Drop it (preferable in our view). Every SSI recipient is aged, blind or disabled, and Louisiana's aged recipients already qualify by age. Receipt alone then matches "Disability can be proven through the Social Security Administration" and also covers blind and 1619(a) recipients. Case 16 and the new receives_ssi case then pin the whole SSI term.
    • Then update the case counts in the body and DC TANF maximum monthly income #1890 (companion test 4 and the required-outputs row).

Suggestions

R2-G1. List the PR's own files in the two new candidates (housekeeping).

  • Where: source_conflicts/candidates.yaml:2779-2836. Both entries have code: [], and their notes end "Code not yet on main: la_liheap_countable_income, gov.states.la.lhc.liheap.income.sources" and "... la_liheap_energy_cost, gov.states.la.lhc.liheap.elderly_age".

  • The problem. This PR adds that code, so both notes become false when it merges.

  • The precedent. Candidates that sibling PRs added alongside their own code list the paths, for example us-ak:hap-* (Add Alaska LIHEAP regular heating assistance #10341), us-ut:heat-* and us-nd:liheap-*. The test accepts any tracked path, and these files are tracked at the head.

  • Fix: set code to:

    • policyengine_us/variables/gov/states/la/lhc/liheap/income/la_liheap_countable_income.py, .../parameters/.../income/sources/earned.yaml and .../unearned.yaml;
    • .../la_liheap_energy_cost.py and .../elderly_age.yaml;

    and drop the "Code not yet on main" clauses.

  • Note on the ruling. I could not trace the "Ruled 2026-10-06" ruling in the repository. Sibling Add Missouri LIHEAP Energy Assistance #10438 (Missouri) cites the same ruling. The choice also follows CONTRIBUTING's rule that, between two agency documents with no statute, the administering agency's reading breaks the tie. So I take the author's statement as given.

R2-G2. Stale CI wording (housekeeping).

  • The PR body still says the five new cases "were not run locally; CI validates them" and leaves "CI passes" unchecked.
  • DC TANF maximum monthly income #1890 says "the fixes at a85f9738ae await re-review" and "The five cases added in 5bdd6c159e are hand-derived and were not run locally".
  • Fix: CI has now run all 54 cases green at a85f9738ae. Update both when the N1 cases land, as in AK R4-G1.

Verified

Hand checks of every new expectation (2026; size-1 limit $30,618). The fix commit only appends cases, so no earlier expectation changed.

  1. Countable Case 12: 4,000 + 2,000 + 1,000 = $7,000.
    • Each source is floored at zero, and the member is an adult not in K-12 school.
    • There is no child, so FITAP (tanf) is 0. The member is not aged, blind or disabled, so SSI is 0.
  2. Countable Case 13: 1,400 + 1,300 + 1,200 + 1,100 + 1,000 + 900 + 800 + 700 + 600 + 500 + 400 + 300 + 200 + 100 = $10,500.
    • military_retirement_income adds military_retirement_pay_survivors.
    • total_unemployment_compensation takes the reported $1,300, since it is nonzero.
    • Nothing else applies: no Social Security, Part B, SSI or FITAP.
  3. Eligibility Case 15: the household is subsidized through public housing and under 60, so its total energy cost is 1,500 − 1,800 = −300, which is not positive. The result is false, although income, responsibility, fuel and size all pass.
  4. la_liheap Case 15:
    • Income: social_security takes the $14,400 of SSDI. Part B is $0, because months_receiving_social_security_disability is 0, so the member is not Medicare-eligible at 45.
    • Burden: 1,740 / 14,400 = 0.1208, band 2, size 1: $350.
    • Supplement: SSDI above 0 adds $100, so $450.
    • Counting: SSDI enters only through social_security. No list entry carries it, so it is not counted twice.
  5. la_liheap Case 16:
    • Income: SSI of $994 a month (the 2026 federal benefit rate) × 12 = 11,928, which is at or below 30,618.
    • Burden: 2,400 / 11,928 = 0.2012, band 3: $500.
    • Supplement: is_ssi_disabled is true, since the member meets the criteria and has no earnings and so no SGA. That adds $100, so $600.

Hand-resolved merge blocks (a85f9738ae), both correct:

  • programs.yaml coverage line. The resolution is main's 31 entries at c55b806541 with LA inserted between KY and MD, 32 in all. ND, NH, UT, VA, WA, WY, AZ and IA are all kept, and nothing is duplicated. Against c55b806541, the PR's programs.yaml diff is only that line and the LA state_implementations block between KY and MD.
  • source_conflicts/candidates.yaml. The conflict was in the note of the preceding us-la:cpi-u-indexing-method entry. The resolution takes main's rewritten note verbatim and keeps the two LIHEAP entries after it. Against c55b806541, the PR's diff to this file is only the 58 added lines. The head has 285 ids, which is 283 + 2.

Guards and batch checks:

Axiom line assessment

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

  • State: OPEN and labelled pe-parity. It was updated 2026-10-29 at 13:40 UTC.
  • It matches the head. It names head a85f9738ae, fix commit 5bdd6c159e and merge base c55b806541. It lists 54 cases by file (16, 5, 15, 5, 13), which match CI. It covers:
    • Required outputs: the new disability test, including social_security_disability and the ssi / receives_ssi receipt with is_ssi_disabled.
    • Companion tests: 4(e) and 4(f) (SSDI $450, SSI $600), 9(f) (public housing) and 13(e)-(f) (the new source cases), each derived from the sources.
    • Interpretations: 1 now gives the corrected electric and gas direction with the EIA figures, 8 the disability group, and 12 the two candidate ids. The scope line calls cooling the larger component.
  • Dispatch-ready: module paths, corpus status, verbatim text and blockers, as in round 1.
  • After N1: add the receives_ssi case (and the blind case under option (a)) to companion test 4, and update the counts. Also see R2-G2.

CI status

35 pass, 0 failed, 0 pending. Run 40792556398 (pull_request, created 2026-10-29 00:12 UTC) tested head a85f9738ae, which includes main at c55b806541, after #10287 and the Montana AGI fix. I re-checked gh pr checks 10429 at the end of this review.

  • Louisiana tests. States-shard-4, batch 5/13 (states/la), ran all five files: 15 + 5 + 13 + 16 + 5 = 54 passed.
  • Also passing: Rest Python shards 1-6 (including the source-conflict, military-retirement and unemployment guards), Microsimulation, Household API Partners, PR validation, Package and compatibility, Quick Feedback, codecov, and every baseline and contrib shard.
  • Known main-caused failures: not applicable.
  • Locally: I ran no tests or model probes. N1's mutations are static: no Louisiana test sets receives_ssi, and every la_liheap case with SSI also has is_disabled or is_ssi_disabled. Its examples are hand traces of the head. The only local script was the static comment-pattern scan.

Branch status

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

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes, round 2: 0 critical, 1 should. All round-1 items are fixed. New: the receives_ssi term and the is_ssi_disabled condition added by the G3 fix have no test (add a receives_ssi case, and pin or drop the condition). Branch note: AK #10341 merged, so the coverage line now needs AK + LA. Full report in the comment above.

hua7450 and others added 2 commits October 29, 2026 21:59
Apply DTrim99's round 2 review: SSI receipt alone places a member in the
priority group, a test for reported SSI receipt, 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:11
@DTrim99

DTrim99 commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 3): Add Louisiana LIHEAP regular heating assistance

PR #10429 (author: hua7450), head 57b8cfdc24. Round 2 reviewed a85f9738ae on 2026-10-29 and requested changes, with 0 critical, 1 should and 2 suggestions open. Since then the author has pushed two commits:

  • 1576cf811b, the round-2 fixes (3 files, +52/−14);
  • 57b8cfdc24, a merge of main at a24c9cb55d with one hand-resolved block, the LIHEAP coverage line in programs.yaml. It is correct (Verified).

There is no new reply comment. The commit message, the PR body and rulespec-us#1890 describe the changes. The PR still touches no partner test file.

Path prefixes:

  • V/ = policyengine_us/variables/gov/states/la/lhc/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/la/lhc/liheap/

Summary

N1 is fixed, using the option we preferred. The is_ssi_disabled condition is gone, so SSI receipt alone places a member in the disability group. A new case pins the reported-receipt alternative:

  • la_liheap Case 17 now pins the reported receives_ssi term;
  • Case 16 now pins the ssi > 0 term on its own.

Deleting either term fails a test.

Both suggestions are adopted:

  • R2-G1. Both candidates now list the PR's files, and the "Code not yet on main" clauses are gone.
  • R2-G2. The body and DC TANF maximum monthly income #1890 were rewritten for this head. Their CI sentences went stale again, though, because CI ran after the edit (R3-G1).

New findings: none critical, none should-address, and one housekeeping suggestion (R3-G1).

Status:

  • CI: 35 of 35 checks pass at 57b8cfdc24. States-shard-4 ran all five Louisiana files, 55 cases.
  • Branch: five LIHEAP siblings merged after the author's merge, so the coverage line conflicts with main again. It needs a one-line resolution, given under Branch (branch note).

Prior findings

Finding Status Evidence
N1. The reported-SSI alternative and the is_ssi_disabled condition could be deleted with no test failing Fixed (option (b): the condition was dropped) Code (V/la_liheap.py:61-68): disabled = is_disabled | (social_security_disability > 0) | receives_ssi, where receives_ssi is ssi > 0 or reported receives_ssi. Nothing else in the Louisiana variables reads is_ssi_disabled.
Reported term. la_liheap Case 17 (T/la_liheap.yaml:417) sets receives_ssi: true with ssi: 0 and expects $750. Delete the reported half and every term of disabled is false:
  • is_disabled is a plain input that is not set;
  • SSDI is $0;
  • ssi is 0.
The member is 45, so neither age group applies, and the result is $650: the case fails.
ssi > 0 term. Case 16 (:391) has SSI of $11,928 and does not set receives_ssi, a monthly input with no formula. Delete the ssi > 0 half and the result is $500 against an expected $600: the case fails. Delete the whole SSI term and both cases fail.
The reason for dropping the condition holds. SSI goes only to people who are aged (65 or older), blind or disabled (42 U.S.C. 1382c(a)(1)(A)), and elderly_age is 60. So the change affects only recipients under 60 that is_ssi_disabled missed, mainly blind recipients and section 1619(a) workers. Both hold the SSA determination that guide 4.2 A accepts.
Text. The comment (V/la_liheap.py:56-60), the documentation, the body and #1890 interpretation 8 all say this.
R2-G1. List the PR's files in the two new candidates Adopted us-la:liheap-plan-income-checklist (source_conflicts/candidates.yaml:3248): code lists la_liheap_countable_income.py and income/sources/earned.yaml and unearned.yaml.
us-la:liheap-utility-allowance-age (:3280): code lists la_liheap_energy_cost.py and elderly_age.yaml.
All five paths exist at the head. Both notes now read only "Ruled 2026-10-06: follow the manual over the plan." The candidate checks passed in CI.
R2-G2. Stale CI wording in the body and #1890 Adopted for the content, but the CI lines are stale again (R3-G1) Updated for this head. The body's Test plan and the #1890 Tests bullet give 55 cases by file, with Case 17 as companion 4(g).
Stale. Both still say "CI has not yet run at the current head", and the body leaves "CI passes" unchecked. CI has since run all 55 cases green at 57b8cfdc24.

New findings

Critical

None.

Should address

None.

Suggestions

R3-G1. Refresh the CI wording again (housekeeping).

  • PR body, Test plan: "Case 17 ... was not run locally; CI has not yet run at the current head, 57b8cfdc24", and "CI passes" is unchecked.
  • DC TANF maximum monthly income #1890:
    • the Tests bullet ends "Case 17 is hand-derived and was not run locally; CI has not yet run at 57b8cfdc24";
    • the oracle blocker says "the fixes at 57b8cfdc24 await re-review".
  • Fix: Run 40989070923 passed all 55 Louisiana cases at 57b8cfdc24. Say so, tick the box, and update the blocker after this review, as in AK R4-G1. Do the same after the coverage-line merge if it moves the head.

Verified

Hand checks of every new or changed expectation (2026; the size-1 limit is floor(floor(98,137 × 0.6) × 0.52) = floor(58,882 × 0.52) = $30,618):

  1. la_liheap Case 17 (new), $750.
    • Income: 3,600. The $3,600 pension counts, because the member is 18 or older and pensions are on the unearned list. Social Security is max(0 − Part B, 0) = 0; at 45 with no SSDI the member is not Medicare-eligible. ssi is 0, and tanf is 0 because there is no child.
    • Eligible: 3,600 ≤ 30,618, with a positive electric bill under ELECTRICITY in unsubsidized housing.
    • Burden: 1,740 / 3,600 = 0.4833 ≥ 0.25, so band 4, size group 1: $650.
    • Supplement: neither age group applies (45 is not 60 or older, nor 5 or younger). is_disabled is unset, SSDI is 0 and ssi is 0, so only the reported receives_ssi adds the $100: $750.
    • The case isolates the reported term. is_blind feeds no term, because is_disabled is a plain input.
    • The comment's SSI estimate is right: (300 − 20) = 280 countable, and 994 − 280 = $714 a month. That explains why ssi: 0 is an artificial isolation input.
  2. la_liheap Case 16 (comments only), still $600. 2,400 / 11,928 = 0.2012, band 3: $500, plus $100 for SSI receipt = $600.
    • Its meets_ssi_disability_criteria: true input and its name ("disabled under SSI rules") are now redundant but harmless.
  3. No other expected value changed. Since a85f9738ae, the only Louisiana files that differ are T/la_liheap.yaml (Case 16 comments, Case 17) and V/la_liheap.py.

Hand-resolved merge block (57b8cfdc24): correct.

  • The resolution. It is the base a24c9cb55d's 33 entries with LA inserted between KY and MD, 34 in all. AK and NM are kept, and nothing is dropped or duplicated.
  • The PR's whole programs.yaml diff, against a24c9cb55d, is that line and the LA state_implementations block between KY and MD.
  • source_conflicts/candidates.yaml auto-merged: the head has 306 ids (304 + 2), all unique.

Guards:

Axiom line assessment

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

  • State: OPEN and labelled pe-parity. It was updated 2026-10-30 at 02:11 UTC, after the push.
  • It matches the head. It names head 57b8cfdc24, fix commit 1576cf811b and merge base a24c9cb55d. It lists 55 cases by file (17, 5, 15, 5, 13), which match CI.
    • Required outputs: the benefit row reads "SSI received (ssi > 0 or receives_ssi)", with no SSI disability test.
    • Companion test 4(g): the blind reported-receipt case, with the $750 derivation from the matrix.
    • Interpretation 8: explains dropping the test (aged recipients qualify at 60, and recipients under 60 are blind or disabled, including those under 1619(a)).
    • Interpretation 12: lists the candidates' files.
  • Dispatch-ready: as in round 2.
  • Only the CI and blocker wording is stale (R3-G1).

CI status

35 pass, 0 failed, 0 pending. Run 40989070923 (pull_request, created 2026-10-30 01:59 UTC) tested head 57b8cfdc24 merged with main at a24c9cb55d. I re-checked gh pr checks 10429 at the end of this review.

  • Louisiana tests. States-shard-4, batch 5/13 (states/la), ran all five files: 15 + 5 + 13 + 17 + 5 = 55 passed. The batch exited 0.
  • Also passing: Rest Python shards 1-6 (including the source-conflict, unemployment, military-retirement and Social Security guards), and every baseline and contrib shard.
  • Known main-caused failures: not applicable.
  • Locally: I ran no tests or probes. N1's mutations are static traces of the head, and CI confirms Case 17's $750 with the term present.

Branch status

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 3: 0 critical, 0 should. N1 is fixed (condition dropped; both SSI terms pinned). Full report in the comment above. Branch note: more LIHEAP states merged, so re-resolve the coverage line (main's line plus LA) before landing.

…p-wave1d

# Conflicts:
#	policyengine_us/programs.yaml
@hua7450
hua7450 merged commit a14576f into main Oct 30, 2026
66 of 68 checks passed
@hua7450
hua7450 deleted the la-liheap-wave1d branch October 30, 2026 16:19
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 Louisiana LIHEAP regular heating assistance

2 participants