Skip to content

Pay the Nevada LIHEAP Fixed Annual Credit to cooling-only households, assuming an energy account - #10738

Merged
hua7450 merged 3 commits into
mainfrom
nv-liheap-cooling
Nov 12, 2026
Merged

hua7450 merged 3 commits into
mainfrom
nv-liheap-cooling

Conversation

@hua7450

@hua7450 hua7450 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Pays Nevada's LIHEAP Fixed Annual Credit to households with no heating, including cooling-only households, by assuming every household has the energy account the manual requires.

Fixes #10737

What and why

Nevada calls its program "a combined year-round heating and cooling program" (FY2026 plan item 1.1; the FY2027 plan says the same). The credit counts "primary heating and electric energy source costs" (EAP Manual 803 III, p. 60). The account rule is that "An applicant household must have or qualify to have an active open account with a Nevada utility/energy vendor" (Section 500, p. 31). On main, nv_liheap_eligible stood in for that account with a heating fuel and a heating expense. A home with no heating, or a gas-heated home with a $0 gas bill, got $0.

PolicyEngine assumes every household has an active energy account and does not model the requirement. nv_liheap_eligible is now:

heating_type_reported = heat_in_rent | (heating_type != UNSPECIFIED)
utilities_in_rent = heat_in_rent | ~tenant_pays_utilities
housing_eligible = ~(utilities_in_rent & subsidized) | (nv_liheap_energy_cost > 0)
eligible = (size > 0) & (income <= limit) & heating_type_reported & housing_eligible
  • Any sent heating type, including NONE, qualifies on income and size. No heating bill, electricity bill or has_cooling_expense is required. UNSPECIFIED qualifies only with heat in rent, as on main.
  • Subsidized or public-housing tenants whose utilities are all in the rent still need an energy burden, meaning a separately billed energy cost (Section 501 I, p. 31; Sections 503 and 803 V.3). That is a separate rule, not the account test, so it stays.
    • Utilities count as in the rent when heat is in the rent, as on main, or when the household's existing tenant_pays_utilities input is false.
    • A home without heating has no heat in its rent, so only the second signal applies to it. Without it, a subsidized no-heat home with every utility in the rent would have been eligible.
  • The credit (nv_liheap) is unchanged: 12-month energy cost minus the median burden times income, with a $360 minimum that also applies to a negative figure (803 V.2, p. 61), capped by the benefit table, plus the $100 targeted supplement. With no heating, the cost is the electricity bill.

Test plan

Tests were not run locally. CI is their first run and starts when this PR leaves draft.

  • YAML tests with expected values computed from the FY2026 and FY2027 manuals:
    • 2026, no heating, $1,500 electricity: 1,500 − 0.0181 × 18,720 = $1,161.17, under the $1,280 band 3 cap.
    • 2026, no heating and no bills: the $360 minimum. The case now sends has_cooling_expense: false, so its inputs match its name.
    • 2027, no heating, $1,500 electricity: 1,500 − 0.0148 × 18,720 = $1,222.94.
    • 2026, two no-heat homes whose tenants pay no utilities and have no bills: the subsidized one is ineligible (501 I) and the unsubsidized one is eligible.
  • Two existing eligibility cases change from false to true, each with a comment giving the reason:
    • Case 3, a home with no heating and an electricity bill.
    • Case 10, a gas-heated home with a $0 gas bill.
    • Their names now say "is eligible". No other existing case changes.
  • Source-conflict register: candidate us-nv:liheap-minimum-fixed-annual-credit (from PR 10432, unreviewed) records the $360 minimum over the $240 the SFY2026 manual prints in sections 504 A, 505 and 702 III. The new cooling-only cases rely on it: in SFY2026 a home with no heating whose credit computes below $360 gets $360. The SFY2027 manual prints $360 throughout.
  • make format (ruff)
  • Changelog fragment in changelog.d/

Not modeled

  • The Section 42-only housing exception, roomers and boarders, master meters and solar vendor credits have no inputs.

Reviewer question

Should the Section 501 I exclusion for subsidized tenants with all utilities in rent stay? This PR keeps it and applies it to homes without heating through tenant_pays_utilities.

axiom: TheAxiomFoundation/rulespec-us#2176 queued

🤖 Generated with Claude Code

