Skip to content

Add Kentucky LIHEAP regular cooling assistance - #10752

Open
hua7450 wants to merge 18 commits into
mainfrom
ky-liheap-cooling
Open

hua7450 wants to merge 18 commits into
mainfrom
ky-liheap-cooling

Conversation

@hua7450

@hua7450 hua7450 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Kentucky's Summer Cooling Subsidy, a one-time electric benefit of $41 to $250 that equals the heating points with the fuel set to electric.

Fixes #10751

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

What and why

The FY2026 plan says "Cooling assistance provides electric benefits only" (item 3.4), with a 150% FPG limit (3.1) and a $41 to $250 range (3.6). In the FY2026 heat-cool workbook the cooling Electric column equals the heating Electricity column in all 64 rows. The FFY 2026 Summer Cooling Subsidy fact sheet prints monthly limits of $1,995 for one person, plus $710 for each added person: 150% of the 2026 guidelines, while heating uses 2025. 921 KAR 4:116 Section 4(3)(a) pays the electric provider or the landlord.

  • The points calculation, the income-limit check and the documentation check move from heating code into shared functions, so cooling reuses them without copying. Heating results are unchanged.
  • New cooling/fpg_year_lag.yaml (0 from FY2026): the cooling income limit uses the current year's guidelines, as the fact sheet prints. The income-point bands use heating's guideline (ky_liheap_fpg, lag 1), which reproduces every income range the FY2026 workbook prints beside the cooling column (for one person, 0-979, 979.01-1,305, 1,305.01-1,631 and 1,631.01-1,957).
  • ky_liheap_cooling_eligible: monthly countable income at or below the cooling limit; has_cooling_expense with a positive electricity bill, or electricity paid through the rent (fact sheet: "pay utility costs as an undesignated portion of rent"; 921 KAR 4:116 Section 4(3)(a)2 pays the landlord); and heating's citizenship and documentation rules.
  • ky_liheap_cooling: the heating points amount with electric fuel points, ceil(points × 16.66), and the subsidized-housing share. No bill cap.
  • The cooling subsidy has no heating-fuel condition: every heating_type, including NONE and UNSPECIFIED, can be paid, since the amount always uses the electric points.
  • has_cooling_expense is required with a positive electricity bill, except for renters whose utilities are included in rent. This is a PolicyEngine convention for cooling costs, not a Kentucky condition: the fact sheet asks for the current electric bill and sets no air-conditioning test. Renters whose utilities are in the rent are identified by the existing tenant_pays_utilities input (false) with positive rent; no input is added.

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. 12 cases in 2 files: workbook cells, the plan minimum and maximum, the fact-sheet limits, the band edge at 75% of the 2025 guideline, subsidized and unsubsidized renters paying electricity through the rent, a renter paying utilities apart from the rent and a rent-free household (neither eligible without an electric bill), no cooling cost, an UNSPECIFIED fuel (paid), and the national liheap total.
  • Source-conflict register: the cooling subsidized amounts reuse the heating subsidized income points, which the open candidate us-ky:liheap-subsidized-income-points tracks. This PR extends that candidate to cooling: it adds ky_liheap_cooling.py, plan item 3.4, 921 KAR 4:116 Section 4(3)(c)2 and the workbook's cooling cells K52-K55 to the entry.
  • make format (ruff)
  • Changelog fragment in changelog.d/

Not modeled

  • One cooling payment per year. FY2026 ran a spring round (April 14 to May 22 in the plan; Community Action Kentucky announced April 20) and a summer round (August 3 to September 11), and the regulation allows one benefit per round. The model pays one because plan item 1.1 offers cooling and summer components only "if additional money is received", and both FY2026 rounds were first come, first served, so a second round depends on funding.
  • No summer 2026 benefit table was published, so the bands follow the FY2026 workbook. Incomes above the workbook's last printed range but within the cooling limit (above $1,957 and up to $1,995 for one person) get the top band.
  • Only FY2026 is verified; the FY2027 plan has no benefit table.

