Skip to content

Add Nebraska LIHEAP regular cooling assistance - #10758

Merged
hua7450 merged 19 commits into
mainfrom
ne-liheap-cooling
Nov 15, 2026
Merged

hua7450 merged 19 commits into
mainfrom
ne-liheap-cooling

Conversation

@hua7450

@hua7450 hua7450 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Nebraska's Cooling Assistance payment from the 2026 cooling table, by income tier and dwelling type, for households with a qualifying member.

Fixes #10757

Builds on the national LIHEAP total from #10730, merged on 2026-11-12: this PR adds ne_liheap_cooling to gov.hhs.liheap.regular_payments.

What and why

476 NAC 2-003 requires a qualifying member: someone 70 or older, a child under 6 in a household receiving ADC, someone with a medical condition heat aggravates, or someone who got a state air conditioner in the last four years. The 2026 cooling table pays single-family homes $700, $595, $525 or $478 and multifamily homes $385, $335, $300 or $273, by income tier (tops 70%, 100%, 130% and 150% of the guideline); "between two income levels, use the row with the higher amount". Income uses heating's test and 20% earned-income disregard (Guidance Document, pp. 2-3). The season runs June 1 through August 31 (476 NAC 1-004.04), and DHHS pays "one time per cooling season" (Energy Assistance page).

  • New ne_liheap_payment_fpg_ratio holds heating's income-ratio calculation, now shared. Every existing heating boundary lands in the same tier.
  • ne_liheap_cooling_eligible: heating's income test; has_cooling_expense, or utilities in the rent of an unsubsidized renter (tenant_pays_utilities false with positive rent, and neither receives_housing_assistance nor is_in_public_housing); and a member 70 or older, or a child under 6 in a unit with positive tanf. The qualifying member must pass the SNAP citizenship rule (476 NAC 2-002.02).
  • ne_liheap_cooling: the single-family or multifamily table, chosen by ne_liheap_dwelling_type. New parameters under cooling/.
  • 2027 is verified: the Guidance Document effective October 1, 2026 prints the same cooling table on the 2026 guidelines (p. 4), and the parameters now cite it. verified_years is "2026-2027". FY2027 plan item 3.5 (p. 12) makes the table subject to update closer to the cooling season based on funding.
  • Cooling adds no heating-fuel condition: the cooling table has no fuel column, so every heating_type, including NONE and UNSPECIFIED, can be paid (Nebraska heating likewise pays a multi-family home whatever its fuel).
  • Renters with utilities in the rent: plan item 3.3 (p. 11) repeats item 2.3's (p. 9) heating sentence, and 476 NAC 3-002.02 lets DHHS pay a household directly "if utilities are included in rent" for cooling as for heating. Cooling reads it as ne_liheap_eligible reads item 2.3, with the same subsidy test: an unsubsidized renter with utilities in the rent qualifies, and a household with housing assistance or in public housing needs its own cooling cost. The project lead set this on 2026-11-13, replacing the 2026-11-09 $0 treatment.

Test plan

Tests were not run locally. This PR now targets main, so CI runs them.

  • YAML tests with expected values worked from the sources above. 24 cases in 3 files (14, 6 and 4): each tier and dwelling type at the tier edges, each qualifying route, the citizenship rule on both routes, the shared ratio, no cooling cost, no heating and UNSPECIFIED fuel (both paid), renters with utilities in the rent (unsubsidized, subsidized, rent-free) and a renter who pays her own utilities, and the national liheap total.
  • Source-conflict register: not applicable; cooling reads plan item 3.3 as heating reads item 2.3, and no source contradicts that reading.
  • make format (ruff)
  • Changelog fragment in changelog.d/

Not modeled

  • The heat-aggravated medical condition and the four-year air-conditioner routes have no input, so they pay $0. Disability is not used in their place: 476 NAC 2-003 has no disability route.
  • Later years carry the FY2027 table forward.
  • ADC receipt is known only for the whole unit, so a child under 6 in a unit that receives ADC qualifies even if that child is outside the grant.
  • has_cooling_expense is a PolicyEngine convention of the all-state cooling work. 476 NAC 2-003 names no air conditioner or separate cooling cost, and plan item 8.3 (p. 22) describes a cooling mass run for current cases, so a household with no reported cooling cost and no utilities in the rent can be paid by Nebraska but gets $0 here.

axiom: TheAxiomFoundation/rulespec-us#2189 queued

🤖 Generated with Claude Code

hua7450 and others added 13 commits November 8, 2026 22:01
Sum each state's regular LIHEAP payment in a new liheap variable, add an
Illinois regular heating payment without crisis assistance, add a Michigan
Home Heating Credit registry entry, and record each state's cooling
treatment in its registry note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add regular_payments.yaml to the aggregate-list integrity test, which checks
that each member exists, is state-gated and is not dropped between year
blocks. Say that Rhode Island plans no regular cooling payment, since its
FY2027 plan still allows a leftover-funds cooling grant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Points the eligibility reference at its first cited page, adds the DHHS
energy assistance page, describes the guideline lag by federal fiscal
year, and checks the national liheap total.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The national `liheap` total evaluates every state's formula over a
mixed-state population, which no household test does. Two patterns on main
raised errors there:

- 26 formula files passed options=[ADD] to an entity call. Core's
  Microsimulation.calculate_add rejects the keyword an entity call passes.
  The option is redundant: a plain read already sums a monthly variable over
  the year through the same Core function.
- The DC and Illinois payment formulas look up a matrix by an income level
  that is 0 for households outside the state. Add the floor Maryland and
  Kansas already use.