hua7450 and others added 3 commits November 8, 2026 22:03
Assume every household has the active energy account Section 500 requires,
so any sent heating type, including NONE, qualifies on income and size.

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

Treat utilities as included in the rent when heat is in the rent or the
tenant does not pay utilities, so a subsidized home without heating whose
utilities are all in the rent needs an energy burden (Sections 501 I, 503
and 803 V.3). Add a mixed-household eligibility case and send
has_cooling_expense explicitly in the benefit case that tests it.

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

codecov Bot commented Nov 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aae7f98) to head (fd132c5).
⚠️ Report is 157 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10738   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines           37        21   -16     
=========================================
- Hits            37        21   -16     
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 Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Pay the Nevada LIHEAP Fixed Annual Credit to cooling-only households, assuming an energy account

PR #10738 (author: hua7450), head fd132c51e5. First review. The PR changes one formula (nv_liheap_eligible), two test files, the Nevada registry note and adds a changelog fragment. It touches no parameter, no partner test file and not the credit formula.

Path prefixes:

  • V/ = policyengine_us/variables/gov/states/nv/dwss/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/nv/dwss/liheap/

Sources read for this review (each downloaded and searched as text):

  • SFY2026 EAP Manual, 115 pages (p. 31).
  • SFY2027 EAP Manual, 115 pages (p. 31).
  • FFY2026 plan, 52 pages (p. 4).
  • FFY2027 plan, 53 pages (p. 4). This file is a scan; I read its OCR text layer and could not render pages, so its check-box states are not verified here.
  • NRS 702.260 and the Division's Eligibility Criteria page.

Summary

The change is right against the sources, and I found nothing that blocks it: 0 critical, 0 should, 6 suggestions.

  • Nevada does pay its regular credit to a cooling-only household. There is one credit, it counts the electric bill whether or not the home is heated, and no source sets a heating or cooling condition. Every quote and page in the PR body checks out.
  • The amount is right, not just nonzero. A home with no heating is credited on its electricity bill alone, under the standard cap table, with the $360 minimum. The credit formula is unchanged.
  • A household with $0 of every energy cost now receives $360. The manual pays the minimum in each of those situations (702 III, 803 V.2, 504 A, 505 D), except to subsidized tenants with all utilities in the rent, whom the head still excludes.
  • Nobody who qualified on main loses or changes amount. The head's eligible set is a superset of main's, and nv_liheap is not edited.
  • Every new or changed term is pinned by a test.
  • CI is green at this head (39 pass, 0 fail), with all 63 Nevada cases run, and the branch is level with main.

The suggestions are about two input combinations that depend on flags the caller may not send, a source conflict worth recording, and test and housekeeping polish.

Answer to the reviewer question (keep the 501 I exclusion?): yes, keep it. Both manuals state it four times (501 I, 503, 702 III, 803 V.3) and both plans repeat it in item 2.3. Applying it to homes without heating through tenant_pays_utilities is the right extension.

Critical

None.

Should address

None.

Suggestions

G1. A subsidized household with no energy cost is paid $360 unless the caller sends an in-rent flag (V/eligibility/nv_liheap_eligible.py:49-56).

  • Rule. Section 503 (p. 34): "Persons/households residing in public/subsidized housing where their rent/mortgage payments include utilities and they are not being billed separately for their energy costs are ineligible for an EAP benefit unless they have an energy burden."
  • Household. One adult, wages $18,720, receives_housing_assistance: true, heating_type: NATURAL_GAS, gas $0, electricity $0, and neither heat_expense_included_in_rent nor tenant_pays_utilities sent.
    • Head: utilities_in_rent is false because tenant_pays_utilities defaults to true, so the household is eligible and gets 0 − 338.832 → $360.
    • main: $0 (no heating expense).
    • The same household with tenant_pays_utilities: false gets $0 at the head (Case 13).
  • Why only a suggestion. Given the inputs as sent, the head is right: the tenant is said to pay utilities, so 501 I does not apply. The body discloses that a $0 bill no longer excludes a household. But a subsidized tenant with no energy cost at all almost always has utilities in the rent, and the result now turns on a flag that defaults the other way.
  • Fix (either).
    • Say in the code comment and the programs.yaml note that a subsidized household with utilities in the rent must send heat_expense_included_in_rent: true or tenant_pays_utilities: false.
    • Or drop the flags for subsidized households: housing_eligible = ~subsidized | energy_burden. That needs no flag and matches 503's "unless they have an energy burden"; its cost is the rare 505 D case (landlord's account, not in the rent, not billed).