axiom: TheAxiomFoundation/rulespec-us#2187 queued

🤖 Generated with Claude Code

hua7450 and others added 12 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>
921 KAR 4:116 Section 4(3)(a)2 pays the landlord for these renters. The
income-point bands now use heating's guideline year, which reproduces
the FY2026 workbook; the income limit keeps the 2026 guideline. Updates
shared parameter descriptions, test comments and margins, 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>
…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 Kentucky LIHEAP regular cooling assistance

Summary

The PR adds Kentucky's one-time electric cooling subsidy (ky_liheap_cooling, ky_liheap_cooling_eligible), a cooling guideline-year parameter, a second Kentucky registry entry and a new line in gov.hhs.liheap.regular_payments. It also moves the heating points calculation, income-limit check and documentation check into shared functions. I checked it against 921 KAR 4:116 (current text, effective 11-23-2021), the FY2026 and FY2027 plans, the FY2026 heat-cool workbook, the FFY 2026 Summer Cooling Subsidy fact sheet and Community Action Kentucky's spring and summer 2026 announcements.

The amounts are right. The cooling Electric column (K) equals the heating Electricity column (E) in all 64 workbook rows, and every cell I checked follows min(size, 5) + income points + 6, at 16.66 a point rounded up, with 30% rounded up in subsidized housing. The fact sheet limits are exactly 150% of the 2026 guidelines. Listing heating and cooling together in the national total does not double count: Kentucky pays both in one program year (quoted below). The refactor leaves heating unchanged line by line, and no existing expectation changed. CI is 36/36 green at the head, and both new test files ran (6 and 4 cases passed).

One should-fix: the new rent route has two terms, and no test pins either one on its own. The suggestions are disclosure and labelling points: the second FY2026 round, the cooling-cost input, UNSPECIFIED fuel for utilities-in-rent renters, the existing subsidized-points candidate, the band extension above $1,957, reference anchors, and a stale line in the axiom issue.

Critical (must fix)

None.

Should address

  1. The rent route's two terms are not pinned on their own (mutation rule). policyengine_us/variables/gov/states/ky/dcbs/liheap/cooling/ky_liheap_cooling_eligible.py lines 36-38:

    utilities_in_rent = ~spm_unit.household("tenant_pays_utilities", period) & (
        add(spm_unit, period, ["rent"]) > 0
    )

    The governing text is the FFY 2026 Summer Cooling Subsidy fact sheet (one page), eligibility item 1: "Must be responsible for home utility costs or pay utility costs as an undesignated portion of rent". 921 KAR 4:116 Section 4(3)(a)2 adds that the payment goes to the "Landlord, if the cost of cooling is included as an undesignated portion of the rent". Only one test reaches this route: ky_liheap_cooling_eligible.yaml Case 4 (line 116), where both households have tenant_pays_utilities false and positive rent. Every other case has rent 0 and the default tenant_pays_utilities true. Reading the tests, neither of these mutations would fail any case:

    • (a) Drop ~tenant_pays_utilities. Any renter with positive rent then qualifies without an electric bill. Example: one adult in Kentucky, wages $7,540, rent $12,564, tenant_pays_utilities true (default), pre_subsidy_electricity_expense 0, has_cooling_expense false. Expected: not eligible, $0, because the household pays neither an electric bill nor utilities through the rent. Under the mutation: eligible, $184. No case catches it.
    • (b) Drop rent > 0. A household that does not pay rent then qualifies whenever tenant_pays_utilities is false. Example: the same adult with tenant_pays_utilities false, rent 0, no electric bill, has_cooling_expense false (living rent-free with utilities paid by someone else). Expected: not eligible, $0. Under the mutation: eligible, $184. No case catches it.

    Fix: add these two households to ky_liheap_cooling_eligible.yaml, each expecting ky_liheap_cooling_eligible: false and ky_liheap_cooling: 0.