Also:
- Reword the North Dakota registry note: the state reserves an optional
  direct cooling payment, so its cooling component is not only equipment.
- Add a `liheap` case with Texas, DC and Illinois households in one
  simulation.
- Add a code-health check against options=[ADD] and options=[DIVIDE].
- Check that every state payment in the LIHEAP registry entry is in the
  list `liheap` sums.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
PolicyEngine/policyengine-core#647 fixes the cause in Core: it lets a
Microsimulation accept the keyword an entity call passes with
options=[ADD] or options=[DIVIDE]. Once that is released the option works
again, so a check that bans it here would be wrong. The 26 formula files
stay without the option, which was redundant and lets `liheap` run on
today's Core.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ogram prefix

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from liheap-aggregate to main November 12, 2026 19:06
hua7450 and others added 2 commits November 12, 2026 14:08
PR 10730 was squash-merged into main as f67bb1e, so this merge uses its
final head 4625f4e, already merged here, as the merge base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Nebraska LIHEAP regular cooling assistance

Summary

This PR adds Nebraska's Cooling Assistance payment (ne_liheap_cooling) and its eligibility flag, moves heating's income-ratio calculation into a shared ne_liheap_payment_fpg_ratio, and adds the cooling payment to the national liheap list. The core of it is right:

  • The cooling table matches both Guidance Documents cell by cell.
  • Tiers, the 20% earned-income disregard, the six-person band cap, the one-year guideline lag and the program-year dating all match the sources.
  • The heating refactor returns exactly what main returns.
  • Nebraska pays heating and cooling to the same household in one program year, so listing both payments in regular_payments is not a double count.

Three things need changing before approval:

  1. The UNSPECIFIED-fuel gate blocks cooling for households that Nebraska's own heating formula pays, and the body says the opposite.
  2. Renters whose cooling is in the rent get $0. That reads the plan's renter sentence the opposite way from how the heating variable reads the identical sentence, and the choice isn't recorded.
  3. One citizenship term has no test that pins it.

CI is green (36 of 36). The branch merges cleanly with main and with each of the eight siblings.

Critical (must fix)

None.

Should address

1. The UNSPECIFIED-fuel gate excludes households that Nebraska's heating formula pays, and "as in every state LIHEAP program" is not accurate for Nebraska

Where: policyengine_us/variables/gov/states/ne/dhhs/liheap/cooling/ne_liheap_cooling_eligible.py lines 42-46 and 50, and ne_liheap_cooling.yaml Case 5 (line 99).

The cooling payment does not depend on heating fuel:

Nebraska's own heating formula pays an unspecified fuel when the home is multi-family:

  • ne_liheap.py lines 34-37 put multifamily first in the select, ahead of every fuel test.
  • On main, integration.yaml Case 7 (line 176) pins ne_liheap: 300 for a multi-family heat-in-rent renter with heating_type: UNSPECIFIED.
  • The heating_type enum documentation says UNSPECIFIED "is also the right value when heat is included in rent and the fuel is unknown".

The body's sentence "heating_type UNSPECIFIED pays $0, as in every state LIHEAP program" is therefore contradicted by Nebraska's own heating variable and its test. The queued rulespec-us issue says this more accurately (item 7: "No Nebraska source states this rule").

Household:

  • Inputs: one person aged 72 with $1,200 a month of Social Security ($14,400). She lives in an apartment (ne_liheap_dwelling_type: MULTI_FAMILY) with heat in the rent (heat_expense_included_in_rent: true, heating_type: UNSPECIFIED). She pays her own electric bill for a window air conditioner (has_cooling_expense: true) and gets no housing assistance.
  • Tier: 14,400 / 15,650 = 0.920, which is tier 2.
  • Heating: ne_liheap = $300, the same as integration Case 7.
  • Cooling expected: multi-family tier 2, $335.
  • Cooling actual, by hand trace: fuel_reported is false at line 46, so ne_liheap_cooling_eligible is false and ne_liheap_cooling is $0.

Fix: drop fuel_reported, since the amount never depends on fuel. At a minimum, let it pass wherever heating would pay, for example fuel_reported | multifamily | heat_expense_included_in_rent. Then update Case 5's UNSPECIFIED expectation, add the household above as a case, and correct the body sentence. If the gate stays as a deliberate cross-state convention, call it a PolicyEngine convention and say that Nebraska heating pays multi-family homes with an unspecified fuel.

2. Renters with cooling in the rent get $0, the opposite of the heating variable's reading of the same plan sentence, and the choice isn't recorded

Where: ne_liheap_cooling_eligible.py lines 37-41 and 49, and the last "Not modeled" bullet in the body.

The two plan sentences are the same:

  • FY2026 plan item 3.3, p. 11, cooling: "For renters with utilities included in rent, the household must be responsible for a portion of the cooling utilities."
  • Item 2.3, p. 9, heating: "For renters with utilities included in the rent, the household must be responsible for a portion of the heating."

ne_liheap_eligible.py (lines 26-43 at the head) reads the heating sentence as satisfied by a market-rate renter with heat in the rent: "Market-rate renters with heating paid through rent are treated as responsible even when the separately reported expense is zero: 476 NAC 3-002.02 lets the Department pay a household directly 'if utilities are included in rent'." Its code is responsible = pays_heating_bill | (heat_in_rent & ~subsidized).

That reading carries over to cooling:

  • 476 NAC 3-002.02, p. 1 ("Only if utilities are included in rent ... may the Department pay a household directly") covers every payment type in 3-001, including "(B) Cooling".
  • FY2026 plan item 1.1, p. 5 lists, among the cases where a supplemental payment for households "that received heating assistance or ... cooling assistance" goes to the household: "An economically vulnerable household's utilities are included in rent."