G2. tenant_pays_utilities: false can exclude a household but cannot qualify one (V/eligibility/nv_liheap_eligible.py:39-41).

  • Rule. Section 803 V.3 (p. 61): "If all utilities are in the landlord's name and included in the rent, and the household does not receive a separate bill that includes consumption and dollar usage, the household's benefit amount is $360."
  • Household. One adult, wages $18,720, unsubsidized, tenant_pays_utilities: false, no bills, heating_type not sent, heat_expense_included_in_rent not sent.
    • Head: heating_type_reported is false, so $0.
    • Expected under 803 V.3: $360. The same household with heat_expense_included_in_rent: true gets $360 (Case 5).
  • Why only a suggestion. An unspecified fuel paying $0 is the convention across the LIHEAP states (Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754). The new point is that the PR adds a second "utilities in the rent" signal and uses it only on the excluding side.
  • Fix. Define utilities_in_rent first and use it in both places: heating_type_reported = utilities_in_rent | (heating_type != UNSPECIFIED). Add a case for the household above.

G3. Record the plan's renter sentence as a source-conflict candidate.

  • The conflict. Plan item 2.3, "Renters with utilities included in the rent?" (FFY2026 p. 8; the FFY2027 plan repeats it on pp. 8 and 11): "Renters living in housing where all utilities are included in the rent, and they are not billed separately for their energy costs are ineligible for an EAP benefit unless they have an energy burden." That sentence has no subsidized-housing qualifier.
  • The other side. Manual 504 A and 803 V.3 pay such unsubsidized households the minimum, in both years. So does item 2.5 of the same plan (FFY2027 p. 9): "If all utilities are in the landlord's name and included in the rent, and the household does not reside in subsidized housing, and does not receive a separate bill that includes consumption and dollar usage, the household will receive an annual payment of $360."
  • The head follows the manual and plan 2.5, which I agree with: the 2.3 sentence looks like a copy of the subsidized-housing line above it. This reading is already on main (Cases 5 and 6). This PR extends it to homes without heating and adds a test that depends on it (Case 13, second household: $360 by the manual, $0 by plan 2.3).
  • Fix. Add a us-nv: entry to source_conflicts/candidates.yaml (follows: gap-fill, state: unreviewed), as was done for the $240/$360 minimum. NRS 702.260 is silent, so no rule is overridden.
  • While there. The two existing Nevada entries (candidates.yaml:7827 and :7859) still say "Code not yet on main" with code: [], although Add Nevada Energy Assistance Program Fixed Annual Credit #10432 merged on 2026-10-30. The body says the new cases rely on the minimum entry, so add this PR to its prs and fill in code.

G4. Pin the amounts of the changed cases, not only eligibility.

  • T/eligibility/nv_liheap_eligible.yaml Cases 3, 10 and 13 assert nv_liheap_eligible only. Their comments state the credit ("the credit pays the minimum"), but no output checks it.
  • Add nv_liheap:
    • Case 3: 1,320 − 338.832 = 981.168.
    • Case 10: 0 − 338.832 → 360.
    • Case 13: [0, 360].
  • Optional: a cooling-only household at the cap with the targeted supplement (age 60, electricity $2,400: 2,061.168 against 1,280 + 100 = 1,380). The queued Axiom issue already carries this as companion test 2.

G5. Cite the manual's affirmative text for paying a household with no bill (V/eligibility/nv_liheap_eligible.py:33-38).

  • The comment rests the no-bill result on an assumption about Section 500. The manual also says it directly, which is stronger:
    • 702 III (p. 43): "Households meeting program eligibility income guidelines, but ineligible for a fixed annual credit (benefit amount), are eligible for $240 in energy assistance" ($360 in SFY2027).
    • 505 D (p. 36): a household whose utility accounts are "in the landlord's name, not included in the rent or billed to the household ... may receive the minimum payment".
  • Add "702 III, 505 D" to the comment and to the test file header.

G6. Housekeeping.

  • The body's test plan says "Tests were not run locally. CI is their first run." CI has now run them green at this head; update the line.
  • The Axiom issue says twice that the tests "have not yet passed in CI at this head". Update it too.