Suggestions

  1. One cooling payment per year, although FY2026 ran two rounds. Section 4(3)(a) allows "A one (1) time subsidy payment during each component period". Community Action Kentucky ran a spring round (April 20 to May 22, 2026; plan item 1.1, page 4 prints 04/14/2026 to 05/22/2026) and a summer round (August 3 to September 11, 2026, fact sheet). Both announcements say "Assistance received during previous LIHEAP components does not affect household eligibility" (summer announcement; the spring announcement carries the same sentence, reprinted here). So a one-person household with $628 a month that applied in both rounds received $184 twice; the model pays $184. The body and registry note disclose this, but they give no reason. Add one sentence giving it (for example: plan 1.1 makes cooling contingent, "We will offer cooling components and/or summer components if additional money is received", and each round was first-come, first-served), or add a rounds-per-year parameter.

  2. has_cooling_expense on the direct route is a convention. Lines 29-35 require has_cooling_expense with a positive electric bill. The fact sheet asks for "Most current electric (utility) bill" and responsibility "for home utility costs". It has no air-conditioning or cooling-equipment test. Section 3(2)(b) does name "Cooling costs". An adult with a $130 monthly electric bill and no cooling cost gets $0 here (Case 2, line 43); Kentucky's printed rule would pay $184. The rent route does not require the input (Case 4), so the two routes differ. The body labels this as the all-state cooling convention, but the code comment presents it as the rule ("must be responsible for its home electric costs"). Say in the comment that has_cooling_expense is a modeling convention, not a Kentucky condition.

  3. The UNSPECIFIED exclusion catches the PR's own utilities-in-rent renters. Lines 40-43 return false for heating_type UNSPECIFIED. This matches Kentucky heating: ky_liheap.py line 79 pays $0 whenever the fuel carries zero points, and UNSPECIFIED has 0 in payment/fuel_points.yaml. So the body's statement is accurate for Kentucky. But the cooling amount does not depend on the heating fuel. The heating_type documentation says UNSPECIFIED "is also the right value when heat is included in rent and the fuel is unknown". Example: a renter whose utilities are in the rent and who does not know the building's fuel (UNSPECIFIED, tenant_pays_utilities false, rent $12,564, wages $7,540). Kentucky pays the landlord $184 (Section 4(3)(a)2); the model pays $0. Consider exempting the rent route from the fuel check, or say in the comment that this case is excluded.

  4. Extend the subsidized-points candidate to cooling. source_conflicts/candidates.yaml (main, line 4390) us-ky:liheap-subsidized-income-points lists only income_points/subsidized.yaml and ky_liheap.py, and cites only Section 4(1)(c). Its paths are not stale: the subsidized lookup still sits in ky_liheap.py, inside ky_liheap_points_amount. But cooling now inherits the reversed ordering, and the workbook's subsidized cooling rows rise with income (41, 45, 51, 56 for one person, cells K52-K55). Two cooling sources bear on the question:

    • Plan item 3.4, page 10: "The highest benefits are awarded to the lowest income household size".
    • Section 4(3)(c)2: subsidized ceilings rise with income ("up to one hundred (100) dollars" for 0-100%, "up to $200" for 101-150%), while nonsubsidized ceilings fall ($400, then $300).

    Under the regulation's ordering, one subsidized person in the lowest band would get ceil(184 × 0.3) = $56, not $41. Add cooling/ky_liheap_cooling.py to pe_us.code and those two sources to the entry. In the body, "adds no new conflict" would be more accurate as "extends the existing conflict to cooling".

  5. Income between the workbook's top band end and the cooling limit. For one person, the workbook's last row ends at 1,957 (cell D8, 150% of the 2025 guideline). The cooling limit is 1,995. Between them the model gives the top-band amount by extension: Case 5 (line 159), $1,976 → 8 points → $134. The body says no summer table was published. Add one line to the formula comment (ky_liheap_cooling.py lines 30-35) saying that incomes above the printed range get the top band.

  6. Reference anchors and a shared comment.

    • ky_liheap_cooling.py line 21 says "PDF pages 4,10-11: Sections 1.1, 3.4-3.6", but only #page=4 is linked. Add separate #page=10 (3.4) and #page=11 (3.6) entries.
    • The comment in the now-shared ky_liheap_meets_income_limit (ky_liheap_income_eligible.py lines 27-32) explains only heating's $688. A clause noting that the cooling fact sheet's $710 is exact (5,680 × 1.5 / 12 = 710.00) would help.
  7. Axiom issue wording. rulespec-us#2187 still says CI "has not yet run its tests at this head" and the tests "have not yet passed in CI at this head". Both new files have now passed at 799669b (6 and 4 cases). Refresh that line.