The PR instead requires has_cooling_expense, which the variable defines as cooling costs "separately from its rent or mortgage". That excludes every renter whose cooling is in the rent, market-rate or subsidized.

The line 40 comment says "Cooling paid only through rent has no existing input". The facts the heating test uses for this case do exist: heat_expense_included_in_rent, receives_housing_assistance and is_in_public_housing, and tenant_pays_utilities for utilities generally.

The body says "Source-conflict register: not applicable". The rulespec-us issue calls this $0 "a settled modeling decision", but nothing in the PR records who settled it.

Household:

  • Inputs: the same 72-year-old in an apartment, with all utilities in the rent (heat_expense_included_in_rent: true, heating_type: ELECTRICITY, has_cooling_expense: false), market rate.
  • Heating: ne_liheap = $300, because heat is in the rent and she isn't subsidized (ne_liheap_eligible.py line 43).
  • Cooling actual: $0 (line 49).
  • Cooling expected under the heating variable's reading of the identical sentence: multi-family tier 2, $335.

Fix, either:

  • (a) mirror heating, for example has_cooling_expense | (utilities in rent & ~subsidized), and add a test; or
  • (b) keep the $0 but record the choice, as a source-conflict candidate or a ruling in this thread. Then reword the body so it doesn't say plan 3.3 requires $0, and say why cooling departs from heating's reading.

3. Mutation gap: the citizenship term on the young-child route has no test

Where: ne_liheap_cooling_eligible.py line 34 (included & (age < p.adc_child_age_limit)).

ne_liheap_cooling_eligible.yaml Case 5 pins included only on the age-70 route (line 36). By my reading, no case in the three files changes if included & is deleted from line 34.

476 NAC 2-002.02, p. 1 says: "A person who does not meet the citizenship and residency requirements must be excluded from a household for purposes of determining eligibility."

Household:

  • Inputs: a mother aged 28 receiving ADC (tanf: 5_920) with a citizen child aged 8 and an undocumented child aged 4 (immigration_status: UNDOCUMENTED).
  • Expected: ne_liheap_cooling_eligible false, because the only child under 6 is excluded.
  • Without the term: true.

Fix: add this case to ne_liheap_cooling_eligible.yaml.

Suggestions

  1. Cooling cost for every household. 476 NAC 2-003 has no equipment requirement. 476 NAC 2-005.02 (p. 4) says: "To qualify for assistance to purchase a window air conditioning unit, the household must be eligible for cooling assistance". So a household with no air conditioner can be cooling-eligible. Plan item 8.3, p. 22 also describes a "mass run" that determines cooling eligibility for households with a current application. The body labels has_cooling_expense as the shared all-state input, which is fine as a convention. Add a "Not modeled" line saying that a household with an electric account but no reported cooling cost is paid by Nebraska and gets $0 here.

  2. Funding contingency and the FY2027 table. Two plan passages make the FY2027 table less certain than "verified" suggests:

    • Plan item 1.1 (p. 4): "The availability of the cooling assistance program and the variables used to determine eligible households will depend on the LIHEAP funding received and available for the current federal fiscal year."
    • Item 3.5 in both plans: "The LIHEAP Cooling Season Payment Table (matrix) is subject to update (closer to the cooling season) based on funding availability."

    Say in the ne_liheap_cooling documentation that FY2027 rests on the 10/01/2026 print and may be revised before June 2027. The program is funded and operated: cooling gets 18% of funds in FY2026 and 27% in FY2027 (item 1.2, p. 5 of each plan), and 13,099 households received cooling in FY2023 (HHS FY2023 Nebraska profile).

  3. Moving URL. The FY2027 references point at DHHS's undated Guidance Docs/...Guidance Document.pdf, which DHHS replaces in place each year; a FY2026 print sat at the same address in July 2026. Add an archived copy, such as a Wayback snapshot, next to it so the 2027 citation keeps opening on the 2027 table.

  4. Dwelling-type default. ne_liheap_dwelling_type defaults to SINGLE_FAMILY. For cooling, that is $700/$595/$525/$478 instead of $385/$335/$300/$273, a gap of $205 to $315 by tier. The heating dwelling-type documentation says so, but the cooling documentation and the body don't. Add one line.

  5. Reference titles. earned_income_disregard.yaml line 9 and payment_size_limit.yaml now title the reference "...Heating and Cooling Payment Computation #page 2-3". That puts page text in the title, and the single href #page=2 opens only on the heating page. Split each into two references with plain titles: #page=2 for heating and #page=3 for cooling.

  6. Size-6+ footnote and a stale comment.

    • The comment moved into ne_liheap_payment_fpg_ratio.py (lines 22-26) records that the footnote states the size-6+ tiers in "gross countable income", while the instructions apply the disregard first. That choice now decides cooling too, since page 3 has the same footnote. Consider recording it as a candidate.
    • ne_liheap_fpg.py still says "The annual period represents the heating season ending in that year". It could say "federal fiscal year (heating and cooling)", matching the re-described fpg_year_lag.

Verified correct

A separate regular cooling benefit exists in both claimed years.

  • 476 NAC 3-001 lists "(A) Heating; (B) Cooling" as separate payment types, and 476 NAC 2-003 sets the cooling test.
  • 476 NAC 1-004.04 sets the cooling season as "June 1 through August 31 of each federal fiscal year".
  • Plan item 1.1 gives 06/01/2026-08/31/2026 (FY2026, p. 4) and 06/01/2027-08/31/2027 (FY2027, p. 4).
  • The DHHS Energy Assistance page describes the cooling payment.