Verified correct

1. Does Nevada pay the regular credit to a cooling-only household? Yes, in both years.

  • One combined program. Plan item 1.1, p. 4 of both plans: "Nevada has a combined year-round heating and cooling program. All the information will be provided in the Heating Assistance section." The FFY2027 plan also prints cooling dates (10/01/2026 to 09/30/2027), and its section 3 says "same as Heating Assistance". Neither plan has a separate cooling matrix.
  • The credit counts electricity. Section 803 III, p. 60 of both manuals: "Include primary heating and electric energy source costs." Appendix A, p. 110: "The Nevada median household energy burden for electricity and heating is 1.81%" (1.48% in SFY2027).
  • No heating condition anywhere.
    • NRS 702.260(3) conditions eligibility on income alone, and (7)(a) sets the amount by income "spent on natural gas and electricity".
    • The manual glossary (p. 106) defines the credit by income "spent on heating/cooling".
    • The Division's eligibility page asks only that the household be "at least partly responsible for home heating or cooling costs by paying a utility company, fuel supplier, or landlord directly".
  • Sources agree on this point, so no conflict marker is needed for it.

2. Quotes and pages in the PR body. All found at the cited place:

Claim Where Result
"a combined year-round heating and cooling program" FFY2026 and FFY2027 plans, item 1.1, p. 4 Found in both
"primary heating and electric energy source costs" 803 III, p. 60 Found in both manuals
"An applicant household must have or qualify to have an active open account with a Nevada utility/energy vendor" Section 500, p. 31 Found in both manuals
Subsidized tenants with all utilities in the rent 501 I p. 31; 503 p. 34; 803 V.3 p. 61 Found in both manuals
$360 minimum "to include a negative figure" 803 V.2, p. 61 Found in both manuals
$240 in the SFY2026 manual 504 A p. 34; 505 D, F, G pp. 36-37; 702 III p. 43 Found; the SFY2027 manual prints $360 in each

The reference change from #page=5 to #page=4 on both plans is right: item 1.1 is on p. 4.

3. Is the amount right?

  • What the credit reads. nv_liheap_energy_cost is heating_expense plus the electricity bill (not added twice for electric or solar heat). heating_expense is 0 for NONE, so a home without heating is credited on electricity alone. That is 803 III's "electric energy source costs".
  • Formula. min(max(cost − burden × FAC income, $360), cap). Unchanged from main.
  • Cap. NONE uses the table headed "ALL ENERGY SOURCES (except propane and oil)". The formula picks the table from a boolean, so there is no fuel key to miss.
  • A gas home with a $0 gas bill is credited on its electricity bill, or gets $360 if it has none (803 V.2).
  • $0 of every energy cost gives $360. The manual pays the minimum to every such household except subsidized all-in-rent tenants and roomers:
    • a negative figure (803 V.2);
    • an income-eligible household "ineligible for a fixed annual credit" (702 III);
    • all utilities in unsubsidized rent (504 A, 803 V.3);
    • a landlord's account that is neither in the rent nor billed (505 D).
  • So the "assumed account" is defensible. Section 500 accepts a household that can "qualify to have" an account, and the four passages above pay households with no bill.
  • Heat and electricity both in the rent. Unsubsidized: $360 (Cases 5, 6 and 13). Subsidized: ineligible unless a separately billed cost exists (Cases 7, 8, 9 and 13). Both match the manual.

4. Line-by-line comparison with main.

  • Heat in rent. main: ~subsidized | energy_burden, with any fuel but NONE. Head: the same test, and NONE no longer excluded.
  • Heat not in rent. main: a known fuel other than NONE and has_heating_expense. Head: any sent fuel including NONE, and not (tenant pays no utilities, subsidized and energy cost 0).
  • Superset. A household eligible on main without heat in rent had a positive heating bill, so its energy cost is positive and it passes the head's housing test. One with heat in rent passes the identical test. No one drops out.
  • No double payment. There is one variable and one credit. A household with heating and cooling costs gets the same amount as on main.
  • Callers. nv_liheap_eligible is read only by nv_liheap (defined_for). No Nevada file reads has_heating_expense any more.