Verified correct

  • Heating and cooling in the same program year (no double count).
    • Section 1(15) defines the subsidy component as "the heating or cooling component", and Section 4(3)(a) gives cooling its own "one (1) time subsidy payment during each component period".
    • Community Action Kentucky's FY2026 summer and spring announcements say "Assistance received during previous LIHEAP components does not affect household eligibility". The 2025 summer announcement says "Prior LIHEAP assistance does not affect eligibility".
    • So ky_liheap and ky_liheap_cooling both belong in regular_payments.
  • A regular cooling benefit exists in FY2026.
    • Plan item 1.1 (page 4): cooling 04/14/2026-05/22/2026, contingent "if additional money is received". Item 1.2: cooling 7.00% (the PDF text layer misaligns this table by one row).
    • The summer fact sheet is titled "Summer Cooling Subsidy Component: FFY 2026" and runs August 3 to September 11, 2026.
    • The FY2027 plan repeats cooling (04/13/2027-09/10/2027), 150%, "electric benefits only" and $41-$250 (pages 4, 10 and 11). No FY2027 matrix is on the Clearinghouse, so verified_years: "2026" is accurate.
  • Fact-sheet limits. 15,960 × 1.5 / 12 = 1,995.00 and 5,680 × 1.5 / 12 = 710.00 exactly. Sizes 2-8: 21,640 → 2,705; 27,320 → 3,415; 33,000 → 4,125; 38,680 → 4,835; 44,360 → 5,545; 50,040 → 6,255; 55,720 → 6,965. All match the fact sheet, so the code's rounding up changes nothing. The federal 110% floor and the 150%/60%-SMI ceiling hold.
  • Guideline-year split.
    • The lag 0 (cooling) / lag 1 (heating) split rests on the two fact sheets' amounts only. Plan items 2.1 and 3.1 print "HHS Poverty Guidelines 150.00%" with no year, and Section 3(1) says "updated annually"; the parameter comments say the lag is inferred.
    • Calendar 2026 reads FFY2026: limit at the 2026 guidelines, bands at the 2025 guidelines.
    • Calendar 2025 returns backfilled values: limit at the 2025 guidelines, bands at the 2024 guidelines (942/1,255/1,569 for one person), same points.
    • Calendar 2027 uses the projected 2027 guideline for the limit and the 2026 guidelines for the bands (998/1,330/1,663 for one person).
    • Backdating to the first modeled year means the 2025-10-01 start date raises no missing-parameter error before FY2026.
  • Points table against workbook column K ("rev benefit income range"):
    • Size 1, unsubsidized, each band: 1+4+6 = 11 → ceil(183.26) = 184 (K5); 1+3+6 = 10 → 167 (K6); 1+2+6 = 9 → ceil(149.94) = 150 (K7); 1+1+6 = 8 → ceil(133.28) = 134 (K8).
    • Other sizes, unsubsidized: size 2 lowest band 12 → ceil(199.92) = 200 (K10); size 3 third band 11 → 184 (K17); size 4 top band 11 → 184 (K23); size 5 lowest band 15 → ceil(249.9) = 250 (K25); size 6 top band 5+1+6 = 12 → 200 (K33); size 8 second band 5+3+6 = 14 → ceil(233.24) = 234 (K41).
    • Subsidized: size 1 lowest 1+1+6 = 8 → 134 → ceil(40.2) = 41 (K52); size 1 top 1+4+6 = 11 → 184 → ceil(55.2) = 56 (K55); size 4 second 4+2+6 = 12 → 200 → 60 (K68); size 5 top 15 → 250 → 75 (K75); size 7 third 5+3+6 = 14 → 234 → ceil(70.2) = 71 (K84).
    • The cooling formula uses 16.66 (L5; N5 "1 pt= 16.66 (roundup)"). The "if no funding FFY26" sheet (33.33, up to $367) is correctly not used, since plan item 3.6 prints $41-$250.
  • Band ends.
    • One person: ceil(1,304.17 × 0.75) = 979, ceil(1,304.17) = 1,305, ceil(1,630.21) = 1,631 (D5-D7).
    • Two people: 1,322 / 1,763 / 2,204 (D10-D12).
    • Four people: 2,010 / 2,680 / 3,349 (D20-D22).
    • Eight people: 3,385 / 4,513 / 5,641 (D40-D42).
  • Minimum and maximum. Neither is clipped, and both are structural. The fewest possible points are 1 + 1 + 6 = 8 → $134, or $41 subsidized (the plan minimum, Case 2). The most are 5 + 4 + 6 = 15 → $250 (the plan maximum, Case 3). Both are reachable and neither can be exceeded. All amounts are within the Section 4(3)(c)2 ceilings.
  • Subsidized share. The workbook's subsidized cooling cells are ROUNDUP(K(reversed row) × 0.3, 0), the same as heating's E52 = ROUNDUP(E8×0.3,0). Section 4(1)(e) applies to "a subsidy component", which Section 1(15) defines to include cooling.
  • Refactor, line by line against main (cc48633).
    • ky_liheap_points_amount reproduces main's income, band, subsidized, points, rounding and subsidized-share lines. Heating passes fuel_points + coal_adjustment and keeps where(fuel_points > 0, amount, 0). Sums of integer-valued floats are exact, so regrouping changes nothing.
    • ky_liheap_members_documented returns main's has_qualified_member & all_documented. ky_liheap_meets_income_limit is main's income test with year_lag=p.fpg_year_lag for heating.
    • Readers: ky_liheap_income_eligible is read only by ky_liheap_eligible (and the cooling helper import); ky_liheap_eligible only by ky_liheap's defined_for; ky_liheap only by regular_payments and programs.yaml.
    • The only test changes are the two new files, and the heating tests ran green in the full baseline shards.
    • The four re-described parameters are read only by Kentucky LIHEAP.
  • Documentation and citizenship rule. The fact sheet requires "Proof of Social Security Number or Permanent Residence card (Green Card) for each member of the household", matching Section 2(1)(d) and the reused heating function. Case 3 pins it.
  • Rent route and subsidized renter. The fact sheet accepts a "statement from your landlord if electric is included in your rent". Plan item 3.3 (page 10, read from the page image) answers No to differing policies for renters, subsidized renters and renters with utilities in rent. Case 4 (184 unsubsidized, 41 subsidized) is right by hand. GA and NV LIHEAP read tenant_pays_utilities the same way.
  • Mutation coverage of the other new terms.
    • Cooling limit and guideline lag: eligible Case 1 (1,995 / 1,995.01) and cooling Case 5 (1,976).
    • has_cooling_expense and electric bill > 0: eligible Case 2, households 1 and 2.
    • Rent route as a whole: eligible Case 4. UNSPECIFIED: cooling Case 6. Documentation: eligible Case 3. Kentucky gate: eligible Case 2, household 3.
    • Electric points for a gas home: cooling Case 1. Band guideline: cooling Case 4 (979 / 979.01; under 2026 bands 979.01 would pay $184). Subsidized share: cooling Case 2.
    • The regular_payments line: cooling Case 1 (liheap: 318) and test_liheap_total_sums_every_registered_state_payment. That test needs every float registry variable in the list, and ky_liheap_cooling is now one. Kentucky is not in LIHEAP_PAYMENT_STAND_INS.
  • All 10 test expectations hand-traced from the fact sheet, the workbook and the 2025/2026 guidelines, including Case 3 (22,880 / 12 = 1,906.67 ≤ ceil(3,137.50 × 0.75) = 2,354 → $250) and Case 5 (1,976 > 1,957 heating limit, ≤ 1,995 cooling limit, > 1,631 → $134).
  • National total and registry. The list line is a dollar amount on SPMUnit/YEAR, gated to Kentucky through ky_liheap_cooling_eligible (defined_for = StateCode.KY), and is not a component of ky_liheap. The second registry entry and the reworded heating note match the code.
  • Mixed-state and periods. No state-gated key: fuel_points.ELECTRICITY is a scalar; fuel_points[fuel] covers all 11 heating types; the state_group breakdown covers every group. Non-residents evaluate without error (masked ky_liheap_fpg and ky_liheap_income are 0), and the Microsimulation job passed. Both variables are YEAR, monthly tests divide annual income by 12, and nothing uses options=[ADD].
  • Hygiene. Labels, units, defined_for and references are on both variables. The parameter descriptions follow the template. Both fact sheets are one page, so no anchor is needed. Tests are in variable-named files, and the changelog fragment is changelog.d/ky-liheap-cooling.added.md. The source-conflict ratchet is unaffected: ky_liheap.py keeps its one counted line, and the new comments contain none of the counted patterns.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2187 queued. The issue is OPEN and labelled pe-parity, and it names the current head 799669b. It has proposed module paths, corpus citations with manifest status, verbatim source text, the required outputs for both variables, and 12 companion tests derived from the fact sheet, workbook and guidelines. It also lists interpretation gaps, including the second round, has_cooling_expense, the unpinned positive-rent term and the subsidized ordering. It depends on rulespec-us#1465 (Kentucky heating) and on ingest of the workbook and fact sheet, which it states. The line is valid. The only staleness is the CI sentence (Suggestion 7).