Heating and cooling in one year: both are paid, so there is no double count. Nothing limits cooling to households without heating help. The supporting text:

  • The DHHS page: "Payments are made one time per heating season" and, separately, "Payments are made one time per cooling season to utility providers".
  • 476 NAC 2-001.02: "An approved application provides household eligibility for the remainder of the program year." The program year (1-004.14, October 1 through September 30) contains both seasons.
  • Plan item 1.1: supplements "for LIHEAP-eligible households that received heating assistance or for LIHEAP-eligible households that received cooling assistance".
  • Plan item 8.3: a cooling mass run for households with a current case "including LIHEAP".
  • ne_liheap_cooling.yaml Case 3 pins both payments: $550 + $595 = $1,145.

Amounts and tiers.

  • Single-family $700/$595/$525/$478 and multi-family $385/$335/$300/$273 appear in both the 10/1/2025 document (p. 3) and the 10/01/2026 document (p. 4). Plan item 3.6 gives $273 to $700 in both years.
  • Tier tops are 70%, 100%, 130% and 150% of the 2025 guideline for FY2026 (one person at 100%: $15,650) and of the 2026 guideline for FY2027 ($15,960; two people $21,640 = 15,960 + 5,680).
  • The size-6+ footnotes match the six-person guideline: $30,205 = 0.7 × 43,150, $56,095 = 1.3 × 43,150, and for FY2027 $31,052 = 0.7 × 44,360.
  • "Use the row with the higher amount" means the next income row up. The footnote confirms it ("$30,205.01 through $43,150 is eligible for Tier 2"), so right=True is right.

Periods. The parameters are dated 2025-10-01 and are read on 1 January. So period 2026 is FY2026, whose cooling season is summer 2026, with the 2025 guidelines. Period 2027 uses the 2026 guidelines (gov.hhs.fpg 2026-01-01: 15,960 / 5,680). No options=[ADD]. The only MONTH read is immigration status for the first month, the same as household size.

Income test. Plan item 3.1 sets 150% of the HHS guidelines, the same as heating item 2.1 and 476 NAC 2-002.01. ne_liheap_income_eligible is reused, and it sits above the federal 110% floor.

Qualifying routes.

  • age >= 70 and age < 6 with tanf > 0. tanf sums ne_adc (tanf_if_takes_up lists "ne_adc # Nebraska Aid to Dependent Children"), so positive tanf is Nebraska ADC receipt.
  • Routes (C) and (D) have no inputs, and that is disclosed in the body, the code and programs.yaml.
  • Disability correctly does not qualify (eligibility Case 4).
  • The plan's wording "5 years and under and ADC-eligible" sits next to its own quote of the rule ("receives"); the PR follows 476 NAC 2-003(A).

Citizenship. Qualifying members must pass 476 NAC 2-002.02 through the same SNAP status variable used for household size (Case 5).

Fuel type. 476 NAC 3-003.01 lists fuel type among the payment factors, and FY2027 plan item 3.5 checks it, but neither cooling table varies by fuel. The PR follows the tables, and a home with no heating (NONE) is paid (Case 5).

Refactor.

  • ne_liheap_payment_fpg_ratio is line for line main's computation: gross minus 20% of earned income, floored at 0, divided by the guideline for clip(size, 1, 6) with the lag.
  • Its only readers are ne_liheap and ne_liheap_cooling, so ne_liheap returns main's values for every household.
  • No existing test file changed, and every heating case still passes in CI.
  • The re-described parameters are read only by Nebraska LIHEAP variables.
  • No source_conflicts record or # Source conflict marker names a Nebraska LIHEAP file. The PR does not touch candidates.yaml. The moved comment matches none of the ratchet's patterns.

National total and registry.

  • The new list line sits in the single 2023-01-01 block. It is a dollar amount on the SPM unit for the year, and it is gated through ne_liheap_cooling_eligible to StateCode.NE, which satisfies the state-gate chain check. It is not a component of ne_liheap.
  • The second Nebraska implementation in programs.yaml has a unique registry label. By my reading, test_liheap_total_sums_every_registered_state_payment finds ne_liheap_cooling in the list, and LIHEAP_PAYMENT_STAND_INS doesn't name it.
  • Case 3 fails if the list line is deleted.

Mixed-state lookups. There are no state-gated int or enum keys. The dwelling type is compared, not used as a key, and the brackets read a float ratio.

Mutation coverage, apart from Should 3. Each term has a case that fails without it:

Term Pinned by
income test cooling Case 6
has_cooling_expense cooling Case 7
fuel_reported cooling Case 5
age 70 edge eligibility Case 1
age 6 edge eligibility Case 2
ADC term eligibility Case 3
citizenship on the age-70 route eligibility Case 5
dwelling branch cooling Cases 2 and 4
each closed tier edge (right=True) cooling Cases 1 and 2
disregard cooling Case 9 and ratio Case 1
six-person cap ratio Case 2
floor at 0 ratio Case 3
FY2027 lag ratio Case 4 and cooling Case 8
list line cooling Case 3

Hygiene. Descriptions follow the template. Labels, units, periods and defined_for are present. References open on the values: the 10/1/2025 document #page=3, the 10/01/2026 document #page=4, 476 NAC 2 #page=2 and the plan #page=11/#page=12. The changelog fragment is in changelog.d/, and the test files are named after their variables.

