Skip to content

Pay Oregon's LIHEAP standard payment to cooling-only households - #10736

Merged
hua7450 merged 8 commits into
mainfrom
or-liheap-cooling
Nov 12, 2026
Merged

hua7450 merged 8 commits into
mainfrom
or-liheap-cooling

Conversation

@hua7450

@hua7450 hua7450 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Pays Oregon's LIHEAP standard payment to households with no heating cost that pay an electric bill for cooling, including homes without heating.

Fixes #10735

What and why

The OHCS Energy Assistance Intake, Operations and Policy Manual describes LIHEAP as a program "to supplement home heating and cooling costs" (PY2026 p. 5). It makes eligible households "partially or fully responsible for their home heating or cooling expenses" (PY2026 p. 90). Each standard-payment matrix has HEATING columns and a COOLING Electricity column (PY2026 pp. 78-81, PY2027 pp. 72-75, PY2025 pp. 58-59). On main, or_liheap_eligible requires a heating expense or heat in rent, so a household whose only energy cost is cooling got $0.

Changes:

  • or_liheap_eligible adds a cooling route to the energy-cost test:
    • heating_cost = has_heating_expense | heat_in_rent
    • cooling_only = (heating_type != UNSPECIFIED) & ~heating_cost & has_cooling_expense & (pre_subsidy_electricity_expense > 0)
    • Any reported heating type qualifies when the household has no heating cost, for example a home heated with wood it gathers itself, because the benefit goes to the energy type it is applied to (PY2026 p. 67). This follows Oregon's text rather than the all-state cooling-only definition, which covers only heating_type NONE.
    • The subsidized heat-in-rent exclusion and the income test are unchanged.
  • or_liheap pays an eligible household with neither a heating cost nor heat in the rent from the Electricity column. Region, size, income band and the roomer rate are unchanged.
    • The published COOLING column equals the heating Electricity column in all 96 rows for PY2025, PY2026 and PY2027, so the existing parameter is reused. Its description now says "heating or cooling", with a comment recording that equality.
    • A future matrix with different cooling amounts would need its own parameter.
  • Income limit stops at the federal maximum. or_liheap_income_limit, which heating and cooling share, rounded 60% of the size-adjusted median up to the next dollar, as the manual prints it. That is $1 or $2 above the greater of HHS's 60% state median income amount and 150% of the poverty guideline, the most 42 U.S.C. 8624(b)(2)(B) allows. Each limit now stops there:
    • FY2026: $38,384 for one person (manual $38,385), $50,194 for two ($50,196), $62,005 for three ($62,006), and $1 lower at sizes 4 to 11. From size 12 the printed amount stays, because 150% of the guideline is the higher maximum.
    • PY2025: $1 to $3 lower at sizes 1 to 11, matching HHS's FY2025 amounts (LIHEAP IM 2024-02 Attachment 4). PY2027: $1 lower at sizes 7, 9 and 11 ($104,692 for seven); the other sizes are whole-dollar products and do not change.
    • Heating eligibility changes for households within those few dollars of the limit. The last payment band for two people ends at $50,195 in the matrix, so a household at $50,195 is now ineligible.
  • The labels and registry note now say the benefit covers heating or cooling, one payment a year, and that a cooling payment on top of a heating payment is not modeled. The eligibility reference cites page 67 and opens on page 11, its first cited page.

Test plan

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

  • YAML tests with expected values from the OHCS matrices. Four new cases:
    • Eligibility:
      • A home without heating that pays for cooling is eligible, and so is a subsidized household that pays its own electric bill. So is a home heated with gathered wood and no heating cost that pays for cooling on an electric bill (p. 67).
      • It is ineligible without a cooling cost, without an electric bill, with UNSPECIFIED fuel, or one dollar above the size-1 limit of $38,384.
    • Payment:
      • PY2026, band 3: $300 in Region 1 and $335 in Region 2 (pp. 78, 80); $0 without a cooling cost. The gathered-wood home in Region 2 gets the $335 COOLING amount, not the Wood/Pellets column.
      • PY2025: $270 in Region 2 (p. 59).
      • PY2027: $310 in Region 1 (p. 72).
  • Existing heating cases that asserted a printed limit above the federal maximum now assert the capped limit (or_liheap_income_limit.yaml, or_liheap.yaml, integration.yaml), and a new PY2027 case pins sizes 7, 9 and 11. One case changes results: at two people, $50,194 is eligible and paid $275, and $50,195 and $50,196 are not.
  • Source-conflict register: candidate us-or:liheap-income-limit-above-federal-ceiling (ruled) records the cap; the code follows the statute. New candidate us-or:liheap-cooling-payment-after-heating-payment (open) records the one-payment reading.
  • make format (ruff)
  • Changelog fragment in changelog.d/

