Skip to content

Add Kansas LIEAP regular heating assistance - #9663

Open
hua7450 wants to merge 18 commits into
PolicyEngine:mainfrom
hua7450:ks-liheap
Open

hua7450 wants to merge 18 commits into
PolicyEngine:mainfrom
hua7450:ks-liheap

Conversation

@hua7450

@hua7450 hua7450 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

Add Kansas LIEAP regular heating assistance for the FY2025 and FY2026 seasons: household and income eligibility, heating-cost responsibility, countable income, and benefit tables by fuel, monthly income, vendor tier, dwelling type, and household size. The program registry marks coverage as partial.

The income limit applies a reformable 150% rate to the federal poverty guidelines through the shared fpg() helper's year_lag argument (#9731). Kansas keeps its one-year guideline lag, monthly rounding increment, and eight-person breakpoint in ks_liheap_income_limit. FY2025 matches the 2024 guidelines at cent precision; FY2026 matches whole-dollar rounding of the 2025 guidelines, with a separately rounded $688 increment above eight people. The calculation reproduces the published limits without a stored dollar table. The lag and the rounding are inferred from the published tables in KEESM 13362, which state the 150% rate and the additional-person amount but no rounding instruction.

Income calculations preserve the child-earnings exclusion and the season-specific interest and dividend exemptions. Employer disability benefits count as unearned income: KEESM 13361 lists the only exempt incomes, and employer disability benefits are not among them. Reusing income verified for SNAP or TANF does not waive the household income test.

Every KEESM citation points at the October 2024 and January 2026 editions rather than the undated current manual, so each season's values trace to the text in force at the time.

Household and immigration basis

KEESM 13320 defines the energy-purchasing household, with specific membership exceptions; the SPM unit is an approximation. KEESM 13330 requires at least one citizen or qualified alien and expressly includes refugees and asylees among qualified categories. For excluded members, it says they “will not be considered in determining household size, although the income from such members will be counted in determining total household income.” The model therefore uses qualified-member size but counts excluded members' otherwise countable income in full, without headcount proration. Ordinary source/age exclusions still apply. SNAP receipt can establish the alienage requirement, but its narrower status test is not substituted for Kansas's own rule. Documentary categories and special household arrangements remain partial. This language is present in both the October 2024 KEESM 13320–13330 and January 2026 edition.

Self-employment approximation

Count existing self_employment_income, sstb_self_employment_income, and farm_operations_income directly alongside wages. Each source is floored at zero, following KEESM 7122.1, which bars deducting a self-employment loss from other income. Earnings of children under 18 are excluded. There is no additional business-expense deduction, no new gross or expense input, no negative missing-data default, and no separate self-employment wrapper.

For example, an adult reporting $15,000 in wages and $4,000 in net business income has $19,000 in modeled countable earnings. The $4,000 is not reduced again by 25%.

This approximates the legal rule: KEESM 13360 uses gross receipts less a 25% standard expense deduction, with an elected allowable-actual-cost alternative. Existing tax-net inputs cannot reproduce every outcome under that rule. Callers with the exact Kansas amount can supply countable earned or household income directly.

PR #9665 (shared self-employment inputs) is closed, and nothing here depends on it. The broader shared self-employment design is deferred to #9682 and is outside the all-state LIHEAP effort.

Coverage and unresolved evidence

  • Modeled seasons are FY2025 and FY2026. The October 2024 parameter anchor identifies the first supported schedule, not the program's inception. Other seasons' rates, rounding, income rules, and benefits remain unverified.
  • Annual income and SPM units approximate application-month income and the energy household. Vendor tier E and house/modular/mobile are modeling defaults. Exceptional housing facts, irregular receipt timing, and some income exclusions require direct inputs. Contract or mortgage payments, legal settlements, and adoption support have no model input.
  • FY2026 payment schedule needs operational confirmation. The model uses the matrix's adjusted 80% column and the state plan's $130 minimum. The adjusted table ranges from $75 to $1,682, including 63 cells below $130; the plan reports a range of $130 to $4,301. No DCF instruction confirming the active column and reconciling these ranges was found. Matching the PDF cells verifies transcription, not the operational schedule.
  • Regular heating supplements remain in scope, but no FY2025 or FY2026 supplement schedule has been established or encoded. DCF says an end-of-season supplement may be issued if funds allow.
  • Cooling, crisis, weatherization, and heating-equipment assistance are excluded. Integration into a national heating-only aggregate is separate work.

Validation

  • 131 Kansas YAML cases in 14 files cover the published income limits and additional-person rule, parameter reforms, guideline-year selection, eligibility gates, income-source treatment including a mixed-status household and business losses, benefit lookups for every fuel table, the minimum-benefit floor, and one mixed-state vectorized simulation with a Missouri unit.
  • All 1,984 benefit-matrix cells match the FY2025 and FY2026 PDF tables.
  • Ruff format and check pass on the Kansas variables; every parameter and test file parses; no undated manual link remains in the Kansas tree.
  • Changelog fragment and partial Kansas registry entry. No partner fixture changes.

The first review-fix commit (f09ef1fdd7) re-templated 14 parameter descriptions, added FY2025 plan, KEESM 13361 and July 2025 edition citations, dropped a redundant select branch, recorded the household-size interpretation and the estimated $130 minimum, fixed test page cites and case labels, and added coverage for heat in rent with an unspecified fuel (paid from the Other table, $639), negative countable income (band 1), and strike benefits, general assistance and workers' compensation ($7,200). The second review-fix commit gives every multi-page reference a single page in the link with the pages named in the title, states the exact effective-date range in one comment, and counts two more sources: partnership and S corporation income as earned income (floored at zero like the other business sources, KEESM 6300 and 6313) and farm rental income as unearned income (KEESM 6313). CI validates the current head.

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; callers must send heating_type with the matching fuel bill.

axiom: TheAxiomFoundation/rulespec-us#1469 queued

🤖 Generated with Claude Code

hua7450 and others added 3 commits September 5, 2026 15:28
Adds ks_liheap: eligibility (150% of prior-year FPG on a citizenship-adjusted
household size, categorical TANF/SSI/SNAP route, energy vulnerability and
subsidized heat-in-rent exclusion), countable gross income, and the published
FY2025 (100%) and FY2026 (80% adjusted) benefit matrices by fuel, income band,
utility rate tier, dwelling type and household size group, with the $100/$130
minimum benefit.

Sources: KEESM 13000-13400; Kansas LIHEAP State Plans FFY2025/FFY2026;
LIHEAP Clearinghouse Kansas benefit matrices FY2025/FY2026.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A6rihVz3MtZQKCzzkCib35
@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (909176a) to head (67d231a).
⚠️ Report is 122 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #9663    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            4        16    +12     
  Lines           76       253   +177     
  Branches         2         1     -1     
==========================================
+ Hits            76       253   +177     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