Hand checks (2025 guideline $15,650 for one person and $21,150 for two; FY2027 $15,960):

  1. Cooling Case 1: 10,955 / 15,650 = 0.70, which is tier 1, $700. 10,955.01 is above 0.70, so $595.
  2. Cooling Case 1: 15,650 is 1.00, $595. 15,650.01 gives $525.
  3. Cooling Case 1: 20,345 is 1.30, $525. 20,345.01 gives $478. 23,475 is 1.50, $478. −100 floors to 0, $700.
  4. Cooling Case 2, multi-family: 10,955 gives $385, 10,955.01 gives $335, 15,650.01 gives $300, 20,345.01 gives $273.
  5. Cooling Case 3: 14,400 / 15,650 = 0.920, tier 2. Heating utility column $550 plus cooling $595 = $1,145.
  6. Cooling Case 4: 5,920 / 21,150 = 0.280, multi-family tier 1, $385.
  7. Cooling Case 6: 24,000 is above 1.5 × 15,650 = 23,475, so income ineligible and $0.
  8. Cooling Case 8 (2027): 14,400 / 15,960 = 0.902, tier 2, $595.
  9. Cooling Case 9: 28,800 − 0.2 × 15,600 = 25,680. 25,680 / 21,150 = 1.214, tier 3, $525. Without the disregard, 1.362 would give $478.
  10. Ratio Case 1: (17,000 − 3,400) / 15,650 = 0.86901.
  11. Ratio Case 2: 31,000 / (15,650 + 5 × 5,500) = 31,000 / 43,150 = 0.71842.
  12. Eligibility Cases 1 and 2: age 69 is false and 70 is true; a child aged 5 with ADC is true and aged 6 is false.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2189 queued is valid. Issue #2189 is OPEN and labelled pe-parity, and it names this head (a3d04b54c5). It is dispatch-ready: proposed module paths, corpus citations with status, verbatim 476 NAC, Guidance and plan text, required outputs, and companion tests worked from the sources rather than from PolicyEngine output.

Two things to refresh:

  • It still says "CI has not yet run its tests at this head" and "They have not yet passed in CI at this head". All 18 cases have now passed.
  • If Should 1 or 2 changes behavior, update the ne_liheap_cooling_eligible required-outputs row, companion test 11 and items 7-8 to match.

CI status

All 36 checks pass at a3d04b54c5 (gh pr checks 10758), and none are pending. Quick Feedback ran the three new test files: 9 passed (ne_liheap_cooling.yaml), 5 passed (ne_liheap_cooling_eligible.yaml) and 4 passed (ne_liheap_payment_fpg_ratio.yaml), matching the body's 18 cases. The state shards, which hold the existing Nebraska heating tests, passed. The run's base includes main through c65b162782, after #10287 and #10730.

Branch status

The head is 12 commits behind main (cc486336d7). git merge-tree --write-tree against main reports no conflicts. Since the head's last merge, main has changed only one programs.yaml line (Hawaii) and source_conflicts/candidates.yaml; neither overlaps this PR.

Pairwise test merges with all eight siblings are clean: AR #10744, GA #10746, OK #10748, LA #10750, KY #10752, SC #10754, AL #10756 and VA #10760. This PR edits regular_payments.yaml and programs.yaml but not candidates.yaml, so it cannot conflict with the siblings there.

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 three changes; none is critical, and the core is right. The cooling table matches both Guidance Documents cell by cell, the tiers, the 20% disregard, the guideline lag and the program-year dating match the sources, the heating refactor returns exactly what main returns, and Nebraska pays heating and cooling to the same household in one program year, so the national total doesn't double count.

  1. The UNSPECIFIED gate. Nebraska's own heating formula pays a multifamily home whatever the fuel (integration.yaml Case 7 pays $300 with heating_type: UNSPECIFIED), and the cooling amount never depends on fuel. The description's "as in every state LIHEAP program" isn't accurate here. Drop the term, or let it pass wherever heating pays and label it a convention.
  2. Renters with cooling in the rent get $0. Plan item 3.3's cooling sentence is word for word the heating sentence in item 2.3, which ne_liheap_eligible reads as met by a market-rate renter with heat in the rent, and 476 NAC 3-002.02 covers cooling payments too. Mirror heating, or record the choice instead of "Source-conflict register: not applicable".
  3. The citizenship term on the young-child route has no test, by my reading. A case is sketched in the report.

Details and households are in the report above. CI is green at a3d04b54c5.

@hua7450

hua7450 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, David. This push (head a497a51822) applies the fixes in e07d4a9e60 and merges upstream main.

Should address

  1. UNSPECIFIED gate. Dropped. ne_liheap_cooling_eligible.py no longer reads heating_type; a comment notes the cooling table has no fuel column. ne_liheap_cooling.yaml Case 5 keeps its inputs and now expects [true, true] and [595, 595], with a "Was" comment. Your household is new Case 10: 72, apartment, heat in the rent, UNSPECIFIED, her own cooling bill, so heating $300 and cooling $335. The body's "as in every state LIHEAP program" sentence is gone; it now says cooling adds no heating-fuel condition and that Nebraska heating pays a multi-family home whatever its fuel. The same change is in all nine cooling PRs.

  2. Renters with cooling in the rent. Mirrored heating, your option (a). The test is now has_cooling_expense | (utilities_in_rent & ~subsidized): utilities in the rent means tenant_pays_utilities false with positive rent, and subsidized is heating's test (receives_housing_assistance | is_in_public_housing). I used tenant_pays_utilities because heat_expense_included_in_rent covers heat only; the AR, LA, KY and AL cooling PRs read the same input. The comment cites plan items 2.3 and 3.3 and 476 NAC 3-002.02. The variable now cites 476 NAC 3 (p. 1), and its plan entry covers pages 9 and 11-12. New cases in ne_liheap_cooling.yaml:

    • Case 11: your market-rate renter, paying the FY2026 Douglas County one-bedroom FMR ($1,148 a month), utilities in the rent, no cooling cost: heating $300, cooling $335.
    • Case 12: the same renter with housing assistance, then in public housing: $0 for both programs.
    • Case 13: rent-free, utilities paid by a relative: $0. This pins the rent > 0 term.
    • Case 14: pays her own utilities, no cooling cost: $0. This pins the tenant_pays_utilities term.

    The project lead adopted this reading on 2026-11-13, replacing the earlier $0 choice. The register line now says cooling reads item 3.3 as heating reads item 2.3, and the "Renters ... get $0" line under Not modeled is gone.

  3. Citizenship on the young-child route. Added your household as ne_liheap_cooling_eligible.yaml Case 6: a mother aged 28 on ADC ($5,920), a citizen child aged 8 and an undocumented child aged 4. It expects size 2, income eligible, and cooling eligible false; without included & it would be true.