Not modeled

  • One standard payment a year: a household with heating and cooling costs gets its heating fuel column, never a second cooling payment. The manual supports one payment: a separated household's adults may not "receive another standard payment during the current program year" (PY2026 p. 31).
    • The Oregon Health Authority's OHOP program, a LIHEAP subgrantee, says otherwise for its clients: "Clients who have already received a heating payment during the year may still be eligible for a cooling payment!" (2024, 2025 and 2026 cooling seasons; August 2026 capture). The plans also fund cooling as its own component.
    • The new candidate us-or:liheap-cooling-payment-after-heating-payment (open) records both readings. If OHCS confirms both are payable, Oregon needs a separate cooling amount and a second entry in the national LIHEAP list.
  • Cooling costs included in rent have no input.
  • Crisis cooling and equipment are out of scope.

axiom: TheAxiomFoundation/rulespec-us#2188 queued

🤖 Generated with Claude Code

hua7450 and others added 7 commits November 8, 2026 22:01
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A household with any reported heating type and no heating cost, including
a home without heating, qualifies on an electric bill when it pays for
cooling, and the payment uses the cooling column whenever an eligible
household has no heating cost. Cite manual page 67 and open the reference
on its first cited page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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>
@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 (cfe1c6b).
⚠️ Report is 159 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10736   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         3    +2     
  Lines           37        72   +35     
=========================================
+ Hits            37        72   +35     
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 Oregon's LIHEAP standard payment to cooling-only households

Head reviewed: a21697d7e9c2fbc92ce4fc6906eed3f29a0c250c (7 commits ahead of main, 0 behind).

Summary

The PR does three things: it opens or_liheap_eligible to a household with no heating cost that pays for cooling on an electric bill, it pays that household from the Electricity table (which equals the printed COOLING column), and it stops or_liheap_income_limit at the federal maximum.

The sources back the main change. Oregon runs cooling assistance as a plan component for the whole fiscal year, the manual defines an eligible household as one responsible for "home heating or cooling expenses", and every matrix prints a COOLING Electricity column. I compared all 288 cells (96 rows in each of PY2025, PY2026 and PY2027) of the parameter with the printed COOLING column and found no difference. Every quote and page in the PR body was found. All changed and new expectations are right by hand, every new term is pinned by a test, and CI is green at the head.

One item needs action before merge. The PR treats heating and cooling as one standard payment a year and says no source addresses a household that gets both. An Oregon Health Authority page for the 2024, 2025 and 2026 cooling seasons says a household that already received a heating payment may still get a cooling payment. The manual points the other way. That disagreement is not recorded, and the body's statement is incomplete.

Counts: 0 critical, 1 should, 8 suggestions.

Critical (must fix)

None.

Should address

1. The one-payment-a-year assumption is contradicted by a state agency source and is not recorded

Where: policyengine_us/variables/gov/states/or/ohcs/liheap/or_liheap.py:43-51 (a household with a heating cost never takes the cooling column), policyengine_us/variables/gov/states/or/ohcs/liheap/eligibility/or_liheap_eligible.py:46-52 (the cooling route requires no heating cost), policyengine_us/programs.yaml:1333, and the PR body under "Not modeled".

What the PR says. "One standard payment a year: a household with heating and cooling costs gets its heating fuel column, never a second cooling payment." It adds that an Oregon Health Authority letter "does not say whether a household paid for heating can also get a cooling payment." The queued Axiom issue repeats this: "None says whether a household paid in winter can be paid again in summer."

What the sources say.

  • For one payment: the manual says "If a previously served household separates, none of the adults may receive another standard payment during the current program year" (PY2026 manual, p. 31). A tribal member already assisted by the tribe "would only be eligible to receive a LIHEAP Crisis payment" (p. 32). The plan says "A household must have exhausted their regular heating/cooling benefit" before crisis aid (FFY2026 plan, p. 13).
  • For two payments: the Oregon Health Authority's OHOP program, the same program whose letter the body cites, publishes this for the cooling season: "Only electric bills can qualify for LIHEAP payments during cooling season." "Clients who have already received a heating payment during the year may still be eligible for a cooling payment!" "A complete application with verification of current income for each household member is needed to be evaluated, for each season." The text appears for the 2026 season (archived 2026-08-13), the 2025 season (archived 2025-08-10) and the 2024 season (archived 2024-07-19).
  • Consistent with separate payments: both plans list heating and cooling as separate components, each running 10/01 to 09/30, with a separate share of funds for cooling (10% in the FFY2027 plan, p. 4; FFY2026 plan, p. 4), and the matrix prints COOLING as its own column although it repeats the Electricity amounts.

