Skip to content

Add Georgia LIHEAP regular cooling assistance - #10746

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

hua7450 merged 19 commits into
mainfrom
ga-liheap-cooling

Conversation

@hua7450

@hua7450 hua7450 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Georgia's regular LIHEAP cooling benefit: $500 at income Level I and $400 at Level II.

Fixes #10745

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

What and why

The FY2026 benefit matrix ("UPDATED W/FY 2026 INCOME GUIDELINES") prints a COOLING BENEFIT column of $500 at Level I and $400 at Level II, one value for every main heating fuel; the FY2025 matrix has the same column. The FY2026 plan sets the same 60% SMI limit for cooling (item 3.1) and a $400 to $500 range (item 3.6). Plan item 16.1 removes the rule that gave households with a member aged 60 or older the highest benefit. The DFCS manual (PAMMS chapter 100, p. 5) allows "a maximum of one heating benefit and one cooling benefit within the same program year". This PR follows the FY2026 matrix and plan over the older manual figures: the 2020 PAMMS chapter's $350 to $400 with seniors at $400, and the January 2022 manual's $350 to $500 with seniors at $500 (section 101.4).

  • New ga_liheap_income_eligible holds the income test, including the federal 110% poverty floor, moved unchanged from ga_liheap_eligible so cooling can reuse it. Heating results are unchanged.
  • ga_liheap_cooling_eligible: household size, the income test, and either has_cooling_expense or heating's direct payment route for unsubsidized rent that includes all utilities (ga_liheap_has_direct_payment_route). The manual's energy burden table pays this renter directly, and section 1300.2 pays costs "included in their rent and designated as a separate cost"; the rental agreement is assumed to designate the cost, as heating assumes the agreement is verified.
  • ga_liheap_cooling: $500 when income is at or below the Level I cutoff (the shared parameter, now described as Level I payments), otherwise $400. New parameter cooling/amount.yaml. No bill cap. FY2027 has no posted matrix, so the FY2026 amounts carry forward; the FY2027 plan prints the same $400 to $500 range (item 3.6).
  • The heating fuel does not affect cooling: the matrix prints one cooling amount for every fuel, so every heating_type, including NONE and UNSPECIFIED, can be paid. Paying NONE reads that single cooling cell as covering homes with no heating.
  • has_cooling_expense is the cooling-cost input; the DHS 2026 release asks applicants for the "Most recent cooling bill or statement of service from their energy providers".

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. 13 cases in 3 files: both levels at the cutoff and above it, the income limit, the moved income test, no cooling cost, UNSPECIFIED fuel (paid), rent that includes all utilities, a household above size 16, a unit in another state, and the national liheap total.
  • Source-conflict register: candidate us-ga:liheap-cooling-benefit-amounts (ruled 2026-11-13 by the project lead: Add Georgia LIHEAP regular cooling assistance #10746 (comment)) records the choice of the FY2026 matrix and plan item 16.1 over PAMMS chapter 100, p. 5 ($350 to $400, seniors at the $400 maximum) and the January 2022 manual, section 101.4 ($350 to $500, seniors at $500).
  • make format (ruff)
  • Changelog fragment in changelog.d/

Not modeled

  • Subsidized renters whose rent includes all utilities and who report no cooling cost get $0, following heating's subsidy rule.
  • The first 30 days of each component for residents 65 and older and the medically homebound (plan items 3.3 and 3.4), funding on a "first-come, first-served basis until all funds are exhausted" (DHS 2026 cooling release), and targeting when funding is too limited (PAMMS chapter 100). The annual variable pays every eligible household.
  • The $1,000 account-credit rule (plan item 3.5): the household's account is assumed to meet it.

axiom: TheAxiomFoundation/rulespec-us#2180 queued

🤖 Generated with Claude Code

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

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describes the lower income limit as the Level I limit, checks the
national liheap total, and records the cooling benefit amounts in the
source-conflict candidates.

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

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

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

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

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

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 requested a review from DTrim99 November 12, 2026 19:13
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Georgia LIHEAP regular cooling assistance

Reviewed at head 3a78498bb3 (13 files, +433/-19). Sources read for this review: the FY2025, FY2026 and FY2027 Georgia LIHEAP state plans, the FY2024, FY2025 and FY2026 benefit matrices, the DFCS PAMMS LIHEAP export (cover date 2026-11-04; chapter 100 effective October 2020), the LIHEAP Policy Manual revised January 2022, the FY2022 cooling FAQ, the DHS cooling press releases of 2025-04-02 and 2026-04-06, and the HHS FY2024 Georgia state profile. No test was run locally: every result below rests on reading, hand traces and the CI logs, and says which.

Summary

The core of the PR is right. Georgia pays a regular cooling benefit that is separate from heating, a household may receive both in one program year, and the amounts ($500 at Level I, $400 at Level II, one value for every fuel) match the printed COOLING BENEFIT column in the FY2025 and FY2026 matrices. The Level I cutoff is the same Step 1 range heating uses, the fiscal-year dating is right for a summer benefit, the income test was moved without changing any heating result, and the stale path in the existing floor candidate was updated. CI ran the three new test files green at this head.

Four items should be addressed before merge, none critical:

  1. The UNSPECIFIED heating-type exclusion is described as holding "in every state LIHEAP program", which Georgia's own heating variable contradicts, and it has no Georgia source.
  2. Renters whose rent includes all utilities are paid by the heating variable and by the manual, but get $0 cooling, and the code comment gives a reason that is not accurate.
  3. The new source-conflict candidate is state: ruled with no ruling on record.
  4. The size clip in ga_liheap_cooling (the only guard against a missing-key lookup) is not pinned by any test.

Critical (must fix)

None.

Should address

1. UNSPECIFIED exclusion: the stated basis is wrong for Georgia, and the term has no Georgia source

  • Where: policyengine_us/variables/gov/states/ga/dfcs/liheap/cooling/ga_liheap_cooling_eligible.py:26-28 and :33; the same sentence in policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/cooling/ga_liheap_cooling_eligible.yaml:90 and in the PR body ("heating_type UNSPECIFIED pays $0, as in every state LIHEAP program").
  • What is wrong: Georgia's own heating program pays an UNSPECIFIED fuel. ga_liheap_eligible.py:55-60 excludes only NONE, and ga_liheap_matrix_amount.py:70-74 pays the direct-payment renter whatever the fuel. The existing test ga_liheap.yaml Case 14 ("bundled utilities in rent pay the income level minimum when the fuel is unspecified") expects ga_liheap: [500, 400, 0] with heating_type: UNSPECIFIED. So the claim is false in the same directory.
  • Why it matters for cooling: issue 9754 describes heating formulas that have no fuel row for UNSPECIFIED. The cooling formula has no fuel row at all: the matrix prints one cooling amount per level across every fuel (FY2026 matrix, Step 2: Level I "$500", Level II "$400", one merged cell beside ELECTRIC, NATURAL GAS, OIL, PROPANE and WOOD). The term also does no work for households that send no inputs, because has_cooling_expense already defaults to false. It only removes households that affirmatively report a cooling cost. No Georgia source conditions cooling on a known heating fuel: FY2026 plan item 3.2 answers No to "additional eligibility requirements for Cooling assistance" (read from the page image).
  • Concrete household (2026): one Georgia adult, wages $12,000, renter with heat included in rent and the building's fuel unknown, so heating_type: UNSPECIFIED (the value the heating_type documentation prescribes for that case), own electric bill of $1,800, has_cooling_expense: true. Expected: $500. Income $12,000 is in the size-one Level I range "$0 - $13589", and the manual's energy burden table gives this renter a burden ("Heat costs included in rent and applicant is billed directly for electric (non-subsidized)": Yes, "Paid to electric vendor", manual page 89). Actual by hand trace: the fourth term at line 33 is false, so ga_liheap_cooling_eligible is false and ga_liheap_cooling is $0.
  • Fix: drop the UNSPECIFIED term (recommended; nothing in the cooling formula reads the fuel), and replace Case 4 with a case that expects the benefit. If the term stays as a PolicyEngine convention, say so in the comment, the test comment and the body, remove "as in every state LIHEAP program", and note that Georgia heating pays an unspecified fuel on the direct-payment route.

2. Rent that includes all utilities: heating pays this renter, cooling does not, and the stated reason is inaccurate

  • Where: ga_liheap_cooling_eligible.py:22-24 ("No input identifies cooling costs included in rent, so those households are not paid") and the PR body's "Not modeled" paragraph.
  • What is wrong: an input does identify this class, and the heating variable already uses it. _ga_liheap_housing.py:17-23 (ga_liheap_has_direct_payment_route) reads heat_expense_included_in_rent & ~tenant_pays_utilities & rent > 0, unsubsidized, as "rent that includes all utilities", and ga_liheap_eligible.py:44 treats that as an energy burden. Rent that includes all utilities includes the electricity that runs the cooling.
  • Governing text: the PR's own sources tie cooling to heating eligibility. PAMMS chapter 100 (PDF page 4): "Clients must meet the eligibility requirements set forth for the current fiscal year's Heating Program." Cooling FAQ, page 1: "LIHEAP heating and cooling assistance adhere to the same eligibility guidelines." The manual's energy burden table is not specific to heating (page 89, read from the page image): "Utility costs (heat and electric) included in rent (non-subsidized)": energy burden "Yes", "Direct pay to Applicant (999)", "Landlord letter or rental agreement". Section 1300.2 (page 70) pays "eligible individuals whose heating or cooling costs are included in their rent and designated as a separate cost". Plan item 3.3 answers No to differing policies for "Renters with utilities included in the rent" (page 10, page image).
  • Concrete household (2026): ga_liheap.yaml Case 14, household 1: one adult, wages $12,000, rent $12,000, heat_expense_included_in_rent: true, tenant_pays_utilities: false, unsubsidized, no separate bill. Heating at this head: $500 (the test expects it). Cooling under the texts above: eligible, paid to the applicant, $500 (Level I; the cooling column has one amount per level, so the "lowest benefit amount" rule of plan item 16.1 changes nothing). Actual by hand trace: has_cooling_expense is false because the cost is inside the rent, so cooling is $0 and liheap is $500 where the rule gives $1,000.
  • Record: the linked rulespec-us issue calls this "a settled decision for Georgia (ruled)" and itself says "The heating module ... pays an all-utilities-in-rent renter directly; the cooling branch does not." Nothing in this PR or in source_conflicts/candidates.yaml records that decision.
  • Fix: either pay cooling on the same route (reuse ga_liheap_has_direct_payment_route, with a case built from Case 14 that expects $500 and $400 and liheap of $1,000 and $800), or keep the gap and correct the comment to say that the route exists in Georgia's text and in the heating variable and is left out by decision, with where that decision is recorded.

3. The new candidate is state: ruled with no ruling on record

  • Where: source_conflicts/candidates.yaml:2771-2807, entry us-ga:liheap-cooling-benefit-amounts. Its note reads: "The user ruled on 2026-11-08 that the FY2026 matrix and plan item 16.1 control over the PAMMS chapter and the 2020 and 2022 manuals."
  • What is wrong: the note names no role and links no record. Neither this PR nor issue 10745 has a comment (both threads are empty as of this review). Recent ruled entries in the same file name the project lead and link the comment, for example "Ruled 2026-11-05 for the regulation by the project lead; the ruling record is Add Idaho LIHEAP regular heating assistance #10613 (comment)...".
  • The substance is sound, and I agree with it: the matrices and plans are the dated, year-specific sources (FY2026 plan item 16.1, page 30: "Georgia updated the benefit matrix to remove all households with a member aged 60 or older receive the highest energy assistance benefit available"), while PAMMS chapter 100 is effective October 2020 and the manual is the January 2022 revision. It is also consistent with the heating amounts already on main, which follow the same matrices. No existing Georgia LIHEAP candidate is ruled; the four on main are unreviewed, three of them following the manual over the plan on eligibility and income, which is a different question from a dated benefit amount.
  • Fix: link the ruling (a comment by the project lead on this PR is enough) and name the role in the note, or set the state to unreviewed until that exists. While editing the entry, add the January 2022 manual, section 101.4 (page 5: "minimum of $350 and maximum of $500 ... senior(s) 60 years of age and older ... will receive the maximum benefit amount of $500") as a source, since the note relies on it and sources lists only PAMMS.

4. Mutation gap: the size clip in ga_liheap_cooling is unpinned

  • Where: policyengine_us/variables/gov/states/ga/dfcs/liheap/cooling/ga_liheap_cooling.py:28, lookup_size = clip(size, 1, p.payment.maximum_table_size).
  • What is missing: payment/lower_income_limit.yaml has keys 1 to 16 only. The clip is the guard for two reachable cases: a household above 16 people, and a size of 0, which every non-Georgia unit has when the formula runs over a mixed-state population (the formula runs for all units once one Georgia unit is cooling eligible). By my reading no case evaluates ga_liheap_cooling for either: all cooling cases have sizes 1 or 2 in Georgia-only populations, the size-0 unit in ga_liheap_cooling_eligible.yaml Case 3 does not request ga_liheap_cooling, and liheap.yaml Case 5 (mixed states) has no Georgia unit. Removing either bound would, by my reading, leave every test green and raise a missing-key error in those cases. The heating test ga_liheap.yaml Case 15 pins the same clip only for ga_liheap_matrix_amount.
  • Fix: add two cases to ga_liheap_cooling.yaml: a 17-person Georgia household (FY2026 size-16 cutoff $42,336: income at or below it expects $500, above it $400), and a Georgia unit with a cooling cost beside a unit in another state (expects [500 or 400, 0]).
  • Every other new element is pinned, by my reading: size > 0 (eligible Case 3), the income test (eligible Case 1), has_cooling_expense (eligible Case 2), the UNSPECIFIED term (eligible Case 4), NONE being paid (cooling Case 2), both amounts and the inclusive cutoff (cooling Cases 1 to 3), FY2025 dating (cooling Case 4), both arms of ga_liheap_income_eligible (its Cases 1 and 2), and the list line (cooling Case 1 expects liheap: 1_000, and test_liheap_total_sums_every_registered_state_payment fails if a registry payment variable is missing from the list).

Suggestions

  1. FY2027. The comment in cooling/amount.yaml:2-3 says FY2027 has no posted matrix, which is true (the Clearinghouse has no FY2027 Georgia matrix). The FY2027 plan is posted (accepted, Revision 1) and prints the same "Minimum Benefit $400 Maximum Benefit $500" at item 3.6 and cooling dates 04/01/2027 to 07/31/2027 at item 1.1. Citing it would back the carry-forward. verified_years: "2025-2026" is correct as it stands, since the 2027 Level I cutoffs are also carried forward.
  2. Disclose what the annual variable leaves out. The plans give the first 30 days of each component to residents 65 and older and the medically homebound (items 3.3 and 3.4, page 10), and DHS opened cooling to them on April 1 and to "All other eligible Georgians" on May 1 in both 2025 and 2026, "first-come, first-served basis until all funds are exhausted" (2026 release, 2025 release). PAMMS page 4 adds that if "funding is too limited ... more vulnerable households such as the elderly and homebound may be targeted", and plan item 3.5 carries the $1,000 account-credit rule. The rulespec-us issue lists all of these as out of scope; the registry note and the eligibility comment do not.
  3. A better current source for has_cooling_expense. Both DHS releases ask every applicant for the "Most recent cooling bill or statement of service from their energy provider". That is closer support for the cooling-cost condition than the FY2022 FAQ, and could be added to the references of ga_liheap_cooling_eligible.
  4. One reference per page. ga_liheap_cooling.py:14-17 notes "PDF pages 11, 30" but links only #page=11; add the #page=30 link for item 16.1. ga_liheap_income_eligible.py:14-15 notes pages 8, 10 and 43 with one FY2026 link.
  5. No-heat households. Paying heating_type: NONE is a reading, not printed text: Step 2 starts from "the main heating fuel type" and lists five fuels, and the cooling cell spans all five. The reading is reasonable and the rulespec-us issue records it; a one-line comment in the variable would record it here too.

Verified correct

  • A regular cooling benefit exists in each claimed year, separate from heating. FY2025 plan item 1.1: cooling 04/01/2025 to 07/31/2025, 24% of funds; FY2026: 04/01/2026 to 07/31/2026 (the explanation runs the season to August 31, 2026), 29% of funds (page 4 of each plan, table text). The matrices for both years print a COOLING BENEFIT column. The DHS releases confirm the program opened in both years, and the HHS FY2024 state profile reports "Cooling Assistance served 75,919 households with a reported average benefit of $437".
  • Heating and cooling in the same year: both are paid, so listing both is not a double count. PAMMS chapter 100, PDF page 5 (printed page 3; export cover date 2026-11-04; chapter effective October 2020): "Households may receive a maximum of one heating benefit and one cooling benefit within the same program year." The January 2022 manual says the same at 101.4 (page 5), 1100.2 (page 50) and 1300.2 (page 70). FY2026 plan item 4.2 (page 12): "Only one benefit payment is allowed per program component, either regular or crisis." FAQ page 1: "each household is eligible to apply for one heating assistance and one cooling assistance per program year." In the FY2024 profile the component counts (83,520 heating, 75,919 cooling, 10,882 crisis) sum to far more than the 117,338 households served.
  • Amounts and cutoffs. cooling/amount.yaml: $500 and $400 from 2024-10-01 match the FY2025 and FY2026 matrices. All 32 Level I cutoffs in payment/lower_income_limit.yaml match the two matrices (FY2026 sizes 1 to 16: 13,589; 17,770; 21,952; 25,469; 30,315; 34,496; 35,280; 36,064; 36,848; 37,632; 38,416; 39,200; 39,984; 40,768; 41,552; 42,336). The matrices print the cutoff as a dollar range by household size and annual income, with no percentage; Step 2 applies the same Step 1 level to the heating and the cooling columns, so sharing the parameter is right. No senior amount (plan item 16.1, in the FY2025, FY2026 and FY2027 plans), no bill cap (item 3.5 leaves "Individual bill" unchecked), plan item 3.6 "$400" to "$500" on page 11 of each plan.
  • Dating. Annual variables read parameters on January 1, so period 2026 reads the 2025-10-01 values (FY2026, summer 2026 season) and period 2025 reads the 2024-10-01 values (FY2025, summer 2025 season).
  • Eligibility is open to all income-eligible households. Plan items 3.1 (60% of state median income), 3.2 (No additional requirements) and 3.3 (No assets test; Yes only for priority to older adults and individuals with a disability, described as service in the first 30 days) were read from the page image of page 10. The priority is an early application period, not a restriction.
  • Refactor. ga_liheap_income_eligible holds main's lines unchanged (same parameters, same fpg call with year_lag, same rounding, (income <= limit) | (income < floor)), with the same StateCode.GA gate. ga_liheap_eligible now reads it; its only other reader is ga_liheap through defined_for. No existing test file changed. lower_income_limit.yaml changed only its description; its readers are ga_liheap_matrix_amount and ga_liheap_cooling. The federal 110% floor carries into cooling through the shared test.
  • Existing source-conflict records. The deciding line of us-ga:liheap-income-floor-vs-smi-limit moved, and the PR updates its code path and note to ga_liheap_income_eligible.py. The two other candidates that name ga_liheap_eligible.py (household members, subsidized renters) still point at lines that remain there. No marker or pinned register entry names a Georgia LIHEAP file.
  • National total and registry. ga_liheap_cooling is a USD amount on the SPM unit, annual, gated to Georgia through ga_liheap_cooling_eligible, and not a component of ga_liheap. The registry gains a second Georgia implementation; labels include the name, so test_entry_labels_are_unique is satisfied, and test_liheap_total_sums_every_registered_state_payment requires the new variable in the list, which it is. LIHEAP_PAYMENT_STAND_INS needs no Georgia entry.
  • Mixed-state and periods. The only state-gated key is ga_liheap_household_size, guarded by the clip (see Should 4 for the test). No options=[ADD] in the new files; all reads are annual.
  • Hand-checked expectations (FY2026 unless noted):
    1. Cooling Case 1: size 2, $15,080 is at or below $17,770, so $500; electric heat Level I 400 + 100 = $500; liheap 500 + 500 = $1,000.
    2. Cooling Case 2: $29,070 is in "$13590 - $34549", so $400 for gas heat and for no heat.
    3. Cooling Case 3: $13,589 pays $500; $13,589.01 pays $400.
    4. Cooling Case 4 (FY2025): $15,080 is at or below $16,993, so $500; limit $43,203.
    5. Eligible Case 1: 29,070 + 26,920 = $55,990, above $45,179 and not below 1.1 x 21,150 = $23,265, so not eligible, $0.
    6. Eligible Case 2: gas Level II heating $560; no cooling cost, so cooling $0.
    7. Eligible Case 3: sizes 1 and 0, so eligible true and false.
    8. Eligible Case 4: UNSPECIFIED, false and $0 as coded (see Should 1).
    9. Income Case 1: $34,549 passes, $34,549.01 fails (floor 1.1 x 15,650 = $17,215).
    10. Income Case 2: floor 1.1 x (15,650 + 15 x 5,500) = 1.1 x 98,150 = $107,965; $107,964.99 passes, $107,965 fails against the printed $107,634 limit.
  • Citations. Every quoted sentence in the body and comments was found at the cited page: PAMMS PDF pages 4 and 5, plan pages 8, 10, 11, 30 and 43, manual 101.4 on page 5, FAQ page 1. Parameter metadata (description, label, unit, period, references) and the changelog fragment changelog.d/ga-liheap-cooling.added.md are in order; tests sit in variable-named files.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2180 queued is valid. The issue is OPEN, labelled pe-parity, names this head (3a78498bb3), and is dispatch-ready: module paths, corpus citations and status, verbatim source text, required outputs, and ten companion tests derived from the printed sources. It matches the head's behavior. If Should 1 or Should 2 changes the code, update the issue's outputs table, companion test 8 and interpretations 4 and 7 to match.

CI status

At head 3a78498bb3: 32 checks pass, 0 fail, 4 pending (Contrib tests: states-shard-1, states-shard-4, other-shard-1, other-shard-3). The PR validation job (lock, guard tests, lint, changelog) is green, all eleven Baseline shards are green, and the selective-tests job ran the three new files at this head: 4 passed, 4 passed and 2 passed. The pending Contrib shards do not cover the changed files; re-check them before merge.

Branch status

7 commits behind main (96a9cea450). A test merge with main is clean, and the merged tree keeps both Georgia lines in regular_payments.yaml, both registry entries and all five Georgia LIHEAP candidates. Test merges against the eight sibling heads (AR #10744, OK #10748, LA #10750, KY #10752, SC #10754, AL #10756, NE #10758, VA #10760) are all clean at their current heads, including regular_payments.yaml, programs.yaml and 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 changes on four points; none is critical, and the core of the PR is right. Georgia pays a regular cooling benefit separate from heating, a household may get both in one program year, the $500 and $400 match the printed COOLING BENEFIT column for FY2025 and FY2026, and the income test moved without changing any heating result.

  1. heating_type UNSPECIFIED. The comment, the test and the description say it pays $0 "as in every state LIHEAP program", but Georgia's own heating variable pays an unspecified fuel (ga_liheap.yaml Case 14), and nothing in the cooling formula reads the fuel. Drop the term, or label it a PolicyEngine convention and remove the claim.
  2. Rent that includes all utilities. Heating pays this renter through ga_liheap_has_direct_payment_route, and the manual pays them for cooling too, but cooling gives $0. The comment's "no input identifies cooling costs included in rent" isn't accurate. Pay cooling on the same route, or say the gap is a decision and where it is recorded.
  3. The new candidate is state: ruled with no ruling on record. Its note says "The user ruled on 2026-11-08", names no role and links nothing. Link the project lead's ruling, or set it to unreviewed.
  4. The size clip in ga_liheap_cooling is pinned by no test. It is the only guard against a missing key for a household above 16 people or a unit in another state. Two cases close it.

Details and worked households are in the report above. CI is green at 3a78498bb3 apart from three contributed-reform shards still running.

@hua7450

hua7450 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

Ruling by the project lead, 2026-11-13: us-ga:liheap-cooling-benefit-amounts

For Georgia's regular cooling benefit, follow the FY2026 benefit matrix (step 2, the cooling benefit: $500 at Level I and $400 at Level II for every main heating fuel) and FY2026 plan item 16.1, which removed the rule that households with a member aged 60 or older receive the highest benefit. PAMMS chapter 100 (page 5: $350 to $400, seniors at the $400 maximum) and the January 2022 manual (section 101.4: $350 to $500, seniors at the $500 maximum) are not followed for the amounts.

Reason: the matrix and the plan are the dated, year-specific sources for FY2026; the PAMMS chapter has been in effect since October 2020. FY2027 has no posted matrix, so the FY2026 amounts carry forward.

Example: a one-person Level I household with a member aged 70 receives $500 of cooling.

hua7450 and others added 2 commits November 13, 2026 15:25
Addresses DTrim99's round-1 review; upstream main merged first.

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

hua7450 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

This push (head 0046a70a0a) makes the four changes and applies four of the five suggestions in ea297cd9a9, and merges upstream main.

Should address

  • 1. heating_type UNSPECIFIED. The term is dropped, as you recommended. ga_liheap_cooling_eligible no longer reads heating_type; its comment says the matrix prints one cooling amount for every fuel. ga_liheap_cooling_eligible.yaml Case 4 keeps its inputs and now expects true and $400, with a "Was" comment. "As in every state LIHEAP program" is gone from the formula comment, the test comment and the description.
  • 2. Rent that includes all utilities. Cooling now pays on heating's route: eligibility is has_cooling_expense | ga_liheap_has_direct_payment_route(...), so heating's subsidy rule carries over. The "no input identifies" sentence is replaced, and the references add manual pages 70 and 89. The 1300.2 quote runs through "and designated as a separate cost", and the comment says the rental agreement is assumed to designate the cost, as heating assumes the agreement is verified. New ga_liheap_cooling_eligible.yaml Case 5 is your Case 14 household 1: ga_liheap $500, cooling $500, liheap $1,000. Its rent is $12,072 (HUD FY2026 one-bedroom Fair Market Rent for Dougherty County, $1,006 x 12) in place of $12,000, because test rents use sourced amounts; the route needs only rent above zero. I did not add the $24,000 household: tests keep one case per equivalence class, the amount does not depend on the route, and Level II is pinned in cooling Cases 2, 3 and 6. "Not modeled" in the description now says that subsidized renters with all utilities in the rent and no cooling cost get $0, as in heating.
  • 3. Ruled candidate with no record. The project lead's ruling is this comment. The note of us-ga:liheap-cooling-benefit-amounts now starts "Ruled 2026-11-13 by the project lead; the ruling record is" with that link, followed by the reading; "The user ruled on 2026-11-08" is gone. The January 2022 manual, section 101.4 (page 5), is added to sources and to readings.other. The description's source-conflict line links the ruling.
  • 4. Size clip. ga_liheap_cooling.yaml Case 5: size 17, income $42,336 and $42,336.01, expects $500 and $400 (limit $109,627, as in heating Case 15). Case 6: Case 2's cashier in Georgia beside the same household in Alabama expects household size [1, 0], eligibility [true, false] and cooling [400, 0].

Suggestions

  • 1. FY2027. cooling/amount.yaml now cites FY2027 plan item 3.6 (page 11), and its comment says the plan keeps the $400 minimum and $500 maximum. verified_years stays "2025-2026".
  • 2. Disclosures. The eligibility comment and "Not modeled" in the description now list the first 30 days for residents 65 and older and the medically homebound (plan items 3.3 and 3.4), first-come funding (credited to the 2026 DHS release), targeting when funding is too limited (PAMMS page 4), and the $1,000 account-credit rule, which is assumed met. The registry note does not carry them: registry notes stay one or two sentences on years and scope; the text is in the code comment and the description. The note did drop its two scope claims that are no longer true (any known fuel, and in-rent costs out of scope).
  • 3. DHS release. The 2026 release is now a reference of ga_liheap_cooling_eligible, quoting its "Most recent cooling bill or statement of service from their energy providers". The description quotes it too.
  • 4. One reference per page. Declined: each source URL is listed once, with the href on the first cited page and the other pages in a # PDF pages ... comment above it. ga_liheap_cooling.py and ga_liheap_income_eligible.py already have that shape.
  • 5. No-heat households. The eligibility comment now says that paying a home with no heating reads the single cooling cell as covering it. The description says the same.

TheAxiomFoundation/rulespec-us#2180 now names this head. Its cooling-eligible output, companion test 8 and interpretations 4 and 7 are updated as you listed, and companion tests 11 to 13 cover the new cases. Tests were not run locally.

CI is running at 0046a70a0a.

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

DTrim99 commented Nov 15, 2026

Copy link
Copy Markdown
Collaborator

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

Reviewed at head 0046a70a0a. Round 1 (2026-11-12) was at 3a78498bb3 and requested changes: 0 critical, 4 should, 5 suggestions. Since then the branch has one fix commit, ea297cd9a9, and two merges of main, 6db6581139 and 0046a70a0a, all on 2026-11-13. Neither merge needed a hand-resolved conflict, and neither changed a line of this PR's own diff: comparing round 1's head with the new head, only the four Georgia files in the fix commit differ.

No test was run locally for this round. Each claim below says whether it rests on reading the code and tests at the head, on a hand trace, or on the CI logs. For this round I re-opened the FY2027 plan (page 11), the January 2022 manual (pages 5, 70 and 89) and the DHS cooling release of 2026-04-06.

Summary

All four should items are fixed, and each fix is pinned by a test. Suggestions 1, 2, 3 and 5 were taken. The decline of suggestion 4 is correct: what I suggested would have broken the reference rule in CLAUDE.md. The fix commit only widens cooling eligibility, in the two ways round 1 asked for, so no household that was paid at round 1 loses its payment and no amount changes. All 36 CI checks pass at the head, and the selective-tests job ran the PR's three test files: 13 cases, all passed. The branch merges cleanly with current main and with each of the eight open sibling PRs. One new suggestion is left, about wording in the queued rulespec-us issue.

Prior findings

Finding Status Evidence
Should 1: the UNSPECIFIED exclusion had a wrong stated basis and no Georgia source Fixed ga_liheap_cooling_eligible.py no longer reads heating_type. Its comment now says the matrix "prints one cooling amount per level for every main heating fuel, so the fuel does not affect eligibility". "As in every state LIHEAP program" and the issue 9754 reference are gone from the formula, the test file and the PR body (searched at the head). Pinned: ga_liheap_cooling_eligible.yaml Case 4 (heating_type: UNSPECIFIED, wages $29,070, cooling cost) now expects ga_liheap_cooling_eligible: true and ga_liheap_cooling: 400. Restoring the term would make it false and $0, so by my reading that case would fail.
Should 2: renters whose rent includes all utilities got $0 cooling, with an inaccurate reason Fixed Eligibility is now (size > 0) & ga_liheap_income_eligible & (has_cooling_expense | ga_liheap_has_direct_payment_route(...)). The route is heating's own helper, unchanged: heat in rent, tenant_pays_utilities false, rent above 0, no housing assistance, not in public housing. The "no input identifies" sentence is gone. The reference block adds manual pages 70 and 89, and the comment states the assumption that the rental agreement designates the cost. Pinned: new eligible Case 5 (heating Case 14's renter, has_cooling_expense: false) expects cooling eligible true, $500, and liheap $1,000. Without the new term it would be false and $0, so by my reading it would fail. Case 2 (no cooling cost, no rent) still expects false, which pins the other side of the OR. I accept the author's reason for not adding the Level II renter I proposed: the amount does not depend on the route, and Level II is pinned in cooling Cases 2, 3 and 6.
Should 3: the candidate was state: ruled with no ruling on record Fixed The ruling is a PR comment, quoted below under "Ruling record". The note of us-ga:liheap-cooling-benefit-amounts now reads "Ruled 2026-11-13 by the project lead; the ruling record is #10746 (comment)", and the old ruling sentence, which named no role and linked no record, is gone. The January 2022 manual, section 101.4, page 5, is now in sources and readings.other. I checked the quoted text on that page: "minimum of $350 and maximum of $500 ... senior(s) 60 years of age and older ... will receive the maximum benefit amount of $500". The PR body's source-conflict line links the ruling. The entry has every field CONTRIBUTING.md#source-conflicts asks for: id, program, years (2025 to 2026, federal fiscal), question, four sources with working links, readings, pe_us (follows: gap-fill, two code paths that exist at the head, prs) and state. The ruling, the note, the PR body and rulespec-us#2180 interpretation 1 all describe the same choice.
Should 4: the size clip in ga_liheap_cooling was unpinned Fixed New cooling Case 5 sets ga_liheap_household_size to 17 with incomes $42,336 and $42,336.01 and expects $500 and $400. payment/lower_income_limit.yaml has keys 1 to 16 only, so without the upper bound the lookup raises on key 17. New cooling Case 6 puts a Georgia unit beside an Alabama unit and expects household size [1, 0], eligibility [true, false] and cooling [400, 0]. policyengine-core runs a formula for every unit once any unit passes defined_for, and applies the mask afterwards (simulation.py, np.where(mask, array, default)). So the Alabama unit's size 0 reaches the lookup, and without the lower bound it raises on key 0. By my reading, each case would fail if its bound were removed.
Suggestion 1: cite the FY2027 plan Taken cooling/amount.yaml cites the FY2027 plan, item 3.6. Page 11 prints "Minimum Benefit $400 Maximum Benefit $500". verified_years stays "2025-2026".
Suggestion 2: disclose what the annual variable leaves out Taken in the code comment and the PR body; not in the registry note (optional) The eligibility comment and the body's "Not modeled" list the first 30 days for residents 65 and older and the medically homebound, first-come funding, targeting when funds are limited, and the $1,000 account-credit rule (assumed met). The author's reason for keeping the registry note short is fine.
Suggestion 3: cite the DHS 2026 release Taken The release is a reference of ga_liheap_cooling_eligible. Its quote, "Most recent cooling bill or statement of service from their energy providers", matches the release word for word. My round-1 quote dropped the final "s".
Suggestion 4: one reference per page Declined, correctly CLAUDE.md: "One reference entry per source document; URLs that differ only by #page= are one source", and "Variable reference tuples have no title: put a # PDF pages 61-62, 67 comment above a multi-page href." At the head, no document URL appears twice in ga_liheap_cooling.py, ga_liheap_cooling_eligible.py, ga_liheap_income_eligible.py or cooling/amount.yaml (checked by stripping the #page= part). Each multi-page href has a # PDF pages comment above it: 11, 30; 10-11; 70, 89; and 8, 10, 43. Adding a #page=30 entry, as I suggested, would have broken the rule. I withdraw the suggestion.
Suggestion 5: record the no-heat reading Taken The eligibility comment now says "paying a home with no heating reads that single cooling cell as covering it". The PR body says the same.

Ruling record. The cited comment exists. It was posted on 2026-11-13 at 20:24 UTC, from the author's account, under the heading "Ruling by the project lead". Merged rulings for #10762, #10613 and #10379 use the same form, and main's register already cites them. It reads:

Ruling by the project lead, 2026-11-13: us-ga:liheap-cooling-benefit-amounts

For Georgia's regular cooling benefit, follow the FY2026 benefit matrix (step 2, the cooling benefit: $500 at Level I and $400 at Level II for every main heating fuel) and FY2026 plan item 16.1, which removed the rule that households with a member aged 60 or older receive the highest benefit. PAMMS chapter 100 (page 5: $350 to $400, seniors at the $400 maximum) and the January 2022 manual (section 101.4: $350 to $500, seniors at the $500 maximum) are not followed for the amounts.

Reason: the matrix and the plan are the dated, year-specific sources for FY2026; the PAMMS chapter has been in effect since October 2020. FY2027 has no posted matrix, so the FY2026 amounts carry forward.

Example: a one-person Level I household with a member aged 70 receives $500 of cooling.

The ruling's figures match the sources. PAMMS page 5 reads "$350 and maximum of $400", the manual's page 5 reads "$350 and maximum of $500", and PAMMS chapter 100 prints "Effective Date: October 2020".

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

  1. Queued issue wording. Interpretation 4 of rulespec-us#2180 says that paying $0 to renters with cooling in the rent was "reversed" by "the project lead ... on 2026-11-13". The PR has no ruling on the in-rent route; the only ruling comment covers the benefit amounts. The change followed round-1 review and the manual's text (1300.2 and the page 89 table), so no ruling is needed. Either link a record or say the change followed review. Line 16 and the Tests bullet say the tests "have not yet passed in CI at this head". They have now (see CI), so those lines can be updated too. Neither point changes what the issue specifies.

Verified

  • No regressions from the fix commit (reading and hand trace). The new eligibility condition is a superset of round 1's: one conjunct was removed (the UNSPECIFIED test) and one disjunct was added (the direct payment route). Every household eligible at round 1 is still eligible. ga_liheap_cooling.py and the amount parameter values did not change, so no amount changes either. The fix commit touches no heating file. The households newly paid are the two classes round 1 asked for, both disclosed in the PR body: an UNSPECIFIED fuel with a cooling cost, and an unsubsidized renter whose rent includes all utilities.
  • No double payment (reading). The route and has_cooling_expense are ORed into one boolean, so a household on both still gets one cooling amount. Heating and cooling stay separate benefits, as PAMMS page 5 provides.
  • The route's terms are pinned (reading the tests). The helper is shared with heating and unchanged. ga_liheap.yaml Cases 9, 14, 16, 17 and 23 vary heat_expense_included_in_rent, tenant_pays_utilities, rent, receives_housing_assistance and is_in_public_housing, so a mutation of the helper fails heating tests. The body's "Not modeled" line follows from the helper's subsidy terms: subsidized renters with all utilities in the rent and no cooling cost get $0. It matches PAMMS page 4: "Subsidized housing residents whose fuel cost is included in their rental payment are not eligible for assistance unless the applicant can verify an energy burden".
  • Mixed-state evaluation (reading). The new route term reads only inputs and does no parameter lookup, so it is safe for non-Georgia units. Cooling Case 6 exercises a Georgia and an Alabama unit together.
  • New and changed expectations, by hand (FY2026):
    1. Eligible Case 4 (changed): $29,070 is at or below the size-one limit of $34,549, so income-eligible. The fuel is not read, so cooling-eligible. $29,070 is above the $13,589 Level I top, so $400.
    2. Eligible Case 5 (new): income is wages only, $12,000. The route holds (heat in rent, tenant_pays_utilities false, rent $12,072 > 0, unsubsidized, not public housing), so ga_liheap_eligible is true. Heating pays the direct payment, the lowest Level II amount plus the uplift: 400 + 100 = $500 (plan item 16.1, "the lowest benefit amount"). Cooling eligibility also holds on the route; $12,000 ≤ $13,589 gives Level I, $500. liheap = 500 + 500 = $1,000. The rent figure checks: the FY2026 one-bedroom FMR for the Dougherty County area (code 1309599999) is $1,006 in main's FMR table, and 1,006 x 12 = 12,072.
    3. Cooling Case 5 (new): the size-17 limit is 107,634 x 165/162 = $109,627 (each member above six adds 3%), the same as heating Case 15. Both incomes pass. The lookup uses size 16, $42,336, so $42,336 gives $500 and $42,336.01 gives $400.
    4. Cooling Case 6 (new): the Georgia unit has size 1, income $29,070 ≤ $34,549, so it is eligible at Level II, $400. The Alabama unit has size 0, is not eligible, and gets $0.
  • Sources for the new text (re-opened this round). Manual page 70, 1300.2: "To eligible individuals whose heating or cooling costs are included in their rent and designated as a separate cost". Manual page 89, row 2: "Utility costs (heat and electric) included in rent (non-subsidized)", "Yes", "Direct pay to Applicant (999)", "Landlord letter or rental agreement". Manual page 5, 101.4: as quoted above. FY2027 plan page 11, item 3.6: "$400" to "$500". DHS 2026 release: as quoted above.
  • Source-conflict ratchet, Tie source-conflict markers to the register shared with Axiom #10287 (pattern search of the comments, no run). No new or changed comment in the scanned Georgia files matches the guard's phrases (discrepancy, conflict, "contrary to", "departs from", "manual says", "in practice" and the rest). No Georgia file is in KNOWN_UNMARKED on main, and no us-ga id is in the vendored register, so the candidate is not double-registered.
  • Registry note (reading). The cooling note dropped its two claims that are no longer true ("any known heating fuel" and "cooling costs included in rent are out of scope"). It now matches the code.
  • PR body (reading). It matches the head. It lists 13 cases in 3 files (6 + 5 + 2), and every heating_type, including NONE and UNSPECIFIED, can be paid. It describes the route and its assumption, links the ruling, and updates "Not modeled".

Axiom

axiom: TheAxiomFoundation/rulespec-us#2180 queued is still valid. The issue is OPEN and labelled pe-parity, updated 2026-11-13 at 20:36 UTC, and names this head, 0046a70a0a, and the fix commit. It matches the fix:

  • The cooling-eligible output now lists both energy-burden arms with the route's five inputs, and says heating_type is not read.
  • Companion test 8 expects $400 for UNSPECIFIED.
  • New companion tests 11 to 13 mirror eligible Case 5 and cooling Cases 5 and 6, with hand derivations from the printed sources.
  • Interpretations 4 and 7 describe the route and the fuel change.
  • Interpretation 1 links the ruling.

It is dispatch-ready: module paths, corpus status, verbatim text, required outputs and companion tests. Suggestion 1 covers two wording points.

CI

All 36 checks pass at 0046a70a0a, in run 43501517436 (pull_request event, started 2026-11-13 at 20:26 UTC). That includes PR validation (lock, guard tests, lint, changelog), all eleven Baseline shards, all nine Contrib shards, the Python shards and Microsimulation. The selective-tests job log shows the PR's three test files ran at this head: ga_liheap_cooling.yaml 6 passed, ga_liheap_cooling_eligible.yaml 5 passed, ga_liheap_income_eligible.yaml 2 passed. That covers every new case. The existing Georgia heating tests ran in the Baseline states shards, which are green. Nothing is pending.

Branch

The head is 54 commits behind main (7c516c74bb). A test merge with main is clean. The merged tree keeps ga_liheap and ga_liheap_cooling in regular_payments.yaml, both Georgia registry entries, and all five Georgia LIHEAP candidates, with us-ga:liheap-cooling-benefit-amounts once.

Since CI ran, main has added one KNOWN_UNMARKED line for an IRS parameter in test_source_conflicts.py and has edited candidates.yaml and programs.yaml. None of this touches a Georgia file, so by my reading the guards pass on the merged tree.

The siblings #10730, #10732, #10734, #10736, #10738, #10740, #10742 and #10762 are on main, so the main merge covers them. Test merges against the current heads of the eight open siblings are all clean: AR #10744 c984f475b7, OK #10748 b3b87c9204, LA #10750 d4215baf78, KY #10752 7c47ff923e, SC #10754 5f487892d2, AL #10756 f3dbfc7056, NE #10758 a497a51822, VA #10760 bd1f8a61d8.

Verdict: APPROVE

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving (round 2). All four round-1 items are fixed:

  • The fuel term is dropped.
  • Cooling now takes heating's direct-payment route, so rent that includes all utilities is covered.
  • The candidate's note links the dated 2026-11-13 ruling.
  • Cases 5 and 6 pin both ends of the size clip.

No regression: the new eligibility condition is a superset of round 1's, and the amounts are unchanged. CI is green at 0046a70a0a, and the branch merges cleanly with main and the eight open siblings. The one suggestion is small: rulespec-us#2180 interpretation 4 credits a ruling that has no record, and two lines still say CI hasn't passed.

@hua7450
hua7450 merged commit 7177c6c into main Nov 15, 2026
36 checks passed
@hua7450
hua7450 deleted the ga-liheap-cooling branch November 15, 2026 01:14
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 Georgia LIHEAP regular cooling assistance

2 participants