Suggestions

  1. Cooling cost for every household. Applied. The formula comment calls has_cooling_expense a PolicyEngine convention of the all-state cooling work, since 476 NAC 2-003 names no air conditioner or separate cooling cost. The body's Not modeled section adds the line and cites plan item 8.3. It leaves out the electric account, because every household is assumed to hold an active energy account.
  2. Funding contingency. Applied. The ne_liheap_cooling documentation says FY2027 rests on the October 1, 2026 print, which FY2027 plan item 3.5 makes subject to update closer to the cooling season. The variable now cites that plan at #page=12, and the body's 2027 bullet says the same.
  3. Moving URL. Not added yet. The Wayback Machine's latest capture of the Guidance Docs address is from 2026-01-01, before the 10/01/2026 print was posted, so no archived copy of that print exists to cite.
  4. Dwelling-type default. Declined. Every state LIHEAP program uses the same single-family default and callers report the dwelling type, so there is no per-state note.
  5. Reference titles. Kept. The repo's reference rule is one entry per source, with the href on the first cited page and the page list in the quoted title (#page 2-3).
  6. Size-6+ footnote and stale comment. The footnote reading stays recorded in the ne_liheap_payment_fpg_ratio.py comment, with no candidate. The ne_liheap_fpg.py comment now says the period is the federal fiscal year ending that year, with its heating season and its June-August cooling season.

Axiom issue. TheAxiomFoundation/rulespec-us#2189 now points at this head. Updated: the review and CI sentences, the scope, the ne_liheap_cooling_eligible output row and inputs, test counts (24: 14, 6 and 4), companion test 11 (UNSPECIFIED now pays $595), test 10(b), new companion tests 13 to 18, and interpretations 6, 7, 8 and 13.

Tests were not run locally.

CI is running at a497a51822.

@hua7450
hua7450 requested a review from DTrim99 November 13, 2026 20:36
@DTrim99

DTrim99 commented Nov 15, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Add Nebraska LIHEAP regular cooling assistance

Summary

All three round-1 requests are fixed at a497a51822, and each fix has a test that pins it:

  • Fuel gate. The heating-fuel condition is gone. Both Guidance Documents leave fuel out of the cooling instructions, while the heating instructions one page earlier list it. Cooling Case 5 (second row) and new Case 10 pin the change.
  • Renters. Renters with utilities in the rent now get the reading heating gives the same plan sentence: an unsubsidized renter qualifies, and a subsidized one needs its own cooling cost. Cases 11 to 14 pin the new route and each of its terms.
  • Citizenship. New eligibility Case 6 pins the citizenship term on the young-child route.

The fix changes nothing for a household that was right at round 1. With tenant_pays_utilities at its default (true), the new route is closed and the test is has_cooling_expense, as before. The fix commit doesn't touch the heating code.

CI is green at this head (36 of 36), and the PR's three test files ran there: 14, 6 and 4 cases passed. The branch merges cleanly with current main, which now includes Arkansas cooling (#10744), and with each still-open sibling.

Two suggestions remain, and neither blocks approval:

  • The body and the reply say the project lead adopted the renter reading on 2026-11-13, but this PR has no record of it.
  • The parity issue still says the tests have not passed in CI at this head.

Basis: I did not run the tests. The results below rest on hand traces of the head code and on reading the tests. CI facts come from gh pr checks and the Quick Feedback job log. Branch facts come from git merge-tree, which I ran.

Prior findings