Household. Period 2026, one person in Multnomah County, countable income $26,000, natural gas heat with a $960 gas bill, a cooling cost and a $1,500 electric bill.

  • Head: or_liheap = $435 (Region 1, size 1, row "$19,194 $28,789", Natural Gas, p. 78).
  • If the Health Authority's statement describes OHCS practice: $435 for heating plus $300 from the COOLING column, $735.

Why this is a should and not a critical. The manual is the controlling document and its separation rule supports one standard payment. The Health Authority page is a subgrantee's notice and says "may". The body lists the second payment as not modeled. What is missing is the record of the disagreement and an accurate statement of what the sources say.

Suggested fix.

  1. Add a candidate to source_conflicts/candidates.yaml, for example us-or:liheap-cooling-payment-after-heating-payment, with the manual (pp. 31, 61), the plan (items 1.1, 1.2 and the crisis explanation on p. 13) and the Health Authority page as sources. Set pe_us.follows to the manual reading and state: open until OHCS confirms.
  2. Correct the body sentence and interpretation 4 of rulespec-us#2188 to say that the Health Authority page allows a cooling payment after a heating payment.
  3. Add to the registry note that a cooling payment on top of a heating payment is not modeled.
  4. If OHCS confirms both are payable, Oregon needs a separate cooling amount: the same column, a cooling test without the no-heating-cost condition, and a second entry for Add a national LIHEAP total, an Illinois regular heating payment and LIHEAP cooling registry notes #10730's regular_payments list.

Suggestions

  1. Cooling included in rent. The body says "Cooling costs included in rent have no input." tenant_pays_utilities exists (household level, default true; false means utilities are in the rent) and merged Indiana and Georgia LIHEAP code reads it. The manual pays this household: direct payments go to "renters who's heating or cooling cost are included in rent" (p. 67), and "Energy Costs included in rent (non-subsidized)" is an energy burden paid "to Applicant(s) or Landlord" (p. 93). Example: 2026, one person in Multnomah County, unsubsidized, heating_type NONE, tenant_pays_utilities false, no electric bill, $26,000: the manual pays $300 directly, the head pays $0. The gap is narrow, because a tenant with all utilities in rent normally also has heat in rent and qualifies through heating. The accurate reason is that no input says the rent covers cooling (has_cooling_expense is defined as a cost separate from rent). Reword the body, or encode the case.

  2. Subsidized tenant with heat in rent who pays for cooling. The head keeps this household ineligible (or_liheap_eligible.py:63). The energy burden table marks subsidized heat-in-rent "No" and subsidized "pays a vendor directly for utility costs" "Yes" (p. 93), and the plan answers "No" to differing policies for subsidized renters under cooling (FFY2026 plan, item 3.3, p. 10). Eligibility Case 1 row 6 pins the result only without a cooling cost. Add a row with has_cooling_expense: true and say in the body that the heating reading is kept for cooling.

  3. A heated home whose fuel has no matrix column. A coal-heated home that pays a coal bill and an electric bill for cooling is eligible and gets $0 (no column). The same home with no heating cost gets $300 through the new route. The manual bases the benefit on "the energy type where the majority of assistance will be applied" when none goes to the primary heating source (p. 67), which would be electricity here. The same holds for heat in rent with an UNSPECIFIED fuel and a tenant-paid electric bill, which stays at $0 under the Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754 convention. Consider letting a household with a cooling cost and an electric bill fall back to the Electricity column when its fuel has no column.

  4. Tests worth adding. None is required by the mutation rule, but each documents a stated choice:

    • gas bill plus cooling cost and electric bill, expecting one payment of $435 (Region 1, size 1, $26,000);
    • heat in rent with natural gas plus a cooling cost, expecting the gas column (only Case 13 row 10, an UNSPECIFIED fuel, pins the heat-in-rent term in or_liheap.py:45);
    • a cooling-only roomer, expecting half of $300.
  5. HHS references for the cap. or_liheap_income_limit.py:14-16 cites only the FY2026 table at page 2. Sizes 7 to 12 are on page 5, and the cap also applies in 2025 and 2027. Add a # PDF pages 2, 5 comment and the FY2025 and FY2027 tables. The candidate at source_conflicts/candidates.yaml:9222 lacks the FY2027 table, which prints $104,692, $109,345 and $113,998 for sizes 7, 9 and 11.

  6. Comments left on the printed limit. These still call the printed amount the limit: or_liheap_eligible.yaml:25; integration.yaml:59, 89, 118; or_liheap.yaml:280, 314, 337, 368, 434. payment/income_band.yaml:1-2 and the note of candidate us-or:liheap-final-payment-band-bound say the last band extends through the eligibility limit; after the cap the limit is below the printed band top at sizes 1, 2, 3, 5 and 7 in FY2026.

  7. Title, issue and status lines. The title and issue Implement Oregon LIHEAP standard payment cooling column for cooling-only households #10735 describe only cooling, and the issue says "leave every existing result unchanged", while the PR lowers the income limit for heating households by $1 to $3. The body discloses this; the title and issue should too. "Tests were not run locally. CI is their first run" in the body and "They have not yet passed in CI at this head" in rulespec-us#2188 are now out of date.

  8. Redundant term. In or_liheap_eligible.py:48, ~heating_cost cannot change energy_cost = heating_cost | cooling_only, so no test can pin it. It is harmless; drop it or keep it for readability.