5. Changed expectations (27 deleted lines). The deletions are 19 formula lines, 1 registry line and 7 test lines: 3 header comment lines, and the name and output of Cases 3 and 10. Both flips are intended and right:

  • Case 3 (NONE, electricity $1,320, wages $18,720): 18,720 ≤ 23,475, fuel sent, unsubsidized → true.
  • Case 10 (gas, gas $0, no electricity): the same → true.

No expectation in T/nv_liheap.yaml changed; that file only gains Cases 26-28.

6. Hand-checks. SFY2026, one person: limit 15,650 × 1.5 = 23,475; expected payment 0.0181 × 18,720 = 338.832.

  1. Case 26. Cost 0 + 1,500. 1,500 − 338.832 = 1,161.168. 18,720 / 15,650 = 1.196, band 3, cap 1,280. Result 1,161.168.
  2. Case 27. 0 − 338.832 = −338.832 → 360; eligible true.
  3. Case 28 (SFY2027). Limit 15,960 × 1.5 = 23,940. 0.0148 × 18,720 = 277.056. 1,500 − 277.056 = 1,222.944. 18,720 / 15,960 = 1.173, band 3, cap 1,280.
  4. Case 3. true (credit would be 981.168).
  5. Case 10. true (credit would be 360).
  6. Case 13, first household. No utilities paid and subsidized, cost 0 → false.
  7. Case 13, second household. Unsubsidized → true.
  8. Case 4. UNSPECIFIED without heat in rent → false, even with has_heating_expense: true.
  9. Case 5. UNSPECIFIED with heat in unsubsidized rent → true.
  10. Cases 7 and 8. Heat in rent, subsidized or public housing, cost 0 → false.
  11. Case 9. Heat in rent, subsidized, electricity 1,320 → burden → true.
  12. Cases 1 and 2. 23,475 → true; 23,476 → false.

7. Mutation check. Each term fails a test when removed:

Term Pinned by
NONE no longer excluded Case 3; Case 13 second household; Cases 26-28
heating_type != UNSPECIFIED Case 4
heat_in_rent in heating_type_reported Case 5
has_heating_expense no longer required Case 10
~tenant_pays_utilities in utilities_in_rent Case 13 first household
heat_in_rent in utilities_in_rent Cases 7 and 8
subsidized Cases 7 and 8; Case 13 (false against true)
energy_burden rescue Case 9

8. Other checks.

Axiom line assessment

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

  • State. OPEN, labelled pe-parity, last updated 2026-11-12 04:17 UTC.
  • Matches the head. It names head fd132c51e5, gives the four-part eligibility definition as coded (including tenant_pays_utilities), and lists the 13 and 28 case counts.
  • Dispatch-ready. It has module paths with corpus status, verbatim source text, required outputs and 10 companion tests derived from the manuals. Its tests 5, 6 and 9 already carry the amounts G4 asks for.
  • It records the open points. Interpretation 7 notes that no candidate entry covers the plan 2.3 sentence (G3), and interpretation 6 notes the tenant_pays_utilities default (G1).
  • One stale line. See G6.

CI status

39 pass, 0 fail, 0 pending, 3 skipped. Run 43153811436 tested head fd132c51e5. The 3 skipped entries come from an earlier run at the same head (43151366035, 03:55 UTC), in which the test workflows were skipped; the later run ran them in full. I re-checked gh pr checks 10738 at the end of this review.

  • Nevada tests. Baseline states-shard-3 ran all 7 files: 13 + 3 + 5 + 9 + 2 + 28 + 3 = 63 cases, all passing. Quick Feedback ran them too.
  • Locally. I ran no tests or model probes. The examples in G1 and G2 are hand traces of the head formula.

Branch status

0 critical, 0 should and 6 suggestions 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. Nevada pays one year-round credit that counts the electric bill with no heating or cooling condition, the credit formula is unchanged, nobody who qualified on main changes, and every new term is pinned by a test. On the reviewer question: yes, keep the Section 501 I exclusion; both manuals and both plans state it, and reading tenant_pays_utilities for homes without heating is the right extension.

Six optional suggestions are in the report above. The two worth a look are G1 (a subsidized household with no energy cost gets $360 unless the caller sends an in-rent flag) and G3 (record the plan item 2.3 renter sentence as a source-conflict candidate). CI is green at fd132c51e5.

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 Nevada LIHEAP Fixed Annual Credit for cooling-only households

2 participants