Finding Status Evidence
Should 1: the UNSPECIFIED-fuel gate excludes households Nebraska heating pays Fixed fuel_reported and the heating_type read are gone (ne_liheap_cooling_eligible.py line 60: "The cooling table has no fuel column, so heating_type is not read"). Cooling Case 5 now expects [true, true] and [595, 595]. New Case 10 is the round-1 household: heating $300, cooling $335. By my reading, both cases fail if the gate is restored. The body's "as in every state LIHEAP program" sentence is gone. Nebraska's sources support dropping the gate (see Verified).
Should 2: renters with cooling in the rent get $0, unlike heating's reading of the same sentence Fixed (option a) Lines 48-59: responsible = has_cooling_expense | (utilities_in_rent & ~subsidized). Here utilities_in_rent is tenant_pays_utilities false with positive summed rent, and subsidized is heating's own test. Case 11 pins the route, Case 12 pins each subsidy input, Case 13 pins rent > 0 and Case 14 pins ~tenant_pays_utilities (table below). The body's renter "$0" line is gone, and the register line now matches the code. The cited ruling has no record on this PR (Suggestion 1), but option (a) didn't need one.
Should 3: no test on the citizenship term of the young-child route Fixed New ne_liheap_cooling_eligible.yaml Case 6 is the round-1 household: an ADC mother, a citizen child aged 8 and an undocumented child aged 4. It expects size 2, income eligible true and cooling eligible false. By my reading, deleting included & from line 36 makes the output true.
Suggestion 1: a cooling cost for every household Taken The line 54-56 comment calls has_cooling_expense a PolicyEngine convention. A new "Not modeled" line cites plan item 8.3 (p. 22), where the "mass run" sentence is.
Suggestion 2: funding contingency for FY2027 Taken The ne_liheap_cooling documentation says FY2027 rests on the October 1, 2026 print, and the variable cites the FY2027 plan at #page=12. Item 3.5's "subject to update (closer to the cooling season) based on funding availability" is on that page.
Suggestion 3: archived copy of the moving URL Not taken (optional) The author says no Wayback capture of the 10/01/2026 print exists yet.
Suggestion 4: note on the dwelling-type default Not taken (optional) The author's reason: every state uses the same default.
Suggestion 5: split the reference titles Not taken. The author is right. CLAUDE.md asks for one entry per source, the href on the first cited page and the page list in the title (#page 2-3), which is what the files do.
Suggestion 6: size-6+ candidate and a stale comment Half taken (optional) ne_liheap_fpg.py now says "the federal fiscal year ending in that year, with its heating season and its June-August cooling season". The footnote reading stays in the ne_liheap_payment_fpg_ratio.py comment, with no candidate.

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

  1. Record the ruling, or drop the attribution.
  2. Refresh the CI sentences in Maryland Tanf test files #2189. The issue still says, in "Why queued" and in the Tests bullet, that the tests "have not yet passed in CI at this head". All 24 cases passed at a497a51822.

Verified

Nebraska's sources support dropping the fuel condition.

  • Guidance Document 10/1/2025, p. 3: the cooling amount "is determined based on the countable income of the household (income after the disregard is applied), household size, and household living arrangement." The heating instructions on p. 2 end the same sentence with "arrangement, and fuel type". The 10/01/2026 edition repeats both sentences, heating on p. 3 and cooling on p. 4.
  • 476 NAC 2-003, p. 2 sets no heating condition.
  • FY2026 plan item 3.5 (p. 12) leaves "Fuel type" unchecked. This rests on the rendered page read in round 1; I did not re-render it this round.
  • Two sources point the other way: 476 NAC 3-003.01 lists "(1) Fuel type" among the payment factors, and the FY2027 plan checks fuel type in 3.5. But neither cooling table has a fuel column, so no fuel can change the amount, and no Nebraska source makes an unreported fuel a bar.
  • The body's "Nebraska heating likewise pays a multi-family home whatever its fuel" matches ne_liheap.py line 36, where multifamily comes first in the select.

The renter route rests on the same sources as heating.

  • FY2026 plan item 3.3, p. 11: "For renters with utilities included in rent, the household must be responsible for a portion of the cooling utilities." Item 2.3 on p. 9 says the same for heating.
  • 476 NAC 3-002.02, p. 1: "Only if utilities are included in rent ... may the Department pay a household directly". 3-001 on the same page lists "(B) Cooling".
  • The subsidy test (receives_housing_assistance | is_in_public_housing) is the one in ne_liheap_eligible.py lines 40-42.
  • tenant_pays_utilities is a household fact that defaults to true, and its documentation says to "set it to false when utilities are included in rent". The Hawaii, Georgia and Nevada LIHEAP variables read it the same way. rent is a person-level amount, summed over the unit.
  • None of these inputs changed between the round-1 head and this head.

No regressions. The diff between the round-1 head and this head touches Nebraska files only through the fix commit. The two merges of main changed no Nebraska file and none of the shared inputs. When tenant_pays_utilities is at its default, utilities_in_rent is false and responsible reduces to has_cooling_expense, so the round-1 cases keep their results by hand trace. Case 7 (no cooling cost, defaults otherwise) still expects $0. Only two kinds of household change, and both changes are intended and disclosed: an UNSPECIFIED fuel (Should 1) and an unsubsidized renter with utilities in the rent (Should 2).

Each new term is pinned. "Without it" is by my reading; I did not run these mutations.

Term Pinned by Result without it
no fuel gate cooling Case 5 second row, Case 10 restoring the gate gives false and $0
has_cooling_expense | cooling Cases 3, 5 and 10 eligible false (the route is closed at the default tenant_pays_utilities)
| (utilities_in_rent & ~subsidized) cooling Case 11 cooling $0 instead of $335
~tenant_pays_utilities cooling Case 14 eligible true
rent > 0 cooling Case 13 eligible true
receives_housing_assistance cooling Case 12 first row eligible true
is_in_public_housing cooling Case 12 second row eligible true
included & on the young-child route eligibility Case 6 eligible true

In Case 12 the subsidy term is what decides the result. Income stays eligible because SNAP's unearned-income list, which ne_liheap_gross_income reads, does not include housing assistance.

Hand checks of every new or changed expectation. These use the 2025 guideline: $15,650 for one person and $21,150 for two.

  1. Cooling Case 5, second row: 14,400 / 15,650 = 0.920, tier 2, single-family $595. Heating is $0, because there is no bill and heat is not in the rent.
  2. Cooling Case 10: ratio 0.920. Heating takes the multi-family branch first, tier 2, $300. Cooling multi-family tier 2 is $335.
  3. Cooling Case 11: tenant_pays_utilities false, rent 13,776 > 0 and not subsidized, so she is responsible. Heating is $300 (heat in the rent, unsubsidized). Cooling is $335.
  4. Cooling Case 12: income eligible (14,400 ≤ 23,475) and aged 72. With no cooling cost and a subsidy, neither household is responsible: false, false, $0, $0. Heating is also $0, because neither has a bill and heat-in-rent doesn't count when subsidized. The rent of $360 a month is 30% of 14,400 / 12.
  5. Cooling Case 13: rent is 0, so the route is closed. With no cooling cost: false, $0.
  6. Cooling Case 14: she pays her own utilities, so the route is closed. With no cooling cost: false, $0.
  7. Eligibility Case 6: the 4-year-old is excluded, so the size is 2. Gross income is the ADC grant of 5,920, which is at most 1.5 × 21,150 = 31,725, so she is income eligible. No included member is under 6 or 70 or older, so cooling eligible is false.

Case 11's rent ($1,148 × 12 = 13,776) only illustrates a market rent; any positive rent gives the same result. I did not check the Fair Market Rent figure.

References.

  • ne_liheap_cooling_eligible now cites 476 NAC 3 at #page=1, where 3-002.02 is.
  • It cites the FY2026 plan at #page=9, with the comment "PDF pages 9,11-12": item 2.3 is on p. 9, item 3.3 on p. 11 and items 3.4-3.5 on p. 12. That follows CLAUDE.md: the href opens on the first cited page.
  • The new FY2027 plan reference in ne_liheap_cooling opens on item 3.5.

Ratchet and registry.

  • No new or changed comment line matches the Tie source-conflict markers to the register shared with Axiom #10287 ratchet's patterns. No Nebraska LIHEAP file is in KNOWN_UNMARKED, at the head or on current main. The PR doesn't touch candidates.yaml. So the "not applicable" register line holds.
  • The fix doesn't change programs.yaml. The cooling note mentions neither fuel nor renters, so it stays accurate.

Body.

  • The "Not modeled" list no longer has the renter line.
  • The new has_cooling_expense line cites plan item 8.3 accurately.
  • The "24 cases in 3 files (14, 6 and 4)" count matches CI.

Input note (not a finding). Heating reads heat_expense_included_in_rent and cooling reads tenant_pays_utilities. The author's reason is sound: the first covers heat only. The body names the cooling input, and Case 11 sets both. A caller who sets only heat_expense_included_in_rent gets heating but not the cooling in-rent route.

Axiom

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

  • The issue is OPEN and labelled pe-parity. It was updated on 2026-11-13 at 20:36 UTC and names a497a51822.
  • Its required-outputs row for ne_liheap_cooling_eligible matches the code term by term. Its inputs list now includes tenant_pays_utilities, rent, receives_housing_assistance and is_in_public_housing.
  • Companion test 11 now pays $595 for UNSPECIFIED. Companion tests 13 to 18 match eligibility Case 6 and cooling Cases 10 to 14, with expected values worked from the quoted sources. Interpretations 7 and 8 match the fix.
  • It is still dispatch-ready.
  • Two things to refresh: the stale CI sentences (Suggestion 2) and item 8's ruling attribution (Suggestion 1).

CI

All 36 checks pass, and none are pending (gh pr checks 10758). The run is 43501543647, on a497a51822; it started 2026-11-13 at 20:26 UTC and finished at 21:52 UTC.

  • Quick Feedback ran the PR's three test files: ne_liheap_cooling.yaml 14 passed, ne_liheap_cooling_eligible.yaml 6 passed and ne_liheap_payment_fpg_ratio.yaml 4 passed. That is the body's 24 cases, including all seven new or changed ones.
  • All four baseline state shards passed; they hold the existing Nebraska heating tests. The Python shards and the PR validation job (guard tests, lint, changelog) also passed.
  • The tests weren't run locally, so this run is their only execution.

Branch

The head is 57 commits behind main (59ab85c066, which merged #10744 at 01:09 UTC on 2026-11-15).

  • git merge-tree --write-tree against that main reports no conflicts. The merged tree's regular_payments list holds both ar_liheap_cooling and ne_liheap_cooling, and programs.yaml keeps both Nebraska entries.
  • Since the head's last merge, main has changed programs.yaml (other programs' entries), regular_payments.yaml (the Arkansas line) and one KNOWN_UNMARKED line for an IRS file. None of these overlaps Nebraska LIHEAP.
  • The CI run predates those commits. A re-run after merging main is optional, since nothing overlaps.

Pairwise merges with the open siblings are clean: GA #10746, OK #10748, LA #10750, KY #10752, SC #10754, AL #10756 and VA #10760. The other siblings (#10730, #10732, #10734, #10736, #10738, #10740, #10742, #10762 and now #10744) are on main, so the main check covers them.

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). All three round-1 items are fixed:

  • The fuel gate is dropped, and cooling Cases 5 and 10 pin it. Both Guidance editions leave fuel out of the cooling instructions.
  • Renters with utilities in the rent now take heating's route, and Cases 11-14 pin every term.
  • Eligibility Case 6 pins the citizenship term on the young-child route.

No regressions, all seven new or changed expectations check by hand, CI is green at a497a51822, and the branch merges cleanly with main and the open siblings. One of the two suggestions: the description, the reply and rulespec-us#2189 cite a 2026-11-13 project-lead ruling that has no record on this PR. Post the ruling here, as the Georgia, Oklahoma and Virginia PRs do, or drop the attribution.

@hua7450
hua7450 merged commit f2415d0 into main Nov 15, 2026
36 checks passed
@hua7450
hua7450 deleted the ne-liheap-cooling branch November 15, 2026 01:37
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 Nebraska LIHEAP regular cooling assistance

2 participants