Verified correct

The ten questions.

  1. Regular benefit for a cooling-only household: yes, in 2025, 2026 and 2027. "The purpose of this program is to supplement home heating and cooling costs" (p. 5); "Eligible – Households whose verified income is in accordance with the LIHEAP/OEAP Benefits Matrix Form and are partially or fully responsible for their home heating or cooling expenses" (p. 90); plan item 1.1 lists cooling assistance for 10/01 to 09/30 in the FFY2025, FFY2026 and FFY2027 plans; plan item 3.1 sets 60% of state median income and item 3.6 sets $250 to $750, the same as heating. The same text is in the PY2025 manual (pp. 5, 67) and the PY2027 manual (pp. 5, 82). No conflict between plan and manual on this point.
  2. Amount: the formula reads the Electricity table for a cooling-only household and the fuel's own table for everyone else. COOLING equals HEATING Electricity in all 96 rows of each year, and the parameter equals the printed COOLING column in all 288 cells (PY2025 pp. 58-59, PY2026 pp. 78-81, PY2027 pp. 72-75; sizes 7 to 12 repeat size 6). Start dates 2024-10-01, 2025-10-09 and 2026-10-01 give periods 2025, 2026 and 2027 the PY2025, PY2026 and PY2027 columns. There is no fuel key lookup, so heating_type NONE cannot hit a missing key. There is no bill cap.
  3. Width of the condition: UNSPECIFIED stays at $0 (Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754 convention, Case 6 row 4). Any other reported fuel with no heating cost qualifies; the manual's rule on the energy type the benefit is applied to (p. 67) supports this, and the body states it. A subsidized household that pays its own electric bill qualifies (p. 93, row 4). See should 1 and suggestions 1 to 3 for the edges.
  4. No double payment in the model: or_liheap is one amount from one select. Main's eligible set is unchanged by the cooling route, because every household eligible on main has a heating cost or heat in rent and so fails the new cooling_only test in or_liheap.py. In every one of the 288 rows each heating fuel column is at least the Electricity amount, so a household with a heating cost is never paid less than the cooling column would give.
  5. Changed expectations: all are the income-limit cap and all are right (table below).
  6. Mutation rule: every new term is pinned.
    • heating_type != UNSPECIFIED: eligibility Case 6 row 4.
    • has_cooling_expense: Case 6 row 2; or_liheap.yaml Case 26 row 3.
    • electric bill above zero: Case 6 row 3.
    • energy_cost in the return: Case 6 rows 1, 5, 7.
    • | cooling_only in the select: Case 26 rows 1, 2, 4 and Cases 27, 28.
    • has_heating_expense inside it: Cases 2 to 5 and 7 to 10. heat_expense_included_in_rent inside it: Case 13 row 10.
    • cap min_: limit Cases 1, 2, 4, 5. Poverty guideline arm: Case 4 size 12. Guideline year lag: Case 1 size 11 (the 2026 guidelines would give $108,510).
  7. Non-residents: no new state-gated key. Region, size and band keep their floors; the cap reads the median by the household's own state. or_liheap.yaml Case 18 (Oregon and Washington in one simulation) passes in CI.
  8. Periods: every variable read is annual. Oregon has no options=[ADD], and Add a national LIHEAP total, an Illinois regular heating payment and LIHEAP cooling registry notes #10730 does not touch the Oregon formulas.
  9. Registry: the note matches the code. verified_years: "2025-2027" is unchanged and backed by the three manuals and three plans.
  10. regular_payments: Add a national LIHEAP total, an Illinois regular heating payment and LIHEAP cooling registry notes #10730 already lists or_liheap; this PR adds no variable, so nothing to add.