CI status

gh pr checks 10752: 36 of 36 pass at head 799669b (run 43287798487); none pending or failed. The Quick Feedback job ran both new files: ky_liheap_cooling.yaml 6 passed and ky_liheap_cooling_eligible.yaml 4 passed. The full baseline state shards (existing Kentucky heating tests), the Python shards and the Microsimulation job are all green.

Branch status

The branch is 15 commits ahead of main and 12 behind (cc48633). git merge-tree --write-tree against main is clean. Test merges against all eight sibling heads are clean: AR #10744, GA #10746, OK #10748, LA #10750, SC #10754, AL #10756, NE #10758 and VA #10760. That includes the adjacent la_liheap lines in regular_payments.yaml and programs.yaml from LA #10750. This PR does not touch source_conflicts/candidates.yaml.

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, two test cases. The amounts are right: the cooling Electric column equals the heating Electricity column in all 64 workbook rows, every cell I checked follows min(size, 5) + income points + 6 at 16.66 a point rounded up, and the fact-sheet limits are exactly 150% of the 2026 guidelines. Kentucky pays both the winter subsidy and the summer cooling subsidy in one program year, so the national total doesn't double count, and the refactor leaves heating unchanged line by line.

The rent route has two terms, ~tenant_pays_utilities and rent > 0, and only Case 4 reaches it, with both true. By my reading, dropping either term fails no case: a renter with positive rent who pays utilities separately, or a rent-free household with the flag false, would then get $184 with no electric bill. Two households, each expecting not eligible and $0, are in the report above (Should Address 1).

