Add Kentucky LIHEAP regular heating assistance - #9725
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9725 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 4 5 +1
Lines 76 108 +32
Branches 2 4 +2
=========================================
+ Hits 76 108 +32
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…come limit - Move the program from ky/chfs to ky/dcbs - Derive the above-eight income-limit increment from the lagged poverty guideline in place of the stored $688 - Drop the hud_utility_allowance proxy from the subsidized-housing test - Floor each unearned source at zero per source - Count ky_ssp, farm operations and farm rent income, workers' compensation and disability benefits; replace the deprecated dividend_income alias - Cite the statewide fact sheet PDF; add metadata.period to parameters - Document the workbook's reversed subsidized rows and the mixed-status gate assumption without changing behavior - Add end-to-end, income, subsidized-cell and large-household tests Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ky LIHEAP The FY2026 plan's Section 1.9 has no dedicated row for either source, so they are recorded as a documented gap instead of counted income. Update the income test case accordingly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Program ReviewPR #9725: Add Kentucky LIHEAP regular heating assistance (hua7450). Reviewed head: Scope: 25 files: 13 parameters, 5 variables, 5 test files (24 cases), a changelog fragment and Overall: The benefit arithmetic reproduces the published matrices exactly:
One eligibility rule contradicts 921 KAR 4:116. The other findings cover gaps in the income definition, a microsimulation default, citation precision and PR hygiene. Path abbreviations:
Line numbers refer to the files at head Source Documents
Branch Status
Critical (Must Fix)C1. The mixed-status gate approves households that 921 KAR 4:116 denies
Should AddressS1. Countable income leaves out three existing variables that the plan counts
S2. The Medicare treatment of SSA benefits is fixed in the formula, but the plans change it by year
S3. Microsim default:
|
| Parameter | Values | Source (page / cell) | Result |
|---|---|---|---|
earned_income_age |
1 (18) | Plan p6: "Earned income of a child under the age of 18" unchecked | Match |
fpg_year_lag |
1 (1) | Workbook "rev" R11 = 15,650/12; fact sheet p1 1,957 | Match |
income_limit |
1 (1.5) | Plan p8, §2.1 "150.00%" | Match (G3: add KAR §3(1)(b)) |
maximum_table_size |
1 (8) | Fact sheet p1, sizes 1-8 | Match |
payment/coal_adjustment |
6 (thresholds 0/2/3; amounts 0/-1/0) | Workbook "Subsidy points" H3, H8:H11, H13 | Match (S5: sheet label) |
payment/dollars_per_point |
1 (16.66) | Workbook "rev" L5:L28, N5 | Match |
payment/fuel_points |
11 (7 mapped; 4 set to 0 by design) | Workbook "Points Description" J2:J7 | Match (S5: sheet label) |
payment/income_bands |
3 (0.75/1/1.25) | Workbook "rev" Q10:S10, Q28:S28 | Match |
payment/income_points/subsidized |
4 (1/2/3/4) | Workbook "rev" E52:E55 formulas (reversed ordering) | Matches the workbook; conflicts with KAR §4(1)(c) p3 (S6) |
payment/income_points/unsubsidized |
4 (4/3/2/1) | Workbook "Points Description" H2:H5 | Match (S5: sheet label) |
payment/maximum_household_points |
1 (5) | Workbook "Subsidy points" E28; "rev" rows 30-43; plan p9 $250 maximum | Match (S5: sheet label) |
payment/subsidized_housing_rate |
1 (0.3) | Workbook "rev" E52 ROUNDUP(E8*0.3,0) |
Match |
unearned_income_sources |
18 | Plan p6 §1.9 boxes; p34 §17.4 | All 18 match; 3 omissions (S1) |
- Total: 53 parameter values audited. No mismatches survived verification.
- Derived outputs:
- 384 of 384 FY2026 dollar cells match (8 sizes x 4 bands x 6 fuels x 2 housing types).
- 384 of 384 FY2025 cells match.
- 8 of 8 fact-sheet income limits match. Each is ceil(FPG x 1.5 / 12) using the 2025 guideline: 1,957 / 2,644 / 3,332 / 4,019 / 4,707 / 5,394 / 6,082 / 6,769.
- Checkbox reads: the FY2025, FY2026 and FY2027 p6 checkboxes were confirmed from the page images. The §17.2 boxes on p33 were confirmed on a 600-dpi render.
- Source discrepancies checked, no finding:
- Workbook guideline years: the stale "2024-2025" title on the rev sheet does not matter, because Q:T use the 2025 guideline and the 2024-based cells feed no dollar cell.
- $687 vs $688: the fact sheet's "Add $688" controls (workbook T37 = 7,457 = 6,769 + 688). For sizes 10-14 the workbook is 1 to 3 dollars lower, which the code comments document.
- Size points capped at 5, not 6: the matrix hard-codes the size-5 values for larger households, and the $250 maximum requires the cap at 5.
- No asset test and no categorical test: plan §2.3 says there is no asset test and §1.4 answers "No", while the CHFS page's 130% limit and asset limits are stale.
- Benefits not capped at the bill: KAR §4(1) requires payment "for the full benefit amount", which supports this.
- Gross income and child earnings: gross income per plan §1.8, with child earnings under 18 excluded, matches all three plan years.
- Heat in rent: the responsibility rule matches KAR §3(2)(c).
- Subsidized status:
receives_housing_assistance | is_in_public_housingmatches KAR §4(1)(e).
Validation Summary
| Check | Result |
|---|---|
| Regulatory accuracy | 1 critical (C1, mixed-status gate). Income-definition gaps in S1 and S2 (period 2025 only) |
| Parameter values vs sources | 53 of 53 match |
| Benefit matrix reproduction | FY2026: 384 of 384. FY2025: 384 of 384 |
| Income limits | 8 of 8 fact-sheet values; "at or below" test matches KAR §3(1) |
| Reference links | 5 of 5 live (plan, workbook, fact sheet, KAR HTML, CAK announcement). The FY2025 workbook proposed in G1 is also live |
| Reference anchors and labels | 2 valid single-page anchors (earned_income_age.yaml:10 p6, income_limit.yaml:10 p8). 2 invalid multi-page anchors in files, plus 1 in the PR body (S4). 4 wrong sheet labels (S5). 1 contradicting reference (S6) |
| Code patterns | Only 0/1 numeric literals; fully vectorized; defined_for and entities correct; no duplicate variables (ky_liheap_fpg uses the shared fpg(..., year_lag=) helper). One aggregation-pattern note is folded into C1 |
| Test coverage | 24 cases; 7 hand recomputations match the workbook and fact sheet. Gaps: coal size 3 and subsidized coal (S11); subsidized size 6+ and a dedicated ky_liheap_fpg file (G12) |
| Test format | 14 underscore-formatted periods (S7). Naming (G9). One formula-variable override (G11) |
| Microsimulation | heating_type default gives a $0 KY total, biased downward by the full program amount (S3). C1 and S1 bias upward, size not quantified. S2 biases period 2025 upward by up to $2,220 a year of countable income per affected recipient |
| CI | 37 of 37 pass |
| Changelog | changelog.d/ky-liheap.added.md: valid type, top-level |
programs.yaml |
Format and placement correct. Order inconsistency (G13). Conflicts with sibling PRs expected |
| Axiom parity | n/a line does not meet CONTRIBUTING requirements (S10) |
| Branch | Head unchanged; 6 ahead and 11 behind main; no overlap with main; clean local merge |
Review Severity: REQUEST_CHANGES
1 critical and 11 should-address findings (13 suggestions).
Next Steps
- C1: require an SSN or permanent residency card for each member (at minimum each adult, per plan §17.2), using existing inputs. Then:
- Rewrite
tests/ky_liheap_eligible.yamlCase 1 to expect ineligible / $0. - Cite KAR §8(3).
- Update the
programs.yamlnote.
- Rewrite
- S1, S2: add
military_retirement_pay,railroad_benefitsandpartnership_s_corp_incometo countable income. Move the Medicare treatment of SSA benefits into a dated parameter, with tests on either side of the change. - S3: decide how UNSPECIFIED
heating_typeis handled in microsimulation (the current default zeroes the program) and lock the behavior in with a test. - S4-S6: split the
#page=anchors, correct the four workbook sheet labels, and retitle the KAR §4(1)(c) reference. Add the FY2025 workbook as a reference. - S7-S9: replace the
2_026/2_022periods, align the parameter descriptions, and remove draft and process wording from the code,programs.yamland the PR body. - S10: open a dispatch-ready rulespec-us issue labelled
pe-parityand change the Axiom line toqueued. - S11: add the size-3 coal and two-person subsidized coal cells.
- Suggestions: optional. G1 (verify FY2025 after S2) and G2 (note the FY2027 schedule change) affect the registry's year coverage.
- Rebase onto main once a sibling LIHEAP PR merges, then re-request review.
DTrim99
left a comment
There was a problem hiding this comment.
Requesting changes per the program review above (#9725 (comment)): 1 critical (921 KAR 4:116 §2(1)(d) and §8(3) deny households where a member lacks an SSN or permanent residency card, but the PR approves mixed-status households), 11 should-address items. All 53 parameter values and the 384 FY2026 benefit cells match the sources. Run /fix-pr 9725 or address the list.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Require a Social Security number or permanent residency card for every household member (921 KAR 4:116 Sections 2(1)(d), 2(2) and 8(3)), keeping the citizen or qualified member requirement. Count partnership and S corporation income and railroad benefits, move both source lists into parameters, and count Social Security gross with the Medicare premium deduction left as a documented gap. Date the parameter values from FY2025 with FY2025 plan and workbook references and record verified years 2025-2026. Name the worksheets and cells that hold each value, mark the regulation that conflicts with the subsidized ordering, use single-page references, fix test periods and names, and add coal, documentation and FY2025 cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Thanks for the review. Fixes are in Critical
Should-address
Suggestions
|
Program Review — Round 2PR #9725: Add Kentucky LIHEAP regular heating assistance (hua7450). Re-reviewed head: Scope at this head: 26 files: 14 parameters ( Path abbreviations (same as round 1):
Line numbers refer to head Status of Round-1 Findings
S1 note on
The choice is defensible. Suggestion R4 covers the cross-program difference. Critical (Must Fix)None. C1 is fixed as the regulation reads. Should Address (remaining)S2. Social Security is counted gross in FY2026 and FY2027, contrary to the plans the PR cites
S10. The queued rulespec-us issue is not dispatch-ready
SuggestionsNew in this round:
Carried over from round 1, not taken and optional:
Suggestions the Author Reports as Done (verified)
Regression Pass over the Delta
CI Status
Review Severity: REQUEST_CHANGES0 critical, 2 should-address (S2, S10) and 10 suggestions.
|
DTrim99
left a comment
There was a problem hiding this comment.
Round 2 (#9725 (comment)): the critical mixed-status gate is fixed and 9 of 11 should items are resolved. Remaining: (S2) Social Security is now counted gross in every year, but the FY2026 and FY2027 plans exclude the Medicare deduction (medicare_part_b_premium exists and is already used elsewhere); (S10) rulespec-us#1465 is labelled pe-parity but not yet dispatch-ready. The 3 CI failures are runner kills in shards without this PR's files.
…nd count military retirement pay The FY2026 plan checks "Excluding MediCare deduction" where the FY2025 plan checks "Including", so a dated parameter switches Social Security from gross to net of the Part B premium the person pays, taken from the existing medicare_part_b_premium variable. military_retirement_pay is a separate input from the pension inputs that pension_income adds, so it is listed beside pension_income. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…into ky-liheap # Conflicts: # policyengine_us/programs.yaml
|
Thanks for round 2. Head is now S2, Social Security and the Medicare premium. Fixed with your first option. A new dated parameter, S10, rulespec-us#1465. Filled in on 2026-10-02 with the PR link, module paths under S1 note and R4, military retirement pay. You are right that R1, R2, R3, R5 and the carry-overs (G2, G4, G7, G12, G8). Not taken in this round. Tests were not run locally; CI runs them. |
Program Review — Round 3PR #9725: Add Kentucky LIHEAP regular heating assistance (hua7450). This round reviews head Path abbreviations (same as earlier rounds):
Scope at this head: 27 files: 15 parameters ( Status of Round-2 Findings
Verification of the Medicare FixYears. The parameter is read as
The reading "Excluding" = net is corroborated by Indiana: its FFY2025 and FFY2026 plans check the same "Excluding MediCare deduction" box, and its PY2026 manual (Section 6.1, p55) states that "the net amount of the Social Security check, after the deduction for Medicare Part B premiums" is counted. Variable.
Code. The Python Hand checks (2026 standard premium $202.90 × 12 = $2,434.80; 2025 premium $185 × 12 = $2,220; MSP limit 1.35 × $15,960 / 12 = $1,795.50 a month in 2026 and 1.35 × $15,650 / 12 = $1,760.63 in 2025):
Existing tests touched by the rule. Only one other period-2026 case has a Medicare-age Social Security recipient: Microsimulation note (informational). Military Retirement Pay
Axiom Issue (rulespec-us#1465)Compared against the structure of #1467:
The issue is dispatch-ready. Three small refresh items are in Suggestion 3. Main-Merge Check (#9672, #9671, #9727)
Programs.yaml Merge ResolutionDiffed against the merge base
Regression Pass over the Delta
Critical (Must Fix)None. Should Address (remaining)None. S2 and S10 are resolved. SuggestionsNew in this round:
Carried over from round 2, optional:
CI and Branch Status
Review Severity: APPROVE0 critical, 0 should-address and 12 suggestions (3 new, 9 carried over).
|
DTrim99
left a comment
There was a problem hiding this comment.
Round 3 (#9725 (comment)): the Medicare Part B deduction and military retirement pay now match the state sources, the axiom issue is dispatch-ready, and the programs.yaml merge is correct; 0 critical, 0 should. The income lists are self-contained, so main's #9672 (snap_rental_income) does not cause double counting. #9727's merge reopened a programs.yaml conflict with main to resolve before merging.
…into ky-liheap Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds Kentucky LIHEAP regular heating assistance for FY2025 and FY2026, using the annual year to represent the heating season ending in that year. A one-person household with no countable income and a gas heating bill receives $134; the published subsidized-housing schedule gives $26.
Year convention: undated formulas follow the model's existing parameter backfilling. Parameter values are dated 2024-10-01 and verified against the FY2025 and FY2026 plans and benefit workbooks; the registry records
verified_years: "2025-2026". Earlier years use the earliest schedule with period-specific federal guidelines and the one-year guideline lag, and are unverified historical estimates.receives_housing_assistanceoris_in_public_housing. No new input variables.ky_ssp), railroad benefits, farm rental income, workers' compensation and employer disability benefits. Excludes the earnings of members under 18. Uses annualtanf, which already includesky_ktapand applies take-up.medicare_part_b_premium, which is zero for a person who is not enrolled and excludes the share a Medicare Savings Program pays, since that share is not withheld from the check. Military retirement pay is counted beside pension income, because the two are separate inputs.Household and immigration basis
921 KAR 4:116 Section 2(1)(d) requires "a Social Security number, or a permanent residency card, for each household member"; Section 2(2) treats the application as incomplete until it is received, and Section 8(3) denies it after five working days. A household with a member who has neither is therefore ineligible. The plan's Section 17.2 checklist requires the card from adults and requests it from other members; the regulation's per-member rule is applied. At least one member must also be a citizen or qualified noncitizen, because LIHEAP is a federal public benefit. Section 1(10) defines the household with no immigration carve-out, so every member counts for size and income. 921 KAR 4:116; Kentucky FY2026 plan, Sections 17.2 and 17.3, pages 33 and 34
Source discrepancies
The FY2026
rev benefit income rangeworksheet has a stale 2024-2025 title but uses 2025 poverty guidelines and matches the FY2026 plan's $15-$250 range; the conditionalif no funding FFY26sheet is not used. The two-person coal cells are preserved as published. Both workbooks' subsidized rows reference the unsubsidized rows in reverse order, so the published subsidized payment rises with income, while 921 KAR 4:116 Section 4(1)(c) gives the highest benefit to the lowest income; the model reproduces the published cells, and which ordering agencies pay is unconfirmed. The fact sheet specifies $688 per additional person, which the derived increment matches; the workbook's own extension for sizes 9-14 differs by up to $3. The FY2025 plan's $134 minimum benefit contradicts its own workbook, whose lowest heating cell is $15.Other documented gaps
Annual income divided by twelve approximates the month before application; SPM units approximate energy-purchasing households. WIA and work-study exclusions, some lump sums, royalties, jury duty, legal settlements, other insurance payments and refunds cannot be isolated with existing inputs. The FY2026 Medicare premium deduction from Social Security is not modeled. Existing housing-assistance indicators approximate federally assisted housing. Fuels without a published column, including an unspecified fuel, return zero.
Microsimulation: the datasets carry no heating inputs, so population totals are $0 until the default heating type changes. That change affects API partners and is tracked separately in #9754.
Heating only; crisis, cooling, weatherization and equipment assistance are excluded.
Sources:
Validation: 34 Kentucky YAML cases cover the income limit and large-household extension, an FY2025 household, the documentation and citizenship rules, subsidized payment cells, the coal adjustment, age exclusions and the benefit calculation. An independent script matched all 384 heating cells of each workbook against the point formula. CI runs the test suite.
axiom: TheAxiomFoundation/rulespec-us#1465 queued