Income-limit cap. The cap's 60% arithmetic (floor the four-person amount, multiply by the size factor, floor again) reproduces HHS's published Oregon amounts at every size from 1 to 12 in FY2025, FY2026 and FY2027 (Attachment 4, pages 2 and 5 of each table), 36 cells. The limit is the printed amount stopped at the greater of that figure and 150% of the prior year's guideline, which leaves size 12 at the printed amount in FY2025 and FY2026. This follows the ruling already applied to West Virginia, Maine, North Dakota and Ohio on main. The unrounded product at size 2 in FY2026 is $50,195.02, so the $50,195 household is excluded only by HHS's rounding; the candidate records that choice.

Hand checks.

Case Inputs Arithmetic Expected OK
or_liheap.yaml 26 row 1 2026, Multnomah, size 1, $26,000, NONE, cooling row "$19,194 $28,789", COOLING, Region 1 (p. 78) $300 yes
26 row 2 Deschutes same row, Region 2 (p. 80) $335 yes
26 row 3 no cooling cost ineligible $0 yes
26 row 4 WOOD, no heating cost, Deschutes COOLING $335, not Wood/Pellets $465 $335 yes
27 2025, Deschutes, $26,000 row "$18,407 $27,609", COOLING (p. 59) $270 yes
28 2027, Multnomah, $26,000 row "$20,164 $30,245", COOLING (p. 72) $310 yes
eligibility 6 seven households T, no cooling F, no bill F, UNSPECIFIED F, subsidized own bill T, $38,385 > $38,384 F, wood T as listed yes
limit 1 (2026) sizes 1, 3, 5, 7, 9, 11 73,816 x 0.52, 0.84, 1.16, 1.35, 1.41, 1.47, floored 38,384; 62,005; 85,626; 99,651; 104,080; 108,509 yes
limit 2 (2025) same sizes 70,789 x the same factors, floored 36,810; 59,462; 82,115; 95,565; 99,812; 104,059 yes
limit 4 (2026) sizes 2 to 12, even 73,816 x 0.68, 1, 1.32, 1.38, 1.44; size 12: 150% x 76,150 = 114,225 > 110,725 50,194; 73,816; 97,437; 101,866; 106,295; 110,725 yes
limit 5 (2027) sizes 7, 9, 11 77,550 x 1.35, 1.41, 1.47 = 104,692.5; 109,345.5; 113,998.5, floored 104,692; 109,345; 113,998 yes
limit 3 (2027) sizes 2, 3, 6, 8, 10, 12 whole-dollar products equal HHS unchanged yes
or_liheap.yaml 11 size 2, electric, $50,194 / $50,195 / $50,196 limit $50,194; last row Electricity $275 275, 0, 0 yes
19, 20 $40,000, size 1 above $38,384 limit 38,384, $0 yes
23 2025, size 1 70,789 x 0.52 = 36,810.28 limit 36,810 yes
integration 1, 2 sizes 2 and 1 as above 50,194; 38,384 yes

The 48 deleted lines. All accounted for: 2 in electricity.yaml (description, label); 1 in programs.yaml (note); 13 in or_liheap_income_limit.yaml (three case names, comments and three expectation lines, Cases 1, 2, 4, and one comment line in Case 3); 4 in integration.yaml (two comments, two limits); 18 in or_liheap.yaml (Case 11: name, inputs, eligibility and amounts; one header comment; Cases 19, 20 and 23: comment and limit each); 5 in or_liheap_eligible.py (label, page comment, reference, documentation line, return); 1 in or_liheap_income_limit.py (return); 4 in or_liheap.py (label, two documentation lines, one select line). No expectation changed except through the cap.

Citations. Found as quoted: PY2026 pp. 5, 67, 77, 90, 93 and matrices pp. 78-81; PY2025 pp. 58-59; PY2027 pp. 72-75; HHS tables. The eligibility reference now opens on page 11, the first page in its comment, as the repository's reference rule asks.

Not checked. The check boxes on page 4 of the FFY2026 plan were read from the text layer (the dates beside cooling assistance are printed); page 10 of that plan and pages 4 and 11 of the FFY2027 plan were viewed as images. The Health Authority's heating-season letter could not be retrieved. No model run was made locally; CI is the test evidence.