Seven optional suggestions follow; the most useful are labelling has_cooling_expense as a convention in the comment, and extending us-ky:liheap-subsidized-income-points to cooling. CI is green at 799669b167.

@hua7450

hua7450 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, David. This push, head 7c47ff923e, adds your two rent-route cases and removes the cooling formula's heating-fuel condition (fix commit 27080d37a2); upstream main is merged in.

Should address

  • 1. Rent-route terms. ky_liheap_cooling_eligible.yaml now ends with your two households, each expecting ky_liheap_cooling_eligible false and ky_liheap_cooling 0. Case 5 pays rent of $12,564 with tenant_pays_utilities true, heats with gas and has no electric bill or cooling cost. Case 6 pays no rent, has tenant_pays_utilities false and has no bill or cooling cost. By hand, dropping ~tenant_pays_utilities turns Case 5 into $184, and dropping rent > 0 does the same to Case 6.

Suggestions

  • 1. Two FY2026 rounds. The ky_liheap_cooling formula comment now gives the reason: Section 4(3)(a) allows one payment per component period, and plan item 1.1 offers cooling and summer components only "if additional money is received", so a second round depends on funding. The body's "Not modeled" bullet says the same and adds that both rounds were first come, first served. No rounds parameter. The registry note is unchanged: registry notes stay one or two sentences on years and scope.
  • 2. has_cooling_expense. The comment in ky_liheap_cooling_eligible.py now says the fact sheet asks for the current electric bill and sets no air-conditioning test, and that requiring has_cooling_expense with the bill is a PolicyEngine convention for cooling costs, not a Kentucky condition. The body says the same. The input stays on the direct route, and the rent route still does not need it.
  • 3. UNSPECIFIED for utilities-in-rent renters. The cooling eligibility formula no longer reads heating_type, so neither route has a fuel condition. This follows the project rule that a separate cooling payment adds no heating-fuel condition of its own. ky_liheap_cooling.yaml Case 6 keeps its inputs and now expects [true, true] and [184, 184] for the NONE and UNSPECIFIED homes, with "Was" comments; ky_liheap stays [0, 0]. By hand, your utilities-in-rent example now gets $184; no separate case was added for it. Heating code is unchanged. The body's UNSPECIFIED sentence is replaced.
  • 4. Subsidized-points candidate. us-ky:liheap-subsidized-income-points now cites Sections 4(1)(c) and 4(3)(c)2, plan item 3.4 (page 10) and workbook cells K52-K55. It adds the cooling amounts ($41, $45, $51 and $56 for one person) and your example ($41 under the workbook, $56 under the regulation's ordering). It also lists cooling/ky_liheap_cooling.py in pe_us.code and this PR in prs. The cooling formula comment names the candidate, and the body now says the PR extends it to cooling.
  • 5. Incomes above the printed range. The ky_liheap_cooling formula comment now says incomes above the printed ranges but within the cooling limit take the top band. The body's "Not modeled" bullet gives the one-person range: above $1,957 and up to $1,995.
  • 6. Anchors and the shared comment. The ky_liheap_meets_income_limit comment in ky_liheap_income_eligible.py now names the heating fact sheet as the source of the $688, and says the cooling fact sheet's $710 is exact (5,680 × 1.5 / 12 = 710). Separate #page=10 and #page=11 entries were not added: each source is listed once, with its pages in the comment above the link, and the existing # PDF pages 4,10-11 comment has that shape.
  • 7. Axiom issue wording. LIHEAP cooling: encode Kentucky LIHEAP cooling subsidy eligibility and one-time electric payment (PE-US PR 10752) TheAxiomFoundation/rulespec-us#2187 now points at 7c47ff923e. Both "not yet" CI lines are gone; it says both files passed at 799669b167. It also updates the scope and the eligibility output (no fuel condition), the test counts (12 cases) and companion test 10 ($184 for both homes). It adds companion tests 13 and 14 for your two cases, updates gaps 1 and 5-8 and the checklist, and drops the UNSPECIFIED gap.

CI is running at 7c47ff923e.

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

This branch has not been deployed

No deployments
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 Kentucky LIHEAP regular cooling assistance

2 participants