hua7450 and others added 3 commits September 30, 2026 14:41
Fold the mixed-state vectorized case into ks_liheap.yaml, drop the
review_regressions file and the boundary and lookup cases that
duplicated coverage held elsewhere, and assert eligibility alongside
the zero benefit where a case exercises an ineligibility gate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replace every undated keesm/current link in the Kansas LIEAP variables
and parameters with the October 2024 and January 2026 editions, name
the edition in parameter reference titles, and point the test-file
headers at the same editions with combined page anchors.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hua7450 and others added 3 commits September 30, 2026 20:45
Brings in the shared federal poverty guideline helper with year_lag (PolicyEngine#9731).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… limit

Replace the Kansas-only tools/liheap.py helper with fpg(..., year_lag=...) from
the shared helper merged in PolicyEngine#9731. The 150% rate, separately rounded monthly
increment and eight-person breakpoint stay in ks_liheap_income_limit, which
reproduces the previous results for FY2025-FY2027 at every household size.
Also records the verified-years note on ks_liheap and the KEESM 13330
household-size comment.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…come

Replace the deprecated dividend_income alias in the formula and send qualified_dividend_income in the tests that used it. Expected values are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review October 1, 2026 19:11
@hua7450
hua7450 requested a review from DTrim99 October 1, 2026 19:11
@DTrim99

DTrim99 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Program Review

PR #9663: Add Kansas LIEAP regular heating assistance (hua7450, branch ks-liheap), reviewed at head e82afc5dd2: 48 files, covering 17 parameters, 15 variables, 14 test files (127 cases), a changelog fragment and programs.yaml.

The review checked eligibility, countable income, the 150% FPG income limit, the FY2025 and FY2026 benefit matrices and the minimum benefit against KEESM and the two LIHEAP state plans. It also checked code patterns, references and test coverage. All 2,021 audited source values match. The open items are a microsimulation data gap, one missing flooring step, and documentation and test hygiene.

Source Documents

Document Link Contents used
FY2026 benefit matrix (4 pp, offset 0) KS_BenefitMatrix_2026.pdf p1 natural gas, p2 electricity, p3 propane, p4 other; the PR encodes the "Benefit Adjustment @ 80%" table
FY2025 benefit matrix (3 pp, offset 0) KS_BenefitMatrix_2025.pdf One "Ben Adj 100.0%" table: p1 natural gas plus electricity band 1 A-I; p2 electricity band 1 J through band 4 plus propane bands 1-2; p3 propane bands 3-4 plus other
FY2026 LIHEAP state plan (48 pp, offset 0) KS_Plan_2026.pdf p5 §1.8 gross income; p6-7 §1.9 income checklist; p8 §2.1-2.3; p9 §2.5-2.6; p13 §4.12
FY2025 LIHEAP state plan (48 pp, offset 0) KS_Plan_2025.pdf p6 child earnings; p8 §2.1-2.3; p9 §2.5-2.6
KEESM, January 2026 edition 13000 · 13200 · 13300 · 13360 · 13400 · 7122 13100 summary, 13230, 13310-13350, 13360-13362, 13410-13421, 7122.1
KEESM, October 2024 edition 13000 · 13200 · 13300 · 13360 · 13400 · 7122 FY2025 rules
KEESM, July 2025 edition 13360 First edition carrying the FY2026 rules (Suggestion 7)
LIHEAP Clearinghouse Kansas profile Kansas.htm Application window; heating min/max; crisis max
DCF Energy Assistance page EnergyAssistance.aspx End-of-season supplement; next-season income table

Branch Status

The branch is 13 commits ahead of main and 10 commits behind it (merge base d254a343d4). None of the 52 files changed on main since the merge base touch Kansas, programs.yaml, or the FPG and heating helpers this PR uses. The fpg(..., year_lag=) helper from #9731 is already in the merge base. The staleness did not affect the findings. GitHub reports the PR as mergeable.

Path abbreviations used below:

  • P/ = policyengine_us/parameters/gov/states/ks/dcf/liheap/
  • V/ = policyengine_us/variables/gov/states/ks/dcf/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ks/dcf/liheap/

Critical (Must Fix)

None.

  • All 2,021 audited values match the source PDFs and KEESM.
  • No values are hard-coded.
  • Every parameter has a reference that corroborates its value.
  • No section citation is wrong.
  • Every formula variable is tested.
  • CI passes.

Should Address

1. ks_liheap is $0 for every microdata household: heating inputs are absent from the data and there is no fallback

  • Where: V/eligibility/ks_liheap_energy_vulnerable.py:20 (return pays_for_heat | (heat_in_rent & ~subsidized)).
  • Code path:
    • ks_liheap is defined_for = "ks_liheap_eligible", which requires ks_liheap_energy_vulnerable.
    • Vulnerability needs has_heating_expense or heat_expense_included_in_rent.
    • has_heating_expense is (heating_expense > 0) & ~heat_in_rent.
    • heating_expense returns 0 when heating_type is UNSPECIFIED. UNSPECIFIED is the default.
    • heat_expense_included_in_rent defaults to False.
  • Data: the populace lock file (e94690b1, 2026-09-21, engine_abi.lock.json) lists heating_type, gas_expense, bottled_gas_expense, heat_expense_included_in_rent and heating_cooling_expense as declared_absent_engine_input.
  • Bias: downward, to zero. Every Kansas record fails energy vulnerability, so the microsimulation program total is $0 in every year, a 100% understatement.
  • The rule itself matches the source. KEESM 13340: "Applicants meet the energy vulnerability requirement by demonstrating responsibility for purchasing the primary heating energy at their current residence." See also FY2026 plan §2.3.
  • How existing programs handle missing data:
    • il_liheap_eligible returns ~heat_in_rent | rent_threshold_met.
    • MA LIHEAP gates only on subsidized housing.
    • dc_liheap_payment and has_heating_cooling_expense keep a legacy adapter for UNSPECIFIED fuel.
    • The documentation of heating_expense says UNSPECIFIED households "are handled by each program's deprecated legacy adapter". Kansas has no such path.
  • Mitigating factor: no state LIHEAP variable feeds a net-income aggregate. Only direct ks_liheap outputs and program totals are affected.

2. Rental losses offset wages and other income although Kansas tests gross income (missing flooring step)

  • Where: P/income/sources/unearned.yaml:12 (- rental_income). V/income/ks_liheap_countable_unearned_income.py:18 sums it with its sign through adds, and ks_liheap_countable_income adds it to earned income.
  • Sources:
    • FY2026 plan §1.8 checks "Gross Income". The checkbox state was verified in the rendered page.
    • KEESM 13360: "The combined gross income of all persons living at the residence address shall not exceed 150% of the federal poverty level."
    • KEESM 7122.1: "A loss from self-employment cannot be deducted from other income."
  • Inconsistency within the PR: V/income/ks_liheap_countable_earned_income.py:32-33 already floors self-employment, SSTB and farm income at 0 per source. Rental income is not floored.
  • Example, FY2026, household of one:
    • Inputs: wages of $24,000, above the $23,472 limit, and rental_income of −$1,000.
    • The model counts $23,000, so the household is eligible.
    • Under gross-income treatment the count is $24,000, so the household is ineligible.
  • The loss can also move an eligible household into a lower income band.

3. Parameter descriptions depart from the description template

  • The template is Kansas [limits/provides/sets/excludes/deducts/uses] ... under the Low Income Energy Assistance Program.
  • Disallowed verb, at line 1 of each file:
    • P/eligibility/max_table_size.yaml ("publishes")
    • P/eligibility/rounding_increment.yaml ("expresses")
    • P/income/earned_income_min_age.yaml ("counts")
    • P/income/interest_income_min_age.yaml ("counts")
    • P/income/sources/unearned.yaml ("counts")
    • P/payment/household_size_group.yaml ("assigns")
    • P/payment/income_band.yaml ("assigns")
    • P/payment/min_benefit.yaml ("pays")
  • Closing program clause missing or not last, at line 1 of each file:
    • P/eligibility/fpg_year_lag.yaml
    • P/eligibility/max_table_size.yaml and P/eligibility/rounding_increment.yaml name the program mid-sentence.
    • P/income/regular_interest_dividend_exemption.yaml has a trailing "; above it the full amount counts".
    • P/payment/matrix/{natural_gas,electricity,propane,other}.yaml: the dimension list follows the program name. Move it before the program clause.
  • Cosmetic only. No value is affected.

4. Reference page anchors that miss part of the cited content

  • V/income/ks_liheap_countable_earned_income.py:25: KS_Plan_2026.pdf#page=6,7 is not a valid PDF fragment, so viewers open p6 and ignore ",7". Split it into separate #page=6 and #page=7 entries.
  • P/income/sources/earned.yaml:25-26: the title is "Section 1.8-1.9" but the link is #page=6. §1.9 is on p6; §1.8 is on p5.
  • P/payment/matrix/electricity.yaml:1194: the FY2025 link #page=1 holds only band 1, tiers A-I. Band 1 tier J and bands 2-4 are on p2.
  • P/payment/matrix/propane.yaml:1194: the FY2025 link #page=2 holds bands 1-2. Bands 3-4 are on p3.
  • The values are correct in every case. Only the anchors need changing.

5. Two edge paths have no test

  • Negative-income floor: max_(income, 0) at V/payment/ks_liheap_income_band.py:20 is never reached by a test.
    • Earned sources are floored per source, so only negative rental_income (Should 2) or a direct negative input reaches this line.
    • A case with negative ks_liheap_countable_income would pin band 1.
  • Benefit amount when heat is in rent and the fuel is unknown: this path resolves to the OTHER table, but T/eligibility/ks_liheap_energy_vulnerable.yaml:25-34 checks eligibility only.
    • Adding ks_liheap_fuel_category: OTHER and the ks_liheap amount would pin the behavior.
    • Expected: band 1, House, size 1-4 = $639 in FY2026 (matrix p4).

6. Test comments, case labels and number formatting

  • Wrong FY2025 matrix page numbers in test comments. The expected values are correct in all four.
    • T/integration.yaml:478, propane 1,700+: cites p.2; the cell is on p.3.
    • T/payment/ks_liheap_matrix_amount.yaml:574, electricity 0-899, tier E, House 5+ = 1,584: cites p.2; the cell is on p.1.
    • T/payment/ks_liheap_matrix_amount.yaml:596, propane 900-1,199, tier J, Duplex = 868: cites p.3; the cell is on p.2.
    • T/payment/ks_liheap_matrix_amount.yaml:618, Other 1,700+, House = 594: cites p.4, which does not exist in the FY2025 PDF; the cell is on p.3.
  • Misleading arithmetic comment: T/integration.yaml:298-299 reads "941 x 0.8 = 752.8; the published 80% table cell is 709", which implies the 80% table is off. The 80% table is 80% of the FY2026 unadjusted matrix, not of FY2025: 886 × 0.8 = 708.8, rounded to 709 (FY2026 matrix p1).
  • Case labels:
    • T/eligibility/ks_liheap_eligible.yaml:12: the first case is labelled "Case 2", and no Case 1 exists.
    • T/income/ks_liheap_countable_earned_income.yaml:11-29 jumps from Case 1 to Case 4.
    • Several other test files also have numbering gaps.
  • Number formatting: T/eligibility/ks_liheap_energy_vulnerable.yaml:10,21,30 (and later cases) uses ks_liheap_countable_income: 10000. The rest of the suite would write 10_000.

7. The axiom: line does not meet the parity requirement

  • The PR description reads axiom: n/a: This task is restricted to the PolicyEngine workspace; cross-organization access is excluded by the user's workspace instructions.
  • CONTRIBUTING.md#axiom-parity lists a new program as in scope.
  • The line can be satisfied by either:
    • a queued rulespec-us issue labelled pe-parity, under us-ks/, with the KEESM citation and verbatim text, the required outputs, and companion tests drawn from the same external sources as the YAML tests (for example, the matrix cells); or
    • an encoded / encoded-correct reference.
  • A maintainer with rulespec-us access can file the issue. No code change is involved.

Suggestions

1. The FY2026 $130 minimum rests on a plan field labelled "estimated"

  • Where: P/payment/min_benefit.yaml:14, applied at V/ks_liheap.py:21.
  • The value is transcribed correctly (FY2026 plan §2.6, confirmed at 600 dpi).
  • Evidence that the plan figures are estimates rather than a floor:
    • §2.6 asks to "Describe estimated benefit levels".
    • Neither $130 nor $4,301 appears in the FY2026 matrix, whose 80% table runs from $75 to $1,682.
    • $4,301 is the Winter Crisis maximum in §4.12.
    • In FY2025, the plan's $100 and $2,232 (FY2025 plan p9) exactly equal the matrix extremes.
  • Bias if DCF does not apply the floor: upward, raising 63 of 992 FY2026 cells (6.4%) by $1 to $55.
  • The PR description already flags this. Recording the assumption in the programs.yaml entry notes (policyengine_us/programs.yaml:826-833) would carry it beyond the YAML comment.

2. The SPM unit understates income where several units share an address

  • Where: every ks_liheap_* variable uses entity = SPMUnit (e.g. V/ks_liheap_household_size.py).
  • Sources:
    • KEESM 13100 counts "the income from all persons" living together.
    • KEESM 13360 refers to the "combined gross income of all persons living at the residence address".
  • Bias: upward on eligibility and benefit for addresses with more than one SPM unit. The size was not quantified.
  • The PR already discloses this, and it matches the IL, DC and MA LIHEAP pattern.

3. Self-employment income uses net profit instead of gross receipts minus the larger of 25% or actual costs

  • Where: P/income/sources/earned.yaml:8-10.
  • Sources: KEESM 13360 and KEESM 7122.1.
  • Bias: countable income is min(net, 0.75 × gross), which never exceeds net. Net profit therefore overstates countable income when actual costs are below 25% of gross receipts, and is exact otherwise. The bias is downward on eligibility and benefit for that subgroup; the size was not quantified.
  • Context: PE has no gross-receipts input. The choice is disclosed, and KS TANF and KS CCAP use the same approach.

4. Household size counts every §1641 qualified status

  • Where: V/ks_liheap_household_size.py:18 (adds = ["is_citizen_or_legal_immigrant"]).
  • Source: KEESM 13330: "Household members who are not citizens or lawful permanent residents will not be considered in determining household size."
  • Assessment: the same section uses "lawful permanent residency" loosely for all qualified aliens, so the PR's reading is plausible. Documenting it as an interpretation in the variable would record that.
  • Impact: limited to households with a non-LPR qualified member. Their size, and so their limit, is one person higher, and they can move into the 5+ column (upward).

5. Unmapped countable sources

  • Missing:
    • partnership_s_corp_income: active pass-through earnings, which fall under KEESM 13360's self-employment paragraph.
    • miscellaneous_income: KEESM counts "most other income", and FY2026 plan §1.9 checks jury duty and legal settlements.
    • farm_rent_income.
  • Bias: each omission understates countable income, so the bias is upward on eligibility and benefit.
  • Correctly handled:
    • military_retirement_pay is a subset of taxable_pension_income, so leaving it out avoids a double count.
    • IRA distributions are exempt under KEESM 13361 and are correctly excluded.

6. Defaults when household inputs are omitted (these also apply in microsimulation once Should 1 is resolved)

  • V/ks_liheap_dwelling_type.py:15 defaults to House/Modular/Mobile, the highest column on every page. The bias is upward for apartment and duplex households.
  • V/ks_liheap_utility_rate_tier.py:21 defaults to tier E, which sits near the median (E = F for natural gas). The direction can't be determined without the provider distribution.
  • When heat is in rent and the fuel is unknown, V/ks_liheap_fuel_category.py maps to OTHER.
    • The Other table equals electricity tier A in FY2025 and is within $1 of it in FY2026.
    • The bias is downward relative to mid-tier gas or electric cells.

7. Comment on the effective date of the FY2026 values

  • Where: the 2025-10-01 entries under P/.
  • Source history:
    • The July 2025 KEESM edition is the first to carry the FY2026 rules: the $1,956-$6,769 table, the $688 increment and the "$30 in a quarter" interest rule.
    • The April 2025 edition still had $1,882.50 and "$50 per month".
  • Why 2025-10-01 works:
    • All variables are YEAR-period and parameters resolve at January 1, so any date from 2025-01-02 through 2026-01-01 gives identical outputs.
    • 2025-10-01 matches the federal program year and the FY2026 matrix date.
  • A comment naming the July 2025 edition would record this.

8. Citation completeness

  • P/income/earned_income_min_age.yaml:10-15: also cite KEESM 13361 directly ("Earned income of child household members under age 18;").

  • P/eligibility/fpg_rate.yaml:9-13: add the plans' §2.1 "150.00%" from FY2026 p8 and FY2025 p8.

  • Several parameters take effect 2024-10-01 but cite only FY2026 plan sections:

    • P/income/earned_income_min_age.yaml:10-11
    • P/income/sources/earned.yaml:25-26
    • P/income/sources/unearned.yaml:33-34
    • the §2.5 entries in the four matrix files

    The FY2025 plan has the same items: child earnings on p6 and §2.5 on p9. The October 2024 KEESM citations already support these values, so this is for completeness only.

  • P/income/interest_income_min_age.yaml:9-13 and P/income/regular_interest_dividend_exemption.yaml:10-14 are titled generically ("KEESM 13360-13362"). Naming 13361 and quoting the operative text would let a reader confirm each value directly.

9. Redundant fourth select branch

  • Where: V/ks_liheap_fuel_category.py:34-43 passes both an other = ~(natural_gas | electricity | propane) condition and default=KSLIEAPFuelCategory.OTHER.
  • Either one is enough. Dropping the condition does not change behavior.

10. Further test coverage (optional)

  • strike_benefits, general_assistance and workers_compensation (P/income/sources/unearned.yaml:5,10,16) appear in no test. One summation case in T/income/ks_liheap_countable_income.yaml would guard the list.
  • The FY2025 $100 floor never binds, because the lowest FY2025 cell is exactly $100 (propane 1,700+, tier A, trailer, p3). Only a parameter-override test could make it bind.

11. programs.yaml conflicts with the sibling LIHEAP PRs

12. Naming across the batch

PDF Audit Summary

Item Confirmed Mismatch Notes
FY2025 matrix cells (gas, electricity, propane: 4 bands × 10 tiers × 8 columns; Other: 4 × 8) 992 / 992 0 "Ben Adj 100.0%" table, dated 2024-10-01
FY2026 matrix cells ("Benefit Adjustment @ 80%") 992 / 992 0 Dated 2025-10-01; band-1 trailer offset in the text extraction resolved by coordinates and the rendered page
Income limits (8 sizes + increment, two editions) 18 / 18 0 Reproduced by formula from the FPG, not stored; includes the printed "$3331"
Minimum benefit ($100 FY2025, $130 FY2026) 2 / 2 0 Values correct; interpretation in Suggestion 1
Effective dates (2024-10-01, 2025-10-01) 2 / 2 0 Suggestion 7
Scalars (rate, lag, table size, rounding ×2, earned min age, interest min age, interest exemption ×2, band thresholds ×4, size thresholds ×2) 15 / 15 0
Total 2,021 / 2,021 0
  • Method:
    • Every matrix cell was extracted by coordinates.
    • Spot checks were made against 300-dpi renders of FY2026 p1-p3 and FY2025 p1-p3.
    • There were no mismatches, so no 600-dpi mismatch re-check was required.
    • The cells behind Suggestion 1 were re-rendered at 600 dpi anyway: FY2026 propane 1,700+ tier A = 301/226/150/75/344/258/172/86, and plan §2.6 = $130 / $4,301.
  • Matrix ranges:
    • FY2025: $100-$2,232.
    • FY2026, 80% table: $75-$1,682, with 63 cells below $130.
    • FY2026, unadjusted table: $94-$2,102.
    • In 217 FY2026 cells the 80% figure differs by more than 0.5 from 0.8 × the printed unadjusted integer, which suggests it was computed from unrounded amounts. The PR correctly encodes the printed 80% values.
  • Not modeled:
    • Plan §2.6 maximums: they never bind.
    • The 25% SE standard deduction (Suggestion 3).
    • FY2026 irregular interest under $30 per quarter: direct input only.
    • KEESM 13350 auto-issuance.
    • The end-of-season supplement.
    • Plan §1.4 categorical income eligibility. It is omitted consistently with KEESM 13360's "Deemed Income Eligible" text, and the PR tests this.

Validation Summary

Check Result Notes
Regulatory accuracy Should address Eligibility, countable income, the income limit and benefit rules match KEESM 13300-13362 and both state plans. Open items: Should 1 (microdata gap, $0 total) and Should 2 (rental loss not floored)
References Should address Every parameter has a corroborating reference, and no section citation is wrong. Four anchors miss part of the cited content (Should 4); completeness items in Suggestion 8
Code patterns Should address No hard-coded values, no reinvented helpers, vectorized, defined_for on every formula, no orphan parameters. 14 descriptions depart from the template (Should 3)
Formatting Should address Changelog fragment valid; ruff passes in CI. Test-number formatting and labels in Should 6
Tests Should address 127 cases, and every formula variable is tested. Limit, band, size-group and floor boundaries are covered. Two edge paths are untested (Should 5)
PDF values Pass 2,021 / 2,021 confirmed, 0 mismatches
CI Pass 37 / 37 checks pass at head e82afc5dd2
PR requirements Should address axiom: line (Should 7)

Review Severity: REQUEST_CHANGES

The review found 0 critical items, 7 should-address items and 12 suggestions. No value mismatches were found. Changes are requested for the should-address items.

Next Steps

  1. Floor rental_income at 0 in the countable-income path, matching the per-source floors on the earned side (Should 2). Add the negative-income band test (Should 5).
  2. Decide how ks_liheap treats microdata records that have no heating inputs (Should 1): either add a fallback, or record the $0 microsimulation result as a known limitation in the programs.yaml entry. The current bias is downward, to zero.
  3. Make the mechanical edits: parameter descriptions (Should 3), reference anchors (Should 4), and test comments, labels and number formatting (Should 6).
  4. Add the heat-in-rent, unknown-fuel benefit test (Should 5).
  5. Update the axiom: line with a queued pe-parity rulespec-us issue or an encoded reference (Should 7).
  6. Rebase onto main before merge, and coordinate the programs.yaml merge order with Add Nebraska LIHEAP regular heating assistance #9723, Add Kentucky LIHEAP regular heating assistance #9725, Add North Carolina LIEAP regular heating assistance #9726, Add Mississippi LIHEAP regular heating assistance #9727 and Add Indiana EAP regular heating assistance #9728 (Suggestion 11).
  7. A separate follow-up outside this PR: KS CCAP countable_income/unearned.yaml counts military_retirement_pay alongside pension_income, while IRS gross income treats it as part of taxable_pension_income. That may be a double count.

@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 per the program review above (#9663 (comment)): 0 critical, 7 should-address items, 12 suggestions. All 2,021 audited parameter values match the FY2025/FY2026 sources. Run /fix-pr 9663 or address the should-address list.

@hua7450

hua7450 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review, @DTrim99. Replies on four of the should-address items:

Should 1, heating inputs absent from the microdata. We will change the default heating_type to electricity in a separate PR, so this is not handled here.

Should 2, rental losses. rental_income is assumed to be zero or positive. That is the assumption in pretty much every other program that counts it, so I am leaving it as is.

Should 4, combined page anchors. I don't want redundant links to the same document within one reference list, so each source stays a single entry. A note in the title naming the pages is sufficient.

Should 7, axiom: line. Axiom does not have this program yet. You're right that it should be added.

hua7450 and others added 2 commits October 1, 2026 17:06
…bels and edge cases

- Re-template 14 parameter descriptions and name the pages in combined reference anchors
- Cite the FY2025 plan, KEESM 13361 and the July 2025 edition; fix the FY2025 matrix and plan anchors that missed pages
- Drop the redundant select branch in ks_liheap_fuel_category; note the household-size interpretation and the estimated $130 minimum
- Fix four test page cites and the 80 percent comment, renumber case labels, format numbers
- Add cases for heat in rent with an unspecified fuel, negative countable income and strike, general assistance and workers' compensation income

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hua7450
hua7450 requested a review from DTrim99 October 1, 2026 21:07
@hua7450

hua7450 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed f09ef1f (plus a merge of main) with should-address 3, 5 and 6, the three anchors in 4 that missed pages, and suggestions 1, 4, 7, 8, 9 and 10. Items 1, 2, the anchor split and 7 are answered above; suggestions 5, 11 and 12 are left for a separate decision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Program Review — Round 2

PR #9663: Add Kansas LIEAP regular heating assistance (hua7450, branch ks-liheap). This round re-reviews head f0e9c030ac. The delta since round 1 (e82afc5dd2) contains three commits:

  • f09ef1fdd7: review fixes.
  • 2a43bf57a6: merge of main.
  • f0e9c030ac: a shorter programs.yaml note.

Round 1 (posted 2026-10-01) found 0 critical items, 7 should-address items and 12 suggestions.

Results of the delta:

  • Should 3, 5 and 6 are fixed.
  • Should 2 is resolved by the author's rationale, which matches repo practice.
  • Should 1, 4 and 7 remain. All three are documentation or tracking items, and none needs a formula change.
  • No parameter value and no existing test expectation changed.
  • The new and extended expectations match the source PDFs and pass CI.

Path abbreviations:

  • P/ = policyengine_us/parameters/gov/states/ks/dcf/liheap/
  • V/ = policyengine_us/variables/gov/states/ks/dcf/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ks/dcf/liheap/

Line numbers refer to f0e9c030ac. Outside programs.yaml, files are identical to 2a43bf57a6.

Round-1 Should-Address Items: Status

# Round-1 item Verdict Evidence
1 ks_liheap is $0 in microsimulation because heating inputs are absent and there is no fallback NOT FIXED Deferring the fix to a separate PR is reasonable: the planned change to the shared heating_type default also affects the nine sibling LIHEAP PRs. However, the plan appears only in the author's comment of 2026-10-01. No issue or PR tracks it, the PR body does not mention it, and the shortened programs.yaml note does not record it.
2 Rental losses offset other income, although Kansas tests gross income ACCEPTED-AS-RATIONALE rental_income has no floor in its definition. 88 parameter source lists include it. The federal TANF, HUD and SSI unearned aggregates (tanf_gross_unearned_income, hud_unearned_income, ssi_unearned_income) and TX CEAP (tx_ceap_countable_income_person) all sum it with its sign through adds/add(). The only program consumer found that floors it is ok_ccs_countable_earned_income.py:19. The PR is consistent with that predominant practice.
3 Parameter descriptions depart from the template FIXED All 16 parameter files now use an approved verb and end with "under the Low Income Energy Assistance Program." The trailing clause of P/income/regular_interest_dividend_exemption.yaml moved to a comment.
4 Reference anchors miss part of the cited content (#page=6,7 is not a valid fragment) NOT FIXED The titles now name page ranges, and those ranges are accurate. But the hrefs still use comma ranges (#page=5,6, #page=8,9, #page=1,2, #page=2,3, #page=6,7), which are not valid single-page fragments. Round 1 had one such href; there are now 14 (see Should Address 2).
5 Two edge paths have no test FIXED T/payment/ks_liheap_income_band.yaml:203 pins negative income to band 1. T/eligibility/ks_liheap_energy_vulnerable.yaml:26-41 pins heat in rent with an unspecified fuel to the OTHER table at $639.
6 Test comments, case labels and number formatting FIXED The four FY2025 page cites and the 80% comment are corrected (verified below). Case labels are contiguous in all 14 files. No unformatted number of four or more digits remains in test inputs or outputs.
7 axiom: line does not meet the parity requirement NOT FIXED The PR body still reads axiom: n/a: rulespec-us does not have this program yet; it should be added as a follow-up. That situation is the one the queued status exists for.

Critical (Must Fix)

None.

  • The delta changes no parameter value and no existing test expectation. It changes only descriptions, references, comments, labels and number formatting.
  • The new expectations were checked against the PDFs (see Delta Verification).

Should Address (remaining)

1. Microsimulation deferral is not tracked (round-1 Should 1)

2. Comma page ranges in reference hrefs (round-1 Should 4)

  • Status: the single-entry approach with page ranges in the title is acceptable, and every title range was verified as accurate. The hrefs themselves are the problem.
  • Viewer behavior:
    • Chromium's viewer reads the first number: parseInt(urlParams.get('page'), 10) in open_pdf_params_parser.ts.
    • Firefox's pdf.js evaluates pageNumber = params.get("page") | 0 || 1 in web/pdf_link_service.js. "8,9" | 0 is 0, so the link opens page 1, which is the cover of the 48-page plans.
  • 14 hrefs (13 added in f09ef1fdd7, 1 carried over from round 1):
    • P/income/sources/earned.yaml:26 and :28: #page=5,6
    • P/income/sources/unearned.yaml:36: #page=5,6
    • P/payment/matrix/electricity.yaml:1194: #page=1,2
    • P/payment/matrix/electricity.yaml:1196 and :1198: #page=8,9
    • P/payment/matrix/natural_gas.yaml:1196 and :1198: #page=8,9
    • P/payment/matrix/other.yaml:146 and :148: #page=8,9
    • P/payment/matrix/propane.yaml:1194: #page=2,3
    • P/payment/matrix/propane.yaml:1196 and :1198: #page=8,9
    • V/income/ks_liheap_countable_earned_income.py:25: #page=6,7. This is a variable reference tuple, so it has no title to name the range.
  • Fix: use one integer per href, for example the first page of the range named in the title (#page=5, #page=8, #page=1, #page=2, #page=6). This keeps one entry per source, as the author prefers.

3. axiom: line (round-1 Should 7)

  • Current line: axiom: n/a: rulespec-us does not have this program yet; it should be added as a follow-up.
  • Requirement: CONTRIBUTING.md line 53 says the parity requirement covers "a new program". Line 57 says to label queued issues pe-parity. The author's reply agrees that the program should be added.
  • rulespec-us state: it has a us-ks/policies/ tree but no LIEAP module. No rulespec-us issue matches "Kansas LIEAP".
  • Fix: file a pe-parity issue in TheAxiomFoundation/rulespec-us and change the line to axiom: TheAxiomFoundation/rulespec-us#NNNN queued. The issue should name the us-ks/ module path, quote the KEESM 13360-13362 text, list the required outputs, and use the matrix cells and published limits as companion tests. A maintainer with rulespec-us access can file it if the author lacks access.

Suggestions

  1. Effective-date comment wording, P/eligibility/rounding_increment.yaml:6-9.
    • The comment says "any date in the 2025 season gives the same result". The exact range is 2025-01-02 through 2026-01-01, because YEAR periods resolve at January 1.
    • A 2025-01-01 date would move period 2025 onto the FY2026 values, and a date after 2026-01-01 would leave period 2026 on the FY2025 values.
    • The comment could state the range.
  2. Shortened registry note, policyengine_us/programs.yaml:833 (f0e9c030ac).
    • It keeps the $130 caveat, which is round-1 Suggestion 1.
    • It drops the earlier disclosures: tier E and house/mobile defaults, the SPM-unit approximation, and net self-employment income without the 25% deduction.
    • These remain in the PR body and code comments, but the registry note is what the model coverage page shows. The TX CEAP entry keeps a "Not modeled" list. A short version of that list, plus the microsimulation status (Should Address 1), would carry these forward.
  3. Test header comments use comma ranges. These are comments only, not links:
    • T/income/ks_liheap_countable_earned_income.yaml:2 (#page=6,7). This line also cites "1.8-1.9", but FY2026 §1.8 is on p5.
    • T/income/ks_liheap_countable_income.yaml:2 (#page=5,6)
    • T/integration.yaml:4 (#page=5,6)
    • T/ks_liheap_fuel_category.yaml:2 (#page=8,9)
    • T/payment/ks_liheap_matrix_amount.yaml:2 (#page=8,9)
  4. PR body counts.
    • "127 Kansas YAML cases in 14 files" is now 129 in 14 files.
    • "adds three cases": two cases are new, and the third added outputs to the existing energy-vulnerability Case 3.
    • "Those expected values are hand-derived and were not run locally" is superseded: the states shards passed at 2a43bf57a6.
  5. Carried forward: unmapped countable sources (round-1 Suggestion 5, left by the author for a separate decision).
    • partnership_s_corp_income, miscellaneous_income and farm_rent_income are still absent from P/income/sources/.
    • Bias: upward on eligibility and benefit.
  6. Carried forward: programs.yaml merge order (round-1 Suggestion 11).
  7. Carried forward: naming across the batch (round-1 Suggestion 12). ks_liheap here versus nc_lieap in Add North Carolina LIEAP regular heating assistance #9726 is unchanged.

Round-1 Suggestions 2, 3 and 6 were disclosures that needed no change.

Verification of Claimed Suggestion Fixes

Round-1 suggestion Verdict Evidence
1. $130 minimum rests on "estimated" plan levels FIXED policyengine_us/programs.yaml:833 records it, in the shortened f0e9c030ac wording
4. Household size counts every qualified status FIXED V/ks_liheap_household_size.py:18-20 records it as an interpretation. The quoted wording matches KEESM 13330 ("citizens or lawful permanent residents")
7. FY2026 effective-date comment FIXED P/eligibility/rounding_increment.yaml:6-9, plus July 2025 references in rounding_increment.yaml and regular_interest_dividend_exemption.yaml. The July 2025 KEESM edition shows $1,956 to $6,769, +$688 and "less than $30 in a quarter". Wording note in Suggestion 1
8. Citation completeness FIXED See the list below the table
9. Redundant select branch FIXED V/ks_liheap_fuel_category.py:34-42. Behavior is unchanged, because default=OTHER covers the dropped condition
10. Further test coverage FIXED T/income/ks_liheap_countable_income.yaml:197-220 covers strike benefits, general assistance and workers' compensation. The FY2025 $100-floor note was informational only

Details for Suggestion 8:

  • KEESM 13361 is now cited for the child-earnings and child-interest exclusions in both editions, with the operative text quoted.
  • The FY2025 plan is cited in earned_income_min_age (p6), the earned and unearned source lists, fpg_rate (§2.1 on p8) and the four matrix files (§2.5).
  • fpg_rate adds §2.1 "150.00%" from both plans, on p8.
  • The new FY2025 citations were checked against the source pages:
    • Gross Income is checked in §1.8 on p5 (PNG).
    • "Earned income of a child under the age of 18" is unchecked in §1.9 on p6 (PNG).
    • §2.1 reads 150.00% on p8.
  • Several of the new hrefs use comma ranges (Should Address 2).

Delta Verification

  • Parameter values: none changed in any of the 16 parameter files.
  • Existing test expectations: none changed. The edits are to labels, comments and number formatting (10000 → 10_000).
  • New or extended expectations:
    • T/eligibility/ks_liheap_energy_vulnerable.yaml:38-41:
      • Monthly income is 10,000 / 12 = 833, which is band 1.
      • The FY2026 matrix p4, 80% table, House, size 1-4 cell is 639 (confirmed on the 300-dpi render).
      • max(639, 130) = 639.
    • T/payment/ks_liheap_income_band.yaml:203-211: max(−5,000, 0) / 12 = 0, which is band 1. This matches V/payment/ks_liheap_income_band.py:20.
    • T/income/ks_liheap_countable_income.yaml:197-220:
      • 1,200 + 2,400 + 3,600 = 7,200.
      • All three sources are in P/income/sources/unearned.yaml.
      • Strike Pay and General Assistance are checked in plan §1.9 (FY2025 p6 render).
  • Corrected comments:
    • T/integration.yaml:298-299 now reads "886 × 0.8 = 708.8 → 709". On the FY2026 p1 render, natural gas 0-899, tier E, House, size 1-4 is 886 in the unadjusted table and 709 in the 80% table.
    • The FY2025 cells and pages are confirmed on the renders and in the text:
      • propane 1,700+, tier A, trailer = 100 on p3 (T/integration.yaml:478)
      • electricity 0-899, tier E, House, 5+ = 1,584 on p1
      • propane 900-1,199, tier J, Duplex = 868 on p2
      • Other 1,700+, House = 594 on p3
  • Page ranges in the new titles, located by the "Page N of 48" footers and the renders:
    • FY2026 plan: §1.8 on p5; §1.9 on p6-7; §2.1 on p8; §2.5 on p8-9.
    • FY2025 plan: §1.8 on p5; §1.9 on p5-6; §2.1 on p8; §2.5 on p8-9.
    • FY2025 matrix: electricity on p1-2; propane on p2-3.
    • Every title range is accurate.
  • Test inventory: 129 cases in 14 files. Case labels are contiguous in every file.

Branch Status

CI Status

  • 2a43bf57a6: 34 checks passed and 3 were cancelled. The cancelled checks are Contrib states-shard-2, Contrib states-shard-3 and Rest (Python + variables). They were superseded at 21:44 UTC when f0e9c030ac was pushed.
    • All four Baseline states shards passed. These shards run the Kansas YAML tests, so the three new expectations compute as written.
    • Quick Feedback and Microsimulation also passed.
  • f0e9c030ac (a programs.yaml note change only): 35 checks are registered. Lint, the changelog fragment check, the committed registry lock check and the release lock guard tests passed. The other 31 checks were queued or in progress at 21:53 UTC.

Review Severity: REQUEST_CHANGES

The review found 0 critical items, 3 remaining should-address items and 7 suggestions.

  • Should-address items: round-1 Should 1, 4 and 7 remain. Should 2 counts as resolved (ACCEPTED-AS-RATIONALE), and Should 3, 5 and 6 are fixed.
  • Fixes needed: each remaining item is a documentation or tracking fix:
    1. Replace each #page=X,Y with a single #page=X (14 hrefs).
    2. Link a tracking issue for the heating_type default, or note the $0 microsimulation result in the registry entry.
    3. File the pe-parity rulespec-us issue and change the axiom: line to queued.

@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.

Round 2 (#9663 (comment)): 4 of 7 round-1 should items resolved (3, 5, 6 fixed; rental-income floor accepted as consistent with repo practice). Remaining: track the heating_type default follow-up (issue or PR-body note), use valid single-page #page=X anchors (14 links use #page=X,Y), and queue a pe-parity rulespec-us issue for the axiom line. No value or expectation changes; new edge cases verified against the source tables.

hua7450 and others added 2 commits October 2, 2026 12:35
Give every multi-page reference a single page in the link, with the pages
named in the title or in a comment. Count partnership and S corporation
income as earned income, floored at zero like the other business sources
(KEESM 6300 and 6313), and farm rental income as unearned income (KEESM
6313). State the exact effective-date range in the rounding comment and
correct two test header page cites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hua7450

hua7450 commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the second round. Fixes are in d9a4e0870e, followed by a merge of main (head 67d231acd5). Nothing was run locally; CI validates.

Should-address

  1. Microsimulation deferral. Tracked in Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754 (default heating_type to electricity and remove the UNSPECIFIED-gated adapters). That change affects API partners, so it needs its own testing and communication and does not block the state PRs. The PR body now links it. Callers must send heating_type with the matching fuel bill.
  2. Comma page ranges. Fixed, and my round-1 reply on this was wrong. Every reference now has one page in the link, with the pages named in the title (13 parameter links) or in a comment above the URL (the one variable reference). The test header comments name pages in text instead of anchors.
  3. Axiom line. Now axiom: TheAxiomFoundation/rulespec-us#1469 queued.

Suggestions

  • 1: the comment in rounding_increment.yaml now states the range (2025-01-02 through 2026-01-01).
  • 3: test headers fixed, including the Section 1.8 page.
  • 4: PR body updated (131 cases).
  • 5: partnership_s_corp_income is now counted as earned income, floored at zero like the other business sources (KEESM 6300: profit from performing services, including managerial responsibilities; KEESM 6313: a loss cannot be deducted from other income). farm_rent_income is counted as unearned income (KEESM 6313: property income is unearned unless a household member actively manages it at least 20 hours a week). Two new cases cover them. miscellaneous_income is not added.
  • 6: all six sibling PRs now place their state alphabetically between IL and TX, on the coverage line and in the list.
  • 2: not taken. Registry notes stay short; the assumptions are in the PR body and code comments.
  • 7: naming unchanged.

@DTrim99

DTrim99 commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Program Review — Round 3

PR #9663: Add Kansas LIEAP regular heating assistance (hua7450, branch ks-liheap). This round re-reviews head 67d231acd5. The delta since round 2 (f0e9c030ac) contains two commits:

  • d9a4e0870e: second set of review fixes (13 files).
  • 67d231acd5: merge of main. It touches no Kansas file and does not touch programs.yaml.

Round 2 found 0 critical items, 3 remaining should-address items and 7 suggestions.

Results of the delta:

  • All three remaining should-address items are fixed.
  • No numeric parameter value and no existing test expectation changed.
  • Two income sources were added to the source lists: partnership_s_corp_income (earned) and farm_rent_income (unearned). Both classifications match KEESM 6300 and 6313, and the plan's §1.9 checklist.
  • The two new test cases compute correctly by hand and pass in CI.
  • Two CI jobs failed. Both were terminated with SIGTERM in test batches that contain no Kansas files (see CI Status).

Path abbreviations:

  • P/ = policyengine_us/parameters/gov/states/ks/dcf/liheap/
  • V/ = policyengine_us/variables/gov/states/ks/dcf/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ks/dcf/liheap/

Line numbers refer to 67d231acd5.

Round-2 Should-Address Items: Status

# Round-2 item Verdict Evidence
1 The microsimulation deferral (shared heating_type default) is not tracked FIXED #9754 ("Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters") is open. hua7450 opened it on 2026-10-02 and it cites the #9663 review. Its scope covers the default change, the DC/IL/MA/MI adapters, three shared gates and the partner fixtures. The PR body's "Microsimulation" paragraph says population totals are $0 until the default changes and links #9754.
2 Comma page ranges in reference hrefs FIXED No #page=N,M or #page=N-M fragment remains in the Kansas parameter, variable or test trees. Each of the 14 flagged hrefs now uses a single integer, the first page of the range named in its title: P/income/sources/earned.yaml:31,33 (5); P/income/sources/unearned.yaml:39 (5); P/payment/matrix/electricity.yaml:1194 (1) and :1196,1198 (8); P/payment/matrix/natural_gas.yaml:1196,1198 (8); P/payment/matrix/other.yaml:146,148 (8); P/payment/matrix/propane.yaml:1194 (2) and :1196,1198 (8); V/income/ks_liheap_countable_earned_income.py:26 (6), with the comment # Section 1.9 (pages 6-7). at line 25. The "Page N of 48" footers confirm the start pages: FY2026 §1.8 on p5, §1.9 on p6-7, §2.5 starting on p8; FY2025 §1.8-1.9 starting on p5. The FY2025 matrix has electricity starting on p1 and propane on p2.
3 axiom: line FIXED The PR body now reads axiom: TheAxiomFoundation/rulespec-us#1469 queued. rulespec-us#1469 ("Encode Kansas LIHEAP with PolicyEngine oracle coverage") is open and labelled enhancement and pe-parity. Its content is noted in Suggestion 3.

Verification of Claimed Suggestion Fixes

Round-2 suggestion Verdict Evidence
1. Effective-date comment wording FIXED P/eligibility/rounding_increment.yaml:8-9 now gives the range 2025-01-02 through 2026-01-01. This is correct: YEAR periods resolve at January 1. A 2025-01-01 date would move period 2025 onto the FY2026 value (1), and a date after 2026-01-01 would leave period 2026 on 0.01.
2. Shortened registry note NOT TAKEN (rationale accepted) The author keeps registry notes short. The assumptions are in the PR body and code comments, and the microsimulation status is now in the PR body and #9754.
3. Comma ranges in test header comments FIXED The five headers now cite pages in text. T/income/ks_liheap_countable_earned_income.yaml:2 and T/income/ks_liheap_countable_income.yaml:2 read "1.8-1.9, pages 5-7", which covers §1.8 on p5. T/integration.yaml:4-5, T/ks_liheap_fuel_category.yaml:2 and T/payment/ks_liheap_matrix_amount.yaml:2 are also corrected.
4. PR body counts FIXED "131 Kansas YAML cases in 14 files" matches CI: the states-shard-1 log shows 14 Kansas files with 131 passing cases. The stale "not run locally" sentence is gone. The fix history now describes the added coverage accurately.
5. Unmapped countable sources PARTIALLY FIXED partnership_s_corp_income was added to P/income/sources/earned.yaml:15, and farm_rent_income to P/income/sources/unearned.yaml:15. Both were checked against the sources (see Delta Verification). miscellaneous_income was not added and no reason is given (Suggestion 4).
6. programs.yaml merge order PARTIALLY FIXED KS sits between IL and TX on the coverage line (policyengine_us/programs.yaml:805) and in the list (:826-833). Sibling PRs #9723 (NE), #9725 (KY), #9726 (NC), #9727 (MS) and #9728 (IN) follow the same convention. #9744 (NY), #9745 (NJ) and #9746 (MD) still append after TX (Suggestion 5).
7. Naming across the batch Unchanged Author's choice (ks_liheap versus nc_lieap). No further action requested.

Delta Verification

  • Numeric parameter values: none changed.
    • P/eligibility/rounding_increment.yaml changed only in a comment. It keeps 2024-10-01: 0.01 and 2025-10-01: 1.
    • The four matrix files changed only in hrefs.
  • Source-list additions:
    • KEESM 6300 (October 2024 and January 2026 text identical): "Earned income is income which is received as wages, salary, or profit resulting from the performance of services, including managerial responsibilities, by the recipient." This supports the earned classification of partnership_s_corp_income (P/income/sources/earned.yaml:11-15).
    • KEESM 6313 (relevant sentences identical in both editions):
      • "A loss from self-employment cannot be deducted from other income". This supports flooring the new earned source.
      • Rental property income "shall be considered self-employment earned income only if a member of the household is actively engaged in the production of income and/or the management of the property at least an average of 20 hours per week. Otherwise it will be considered unearned income." This supports farm_rent_income as unearned (P/income/sources/unearned.yaml:13-15).
    • KEESM 13360 refers self-employment treatment to 6313 and 7122, so 6313 applies to LIEAP.
    • The plan's §1.9 checks "Self - Employment Income" and "Rental income" in both seasons (FY2026 p6 and FY2025 p6, confirmed on the PNG renders).
    • All four new KEESM 6313 hrefs return HTTP 200.
    • partnership_s_corp_income and farm_rent_income are Person-level YEAR variables, consistent with the per-person earned formula and the adds aggregation.
  • Formula: V/income/ks_liheap_countable_earned_income.py:33-34 floors each listed source at zero, so the new partnership/S-corp source is floored. The unearned variable (V/income/ks_liheap_countable_unearned_income.py:18) uses adds, so farm_rent_income is summed with its sign (Suggestion 1).
  • New test expectations, checked by hand:
    • T/income/ks_liheap_countable_earned_income.yaml:150-177 (Case 9):
      • person1: 20,800 + 9,000 = 29,800.
      • person2: 20,800 + max(−4,000, 0) = 20,800.
      • Household total: 50,600. No unearned income is supplied, so countable income is 50,600.
    • T/income/ks_liheap_countable_income.yaml:222-244 (Case 10): farm rent of 7,200 gives 7,200 unearned income and 7,200 countable income.
  • Existing expectations: none changed. The test-file edits outside the two new cases are header comments only.
  • Test inventory: 131 cases in 14 files, all passing in Baseline states-shard-1 at 67d231acd5.

Critical (Must Fix)

None.

Should Address (remaining)

None. All three round-2 items are fixed (see the status table).

Suggestions

  1. Farm rental losses offset other income: P/income/sources/unearned.yaml:13-15 and V/income/ks_liheap_countable_unearned_income.py:18.
    • farm_rent_income has no floor in its definition, and adds sums it with its sign. A negative amount therefore lowers countable income.
    • KEESM 6313 is the section the PR cites for this source. It says ownership of rental property "shall be considered a self-employment enterprise" and that "A loss from self-employment cannot be deducted from other income."
    • The same commit floors partnership_s_corp_income on the strength of the 6313 loss rule.
    • This is the same class of issue as round-1 Should 2 (rental_income), which was accepted as consistent with repo practice. It is therefore not a should item here.
    • Options: floor both rental sources, or note the signed treatment in the comment. A negative-farm-rent case would pin either choice.
  2. KEESM 6300 reference: P/income/sources/earned.yaml:11-15 and :21-41.
    • The new comment relies on KEESM 6300, but the metadata references list only 6313 for the new source.
    • Adding the October 2024 and January 2026 KEESM 6300 links would make the citation traceable:
      • https://content.dcf.ks.gov/ees/KEESM/Robo10-24/Robo_10_01_24/keesm6300.htm
      • https://content.dcf.ks.gov/ees/KEESM/Robo01-26/Robo_01_01_26/keesm6300.htm
  3. Content of rulespec-us#1469: PR body, axiom: line.
  4. miscellaneous_income still unmapped (carried forward from round-1 Suggestion 5): P/income/sources/unearned.yaml:3-29.
    • KEESM 13360 counts "most other income". Leaving this source out biases eligibility and benefits slightly upward.
    • If it is left out on purpose (for example, because the input mixes one-time receipts), a one-line comment would record why.
  5. Registry merge order across the batch: policyengine_us/programs.yaml:805, 826-833.

Branch Status

  • GitHub reports the PR as MERGEABLE. The merge state is BLOCKED (changes requested, and CI not green).

CI Status

  • Run 37035030288 at 67d231acd5: 34 checks passed, 2 failed and 1 is pending (codecov/project).
  • Failed: Full Suite - Baseline (ssa-usda).
    • The log ends with make: *** [Makefile:142: test-yaml-no-structural-other-usda] Terminated and ##[error]Process completed with exit code 143.
    • This came after 783 s without output following gov/usda/wic/wic.yaml.
  • Failed: Full Suite - Baseline (states-shard-3).
    • Batch 3 stalled for about 300 s in gov/states/ok/..., and the job was terminated with exit code 143 after ok_count_exemptions.yaml.
  • Nature of the failures:
    • Neither job includes a Kansas test file, and neither log shows an assertion failure.
    • The concurrent sibling run 37035037082 (ky-liheap, which touches only Kentucky files) failed the same two jobs, plus states-shard-2, with the same exit code. This points to runner termination rather than a defect in this PR.
  • Kansas tests: all 14 Kansas files ran in Baseline states-shard-1 and passed (131 cases).
  • Passed: Lint, the changelog fragment check, Quick Feedback, Microsimulation, Household API Partners and every other shard.
  • Needed: re-run the two failed jobs.

Review Severity: APPROVE

0 critical and 0 remaining should-address items; all three round-2 items are fixed. The two failing checks (Full Suite - Baseline ssa-usda and states-shard-3) are runner kills (exit 143) in shards with no Kansas files: every batch that completed passed, and a re-run was killed the same way (states-shard-3 batches peaking at 9.4-10.6 GB RSS). The same two shards also fail on main (run 37012346466) and on the sibling LIHEAP PRs, so this is a CI capacity issue, not a defect in this PR.

@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.

Round 3 (#9663 (comment)): all round-2 should items fixed; 0 critical, 0 should remaining. The two failing CI shards are runner kills unrelated to this PR (same failure on main).

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.

2 participants