Other. Changelog fragment changelog.d/or-liheap-cooling.fixed.md is present and covers both changes. Parameter description follows the house template. The PR does not touch income counting, so the income-list guards (#9558, #10317, #10408) do not apply.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2188 queued is valid. The issue is open, labelled pe-parity, written against head a21697d7e9, and covers all three changes with module paths, corpus citations, verbatim text, required outputs and companion tests drawn from the manuals and HHS tables. Two lines need a refresh: interpretation 4 (should 1) and "They have not yet passed in CI at this head".

CI status

39 checks pass, 3 skipped placeholders, none failing or pending, on the run at the current head. The Oregon files ran: or_liheap_eligible.yaml 6 passed, or_liheap_income_limit.yaml 5, integration.yaml 3, or_liheap.yaml 28.

Branch status

0 behind main; the merge with main is clean. Pairwise merges with #10730, #10732, #10734, #10738, #10740, #10742 and #10762 are also clean. All eight edit policyengine_us/programs.yaml, in different entries.

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 one change, to the record and not to the formula. The PR treats heating and cooling as one standard payment a year and says no source addresses a household that gets both. The Oregon Health Authority's cooling-season notice for 2024, 2025 and 2026 says "Clients who have already received a heating payment during the year may still be eligible for a cooling payment", while the manual points the other way. Please add a source-conflict candidate and correct the body sentence and the queued Axiom issue (Should Address 1 in the report above).

Everything else holds: all 288 cells match the printed COOLING column across PY2025-PY2027, the income-limit cap reproduces HHS's Oregon amounts at every size, every changed expectation is the cap and is right by hand, and every new term is pinned by a test. The eight suggestions are optional. CI is green at a21697d7e9.

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

hua7450 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, David. Recorded, with no formula change, in cfe1c6be84:

@hua7450
hua7450 requested a review from DTrim99 November 12, 2026 17:10
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Pay Oregon's LIHEAP standard payment to cooling-only households

Head reviewed: cfe1c6be84332eeb56eac42f8a1bac479e7a9b01 (8 commits ahead of main, 3 behind). Round 1 was at a21697d7e9.

Summary

Round 1 had no critical finding, one should-address and eight suggestions. The should-address asked for a record of a disagreement between sources: the manual points to one standard payment a year, while an Oregon Health Authority page says a household already paid for heating may still get a cooling payment.

The author answered with one commit that changes two files, source_conflicts/candidates.yaml and policyengine_us/programs.yaml. No variable, parameter, test or changelog file changed, so every result and every expectation is the same as at round 1.

The record is complete and accurate. I opened each cited source again and found every quoted sentence on the cited page. The candidate has every required field, the PR body, the registry note and the queued Axiom issue now say the same thing, and the candidate file passes the repository's own checks on the tree merged with main. The should-address is closed.

The eight suggestions were optional and were not taken. I raise two new suggestions about wording; neither blocks the merge.

Counts open after this round: 0 critical, 0 should, 2 suggestions.

Prior findings

Finding Status Evidence
Should 1. The one-payment-a-year assumption is contradicted by a state agency source and is not recorded Fixed (answered by a record, with no formula change, as round 1 asked) New candidate us-or:liheap-cooling-payment-after-heating-payment at source_conflicts/candidates.yaml:9163-9202; registry note at policyengine_us/programs.yaml:1333; PR body "Not modeled"; interpretation 4 of rulespec-us#2188. Each part is checked below under "Verified". All four parts of the round 1 fix are present: the candidate, the corrected sentences, the registry note, and the statement of what Oregon would need if OHCS confirms both payments.
Suggestion 1. Cooling included in rent: reword "have no input" or encode the case Not taken (optional) The body sentence is unchanged.
Suggestion 2. Subsidized tenant with heat in rent who pays for cooling: add a test row and a body line Not taken (optional) No test file changed.
Suggestion 3. Heated home whose fuel has no matrix column Not taken (optional) No formula changed.
Suggestion 4. Three documenting tests Not taken (optional) No test file changed. For later: when this candidate moves to the shared register, each reading is pinned to a named YAML case. The candidate's own example household (gas heat, a cooling cost and an electric bill, $435) is not a case yet; the first test of this suggestion would be that case.
Suggestion 5. HHS references for the cap Not taken (optional) or_liheap_income_limit.py and the income-limit candidate are unchanged.
Suggestion 6. Comments left on the printed limit Not taken (optional) No test or parameter file changed.
Suggestion 7. Title, issue and status lines Not taken (optional) The title and issue #10735 are unchanged. "Tests were not run locally. CI is their first run" is still in the body.
Suggestion 8. Redundant ~heating_cost term Not taken (optional) or_liheap_eligible.py is unchanged.

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

  1. One clause of the candidate states an inference as the manual's rule. source_conflicts/candidates.yaml:9185, readings.statute: "the manual pays one standard payment a program year". No sentence in the manual says this of a household that stays together. Page 61 defines "Regular/Standard Payment – The standard energy assistance payment determined by the LIHEAP/OEAP Benefits Matrix provided by OHCS each program year", where "each program year" describes the matrix. The quoted half of the reading (page 31) is verbatim. Because the open question is exactly whether the manual means one payment, the clause could read: "the manual defines one Regular/Standard payment set by the matrix (page 61), and a separated household's adults may not ...". Optional.

  2. The Axiom issue's file list does not name the new candidate. In rulespec-us#2188, under "Variables", the line "The PR also edits programs.yaml, adds a changelog fragment and adds the source_conflicts/candidates.yaml entry us-or:liheap-income-limit-above-federal-ceiling" lists one candidate; the PR now adds two. Interpretation 4 does name the new one, so nothing is missing for dispatch. Optional.

Verified

No formula or expectation change (read from the commit). The fix commit cfe1c6be84 changes policyengine_us/programs.yaml (1 line) and source_conflicts/candidates.yaml (40 lines added). The diff between the two reviewed heads over policyengine_us/variables, policyengine_us/parameters, policyengine_us/tests and changelog.d is empty. I read or_liheap.py and or_liheap_eligible.py whole at the new head; both are as reviewed in round 1. There is no new branch, term or expectation to hand-check or to pin.

The candidate, field by field (read against CONTRIBUTING.md, "Source conflicts", and the guard's rules).

Field Value Check
id us-or:liheap-cooling-payment-after-heating-payment Well formed; unique among 431 candidates on the merged tree; not in the pinned register.
program liheap Right.
years 2025 to 2027, type: program, with the required note Matches the years the PR claims. The separation sentence is in all three manuals: PY2025 (PDF page 25), PY2026 (page 31) and PY2027 (page 32). The Health Authority text appears for the 2024, 2025 and 2026 cooling seasons.
question Can a household paid a standard payment for heating also receive one for cooling in the same program year? States the disagreement plainly.
sources manual, plan, guidance Three sources (at least two are required); all kinds are allowed values; all links are https and open. Each is checked below.
readings statute and other Both quotations are verbatim (below). See suggestion 1 for the unquoted clause.
example $435 under one payment; $435 + $300 = $735 if both are payable Right by hand: 2026, Region 1 (Multnomah), size 1, $26,000 falls in the row "$19,194 $28,789"; Natural Gas $435, COOLING $300 (PY2026 manual, page 78).
pe_us.follows gap-fill The right value: no statute settles it and the code follows the manual. Other LIHEAP candidates that follow a manual use the same value.
pe_us.code or_liheap.py, eligibility/or_liheap_eligible.py Both files exist at the head and hold the deciding lines (or_liheap.py:43-51, or_liheap_eligible.py:46-52).
pe_us.prs this PR's URL Well formed.
state open As round 1 asked, pending confirmation from OHCS.
note Follows the manual; the Health Authority page is a subgrantee notice and says "may"; what Oregon needs if both are payable Matches the code and the page.

Each cited page, opened again.

  • PY2026 manual, page 31: "Household Separation: If a previously served household separates, none of the adults may receive another standard payment during the current program year. Each new household may still be eligible for a crisis payment." The candidate's quotation is verbatim. The printed page number equals the PDF page.
  • PY2026 manual, page 61: the payment types list and the definition "Regular/Standard Payment – The standard energy assistance payment determined by the LIHEAP/OEAP Benefits Matrix provided by OHCS each program year." The citation's label "standard payment" fits.
  • FFY2026 plan, page 4, viewed as an image: item 1.1 has Heating assistance and Cooling assistance both checked, each with 10/01/2025 to 09/30/2026; item 1.2 gives heating 54% and cooling 10%. This backs "the plans fund heating and cooling as separate components". The FFY2027 plan's page 4 lists the same two components for 10/01/2026 to 09/30/2027.
  • FFY2026 plan, page 13: "A household must have exhausted their regular heating/cooling benefit." Found as cited.
  • Oregon Health Authority OHOP page, capture of 2026-08-13: under "Cooling Season Specific Announcements": "Only electric bills can qualify for LIHEAP payments during cooling season. Clients who have already received a heating payment during the year may still be eligible for a cooling payment! A complete application with verification of current income for each household member is needed to be evaluated, for each season." The candidate, the body and the Axiom issue quote the middle sentence verbatim. The same sentence is in the captures of 2025-08-10 and 2024-07-19, which backs "2024, 2025 and 2026 seasons".

The three texts agree. The PR body's "Not modeled" bullet, interpretation 4 of rulespec-us#2188 and the candidate each say: one standard payment a year is modeled; the manual's separation rule supports it; the Health Authority page says otherwise and is quoted; the plans fund cooling as its own component; the candidate is open; a separate cooling amount is needed if OHCS confirms both. The body's test plan names the new candidate.

Registry note. programs.yaml:1333 now reads "regular heating or cooling standard payment, one a year, ... A cooling payment on top of a heating payment, crisis, Fuel Payment, equipment and OEAP assistance are not modeled." This matches the code. verified_years is unchanged.

Candidate file on the tree merged with main (a run I executed). The merged candidates.yaml and programs.yaml parse. I ran the source-conflict guard file on the merged tree: 102 of 103 checks passed, including unique and well-formed candidate ids, no candidate already registered, the field rules, the public-text scan, the register pin and the comment-count ratchet. The one failure is a self-test of the comment scanner that feeds literal strings to the tokenizer and reads no repository file; it fails under the local Python 3.11 and is unrelated to this PR. Outside a git checkout the guard checks that code paths exist rather than that they are tracked, so I confirmed separately that both paths are in the head tree.

Ratchet (reasoned from the diff). The guard counts comments only under policyengine_us/variables, parameters and reforms. The fix commit touches none of them and adds no marker, so no count and no KNOWN_UNMARKED line moves. This is the right step for a new candidate: the marker comes only after the entry is in the shared register.

No follow-up issue was promised, so there is none to check. The open candidate is the record.

Axiom

axiom: TheAxiomFoundation/rulespec-us#2188 queued is valid. The issue is open, labelled pe-parity, was updated at 17:10 UTC on 2026-11-12 and names head cfe1c6be84 in its header and scope. Interpretation 4 now quotes the Health Authority page, says it allows a cooling payment after a heating payment, and names the open candidate. Companion test 12 (gas heat with a cooling cost, $435) matches the candidate's example. The issue matches the fix. See new suggestion 2 for one line; the status lines "The PR has no review yet" and "They have not yet passed in CI at this head" were noted in round 1 and are optional.

CI

All 39 checks pass on the run at the new head (cfe1c6be84), which started at 17:09 UTC on 2026-11-12 and finished at about 18:26 UTC. None is failing or pending.

Read from the job logs at this head:

  • The PR's own test files ran twice, in "Quick Feedback" and in "Baseline (states-shard-3)": or_liheap_eligible.yaml 6 passed, or_liheap_income_limit.yaml 5, integration.yaml 3, or_liheap.yaml 28.
  • The source-conflict guard ran in "Rest (Python shard 2)" with no failure, including the four candidate checks (unique ids, not registered, fields, public text), the pin check and the comment-count ratchet. The scanner self-test that failed in my local run passes there.
  • All ten "Rest" Python shards, all eleven baseline shards, the nine contrib shards, the microsimulation job, "PR validation", "Package and compatibility" and "Household API Partners" pass.

The author's note that tests were not run locally is covered by these runs.

Branch

3 behind main (#10738 Nevada cooling, a version bump and #10813). The merge with main is clean, and main's new guard on the partner contract rule does not touch this PR's files. Pairwise merges with the current heads of #10730, #10732, #10734, #10740, #10742 and #10762 are clean; #10738 is now merged and is part of main. All of them edit policyengine_us/programs.yaml, in different entries.

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. The fix commit records the disagreement round 1 asked about and changes no formula, test or parameter: the new candidate us-or:liheap-cooling-payment-after-heating-payment is open, follows the manual, and cites the manual, the plan and the Oregon Health Authority page, with every quote found on its cited page. The description, the registry note and rulespec-us#2188 now say the same thing.

Two optional wording suggestions are in the report above. CI is green at cfe1c6be84.

@hua7450
hua7450 merged commit 8c19836 into main Nov 12, 2026
39 checks passed
@hua7450
hua7450 deleted the or-liheap-cooling branch November 12, 2026 18:44
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 Oregon LIHEAP standard payment cooling column for cooling-only households

2 participants