Skip to content

Add Mississippi LIHEAP regular heating assistance - #9727

Merged
hua7450 merged 10 commits into
PolicyEngine:mainfrom
hua7450:ms-liheap
Oct 3, 2026
Merged

hua7450 merged 10 commits into
PolicyEngine:mainfrom
hua7450:ms-liheap

Conversation

@hua7450

@hua7450 hua7450 commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Mississippi LIHEAP regular assistance using FY2026 rules, with existing inputs only. Mississippi pays energy bills up to a cap per energy type. For a one-person household with $10,000 of countable annual income, the caps are $1,300 for an all-electric home, or $700 for propane, $600 for natural gas and $320 for wood or another fuel, plus up to $600 toward the electric bill of a home that does not heat with electricity.

Year convention: undated formulas follow the model's existing parameter backfilling. Earlier years use the earliest available state schedule with period-specific federal median income; these are unverified historical estimates. The registry records verified_years: "2026".

  • Income limit. 60% of state median income. Truncating the four-person 60% amount before the household-size adjustment reproduces all 20 published limits; this is a reconciled numerical pattern, not an express rounding rule.
  • Income. Counts adult wages, existing net self-employment income (ordinary and SSTB) and farm operations income, and unearned income of every member, including Railroad Retirement benefits (Rule 6.11 F(3)). Rule 6.11 I(1) directs use of Schedule C net profit or loss, so the signed amounts are kept and the household total is floored at zero. The unearned list follows manual Rule 6.11 C(13), F(7)-(8) and K(1); FY2026 plan section 1.9 leaves the matching rows unchecked.
  • Payment (Rule 7.8 C and D). The main heating fuel bill is paid up to that fuel's column. A total electric household receives the Total Electric column. "All other households may receive a total of the electric column and the main heating source column that is used", so a home not heated by electricity also has its electric bill paid up to the Electric column. The two caps never exceed the Maximum Benefit column, which equals the propane and electric columns combined, so that column is not a separate parameter. Any energy bill establishes responsibility under Rule 6.1 A.
  • Income bands. The published bands and exceptional cells are kept as printed, with a shared step per band for each column.

Household and immigration basis

Household size is the number of documented members. Rule 6.3 B lets documented members receive assistance when the head is undocumented, and under Rule 6.3 C an undocumented member's income "must be included in determining the household's total income" while "this individual will not be counted in determining the household size". Rule 6.11 J(2) says a minor's Social Security or SSI "must be included and is listed under the parent or legal guardian in the household", so a minor's unearned income is counted in every household, including one whose head is undocumented. Minors' earnings are excluded (Rule 6.11 D(3) and J(1)). The existing status input approximates the required documentation; boarder and attendant arrangements under Rules 6.5-6.6 are not modeled.

Why the published income bands are kept

Rule 7.8 A (manual PDF page 39) and FY2026 plan section 2.5 (page 9) direct use of the attached benefit matrix. Neither defines 25-percentage-point poverty bands or a rounding rule. The matrix prints $31,313 for size 3 where that pattern yields $33,313, and $35,325 for size 10 where it yields $32,575. It also extends size 8's fifth band to $70,965 and size 12's fourth band to $77,136. The tables are encoded as published.

Limitations

  • The matrix has unprinted intervals for sizes 11 and 15, no upper range for sizes 10, 18 and 20, and no schedule above size 20. These return zero as coverage gaps, not findings of ineligibility. The size-16 fourth-band propane amount is printed as $350 and kept.
  • A household with heat included in rent is eligible but receives nothing for the heating column: Rule 6.4 pays the itemized charge, or $100 per intake, and neither is an input.
  • Annual income approximates the preceding 30-day budget and annual bills approximate the bills presented during the program year. Prior awards, propane gallon limits, prepaid accounts, emancipated applicants and recurring versus occasional receipts are not modeled.
  • heating_type must be sent. An unspecified fuel receives no payment unless heat is included in rent.
  • 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 Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754.

Crisis assistance and energy equipment are out of scope.

Sources:

Validation: 39 Mississippi YAML cases cover the published band boundaries and anomalies for every table, the electric column, the propane exception and its neighbours, income limits, bill caps, household treatment, and business profits and losses. All 93 matrix rows and all 20 published income limits were reconciled against the source. CI runs the test suite.

axiom: TheAxiomFoundation/rulespec-us#1467 queued

@codecov

codecov Bot commented Sep 30, 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 (34d7edb).
⚠️ Report is 130 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9727   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         5    +1     
  Lines           76        96   +20     
  Branches         2         1    -1     
=========================================
+ Hits            76        96   +20     
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 5 commits September 30, 2026 19:59
…nd tests

- Count farm_operations_income with self-employment income (Rule 6.11 C, I)
- Remove the terminal zero bracket that duplicated the income limit in 17
  income-band tables; keep it where it encodes a published gap
- Disclose that the countable unearned list follows the manual where the state
  plan checklist differs
- Add metadata.period to parameters
- Add farm income, band-table and end-to-end tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replace the deprecated dividend_income alias. Results are unchanged because the alias only adds ordinary_dividend_income.

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

DTrim99 commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Program Review

PR #9727: Add Mississippi LIHEAP regular heating assistance (author: hua7450, head b7885ff92a)

Scope: MS LIHEAP regular heating assistance (MDHS Division of Community Services), FY2026 values dated 2025-10-01. Crisis, cooling and weatherization are out of scope. The PR adds 29 parameters, 5 variables, 5 test files (25 cases), a changelog fragment and a programs.yaml entry.

Path abbreviations used below:

  • P/ = policyengine_us/parameters/gov/states/ms/mdhs/liheap/
  • V/ = policyengine_us/variables/gov/states/ms/mdhs/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ms/mdhs/liheap/

Source Documents

Document Link Page convention
MS LIHEAP Policy Manual, Title 18 Part 24, revised January 2026 (cited by the PR) https://www.sos.ms.gov/adminsearch/ACCode/00000693c.pdf All #page= links below are PDF pages (printed page N = PDF page N+1). Appendix pages 58-64 are images. The URL returns HTTP 403 to automated clients; a Wayback copy is at https://web.archive.org/web/20260616040020/https://www.sos.ms.gov/adminsearch/ACCode/00000693c.pdf
FY2026 benefit matrix (cited by the PR) https://liheapch.acf.gov/docs/2026/benefits-matricies/MS_BenefitMatrix_Heat-Cool_2026.pdf Offset 0; p.1 sizes 1-3, p.2 4-6, p.3 7-9, p.4 10-13, p.5 14-17, p.6 18-20. Identical, cell for cell, to manual PDF pp.59-64
FY2026 LIHEAP State Plan https://liheapch.acf.gov/docs/2026/state-plans/MS_Plan_2026.pdf Offset 0
FY2025 benefit matrix (sizes 1-3 only) https://liheapch.acf.gov/docs/2025/benefits-matricies/MS_BenefitMatrix_2025.pdf Offset 0
FY2025 LIHEAP State Plan https://liheapch.acf.gov/docs/2025/state-plans/MS_Plan_2025.pdf Offset 0
MDHS LIHEAP page https://www.mdhs.ms.gov/community/liheap/ HTML
ACF FY2026 Mississippi SMI table https://liheapch.acf.gov/profiles/povertytables/FY2026/mssmi.htm HTML

The MDHS-hosted manual copy (2025-LIHEAP-Policy-Manual-(Final-Unmarked).pdf) is not interchangeable with the sos.ms.gov edition: its image appendix starts one page later (PDF 60 rather than 59). The PR's anchors fit the sos.ms.gov edition.

Branch Status


Critical (Must Fix)

1. Rule 7.8 D: the Electric column is never paid to households whose main heat is not electric

  • Code:
    • V/ms_liheap.py:37 builds the cap from the main-fuel column only; V/ms_liheap.py:44-49 scopes out the electric allowance in a comment and uses the main-fuel heating_expense only; V/ms_liheap.py:54 pays min(cap, expense).
    • P/maximum_payment.yaml and P/payment_reduction.yaml encode the Propane, Natural Gas, Wood/Other Fuel and Total Electric columns. The matrix's Electric column (700 at band 1, step 50) and Maximum Benefit column (1,500 at band 1, step 100) are not encoded.
    • V/ms_liheap_eligible.py:21-23 requires a main-fuel bill above zero.
    • T/ms_liheap.yaml:12-22 asserts the omission (NATURAL_GAS 700, PROPANE 800, WOOD 400 at band 1).
  • Source:
    • Rule 7.8 D (manual PDF p.39): "The "Total Electric" column represents the yearly amount that a total electric household may receive. All other households may receive a total of the electric column and the main heating source column that is used."
    • Rule 7.8 C (manual PDF p.39) applies per-energy-type caps inside an overall cap; its example C(1)(ii) lists "Electric: $550 or $1,200 (for total electric households)", a separate electric cap for non-total-electric homes. (The example's "$1,100" total conflicts with the band-4 Maximum Benefit of $1,200 and with C's own "overall cap is $1,200", so it is not reliable as a total; the PR does not rely on it.)
    • The matrix (p.1 through p.6) prints the Electric and Maximum Benefit columns in all 93 rows; Maximum Benefit equals Propane plus Electric in every row.
    • Rule 6.1 A (manual PDF p.22) bases eligibility on "an obligation to pay an energy bill", not only a main-fuel bill.
  • Scope note: the PR documents this as out of scope, but the treatment is asymmetric rather than a consistent "heating only" scope. Electric-heated households already receive the Total Electric column, which 7.8 D calls "the yearly amount" (up to $1,500, the whole program-year cap). The manual has a single "regular assistance" component, and the plan applies the same matrix and $1,500 maximum to both its heating and cooling dates.
  • Code-path trace (2026): size 1, countable income 0 (band 1), NATURAL_GAS, gas bill $2,000, electricity bill $1,000. The model pays min(700, 2,000) = $700 (confirmed by T/ms_liheap.yaml:19-22). The rule allows 700 + min(700, 1,000) = $1,400, within the $1,500 Maximum Benefit.
  • Impact: awards are understated by up to $700 a year for every household whose main fuel is gas, propane, wood, kerosene, coal, fuel oil or other: 47-64% of the legal cap at band 1 (propane 800 vs 1,500; gas 700 vs 1,400; wood/other 400 vs 1,100; gas at band 6, 450 vs 900). A gas or propane household with a $0 main-fuel bill but an electric bill is classified ineligible.
  • Fix: add Electric and Maximum Benefit parameters; for non-electric main heat, pay min(maximum_benefit, min(fuel_cap, heating_expense) + min(electric_cap, pre_subsidy_electricity_expense)); broaden the responsibility test to any energy bill; update T/ms_liheap.yaml:12-22. Document that heating_type == ELECTRICITY is used as a proxy for 7.8 D's "total electric household" (the comment at V/ms_liheap.py:44 says "electric-heating households", which is not the rule's term).

2. Rule 6.3 B: when the head is undocumented, only members 18 and older contribute income

  • Code: V/ms_liheap_income.py:35-42 counts every member's unearned income in all cases. The comment at :35 says Rule 6.3(B)-(C) "retains non-applicants' countable income", but Rule 6.3 B excludes minors' income in this situation.
  • Source: Rule 6.3 B (manual PDF p.23): "If the head of household is undocumented and has documented member(s) in the household, the application shall not be denied for the entire household. The application must be determined eligible by using the household size of the documented household members and the income for all household members eighteen (18) years of age and older." This specific rule governs over the general Rule 6.11 J(2) (manual PDF p.33), which counts minors' unearned income.
  • Test asserting the incorrect value: T/ms_liheap_income.yaml:28-56 has an undocumented sole adult (the head) and a documented 15-year-old with SSI $2,400 and Social Security $1,200. Line 55 asserts 15,600; Rule 6.3 B gives 12,000.
  • Impact: in mixed-status households with an undocumented head and children receiving SSI or Social Security, countable income is overstated, which can raise the income band (lower cap) or flip eligibility.
  • Fix: is_household_head exists; when the head has ~is_citizen_or_legal_immigrant, restrict the unearned-income mask to members at or above adult_age, and correct line 55.

3. Railroad Retirement benefits are omitted from the countable unearned income list

  • Code: P/unearned_income_sources.yaml:4-19.
  • Source: Rule 6.11 F(3) (manual PDF p.31): "Payments such as Social Security benefits, Railroad Retirement benefits, Department of Veterans Affairs benefits, civil service annuities, workers' compensation, unemployment insurance, retirement benefits...".
  • Detail: railroad_benefits (policyengine_us/variables/household/income/person/retirement/railroad_benefits.py) is separate from social_security and appears in 17 other income-source lists in the repo.
  • Impact: income is understated for Railroad Retirement recipients, biasing eligibility and the award upward for that (small) population.
  • Fix: add railroad_benefits to the list.

Should Address

1. Invalid multi-page anchors, a skipped operative page, and a missing governing rule

  • Invalid #page=a,b,... syntax (viewers open only the first page): P/adult_age.yaml:10, P/income_limit.yaml:10, P/maximum_payment.yaml:32, P/payment_reduction.yaml:32, P/maximum_table_size.yaml:10, P/unearned_income_sources.yaml:26, V/ms_liheap.py:15, V/ms_liheap_eligible.py:10, V/ms_liheap_income.py:11, plus the PR body's plan (#page=5,6,7,8,9), manual and matrix anchors.
    • V/ms_liheap.py:15 resolves to PDF p.24 (Rule 6.4, renters), not the benefit rules on PDF pp.36-37 and 39.
    • V/ms_liheap_income.py:11 includes PDF pp.22 and 24 (Rules 6.1 and 6.4), which are not income rules.
  • Skipped page: P/unearned_income_sources.yaml:26 (#page=29,30,32,33) omits manual PDF p.31, which holds Rule 6.11 F(1)-(3), the basis for most list entries. The title at :25 cites "Rules 6.11 C, D, F and J", while the comment at :13-15 relies on K(1) (manual PDF p.33).
  • Missing rule: P/adult_age.yaml:10 (#page=30,33) cites only the income-age rules. The parameter also drives the applicant-age test at V/ms_liheap_eligible.py:17, which is Rule 6.1 D (manual PDF p.22).
  • Fix: one reference per page, e.g. income_limit: manual PDF p.22 (Rule 6.1 C) and manual PDF p.58 (60% SMI table) as two entries.

2. Reference titles name no rule, 2025-10-01 values cite only a later edition, and the FY2026 plan is not cited in metadata

  • Titles without a rule or appendix: P/adult_age.yaml:9, P/income_limit.yaml:9, P/maximum_table_size.yaml:9, P/propane_exception_amount.yaml:9, P/propane_exception_household_size.yaml:9, P/propane_exception_income_band.yaml:9 all read only "Mississippi LIHEAP Policy Manual, January 2026 revision". Examples of the expected form: "Rule 6.1 C; Appendix, 60% SMI table"; "Rule 6.1 D; Rule 6.11 D(3) and J(1)"; "Appendix benefit matrix, household size 16".
  • Edition alignment: these values take effect 2025-10-01, but the only source is the manual revised January 2026. Add the FY2026 documents in force on that date:
    • plan p.8 (section 2.1, 60% SMI) in P/income_limit.yaml;
    • matrix p.5 (size 16, band 4, propane $350) in the three P/propane_exception_*.yaml files;
    • plan p.5 and plan p.6 (section 1.9) in P/unearned_income_sources.yaml, whose comment at :13-15 relies on plan 1.9;
    • plan p.9 in V/ms_liheap_eligible.py:10, for the subsidized-housing rule in the comment at :27 (V/ms_liheap_eligible.py:24-30). The plan p.9 sentence is grammatically incomplete, plan 2.3 (plan p.8) answers "No" for subsidized renters and utilities in rent, and Rule 6.1 A accepts landlord evidence of an "undesignated portion of the rent". The PR's reading is reasonable given the ambiguity; the comment should note the 6.1 A reading alongside the new reference.
  • All values were checked against the matrix and manual with 0 mismatches; this item concerns citation completeness.

3. Non-standard test periods 2_026 / 2_022

  • Where: T/integration.yaml:2, 14, 30; T/ms_liheap.yaml:2, 13, 24, 35, 46; T/ms_liheap_eligible.yaml:2, 12; T/ms_liheap_income.yaml:2, 14, 29; T/ms_liheap_income_limit.yaml:2 (14 cases in 5 files).
  • YAML parses 2_026 as 2026, so CI passes, but no other test file in the repo uses an underscored period, searches for period: 2026 miss these cases, and the same files mix both forms (T/ms_liheap.yaml uses 2_026 for its first five cases and 2026 for Cases 6-10).
  • Fix: period: 2026 and period: 2022.
  • Source: repository convention (not a regulatory finding).

4. ms_liheap_household_size should use adds

  • Where: V/ms_liheap_household_size.py:12-17; the formula only returns add(spm_unit, period, ["is_citizen_or_legal_immigrant"]).
  • Fix: declare adds = ["is_citizen_or_legal_immigrant"] with no formula, keep defined_for = StateCode.MS, and move the comment at :13-16 into documentation.
  • Source: Rule 6.3 (manual PDF p.23) for the documentation text; the finding itself is a repository pattern.

5. Parameter descriptions and labels break the template, and some misdescribe the value

  • P/income_bands/1.yaml:1 through 20.yaml:1: "a household of 1 eligible people"; labels read "income bands for N people" (e.g. 1.yaml:32, 8.yaml:28, 12.yaml:24, 19.yaml:20); "assigns" is not a template verb. Suggested: "Mississippi sets these income bands for an eligible household of N members under the Low Income Home Energy Assistance Program." / "Mississippi LIHEAP income bands for household size N".
  • P/income_bands/10.yaml:1, 11.yaml:1, 15.yaml:1, 18.yaml:1, 20.yaml:1 append "; zero denotes an interval not covered by the published matrix." Move that note to a YAML comment above the zero bracket.
  • P/adult_age.yaml:1: misplaced modifier ("emancipated minors under the ... Program"), non-template verb "counts", and no mention of the applicant-age use. Suggested: "Mississippi sets this minimum age for the applicant and for counting earned income under the Low Income Home Energy Assistance Program."
  • P/payment_reduction.yaml:1 describes the values as "heating caps"; they are the step-down per band. Suggested: "Mississippi reduces the heating cap by this amount for each income band above the first, by primary heating fuel, under the Low Income Home Energy Assistance Program."
  • P/maximum_payment.yaml:27 label "Mississippi LIHEAP maximum payment" reads as the program maximum; the value is the first-band cap by fuel (e.g. "Mississippi LIHEAP first income band heating cap").
  • P/maximum_table_size.yaml:1, P/propane_exception_amount.yaml:1, P/propane_exception_household_size.yaml:1, P/propane_exception_income_band.yaml:1 use "publishes", which describes the document; use "sets", "provides" or "uses".
  • P/unearned_income_sources.yaml:1 says "available regular unearned income sources"; the model cannot separate regular from occasional receipts (V/ms_liheap_income.py:39-41). Drop the qualifiers.
  • Source: repository description template (not a regulatory finding).

6. Missing boundary and branch tests

  • Band tables without an in-band test: sizes 10, 18 and 20 appear only in the zero-gap row of T/ms_liheap.yaml:45-56. Untested printed thresholds: P/income_bands/10.yaml 16,288, 35,325 (printed anomaly; see Suggestion 2), 48,863, 65,150 (matrix p.4); P/income_bands/18.yaml 27,288, 54,575 and P/income_bands/20.yaml 30,038 (matrix p.6). Size 11 bands 2-4, size 15 bands 1-2 and size 16 bands 1-2 are also untested. Every other table has top/next-dollar pairs in Cases 6-10; a mistyped threshold in these files would pass CI.
  • Propane exception negative controls: T/ms_liheap.yaml:34-44 tests the exception cell, but the three-way AND at V/ms_liheap.py:38-43 has no negative cases. Add size 16 band 4 NATURAL_GAS (550) and ELECTRICITY (1,200), and PROPANE band 4 at sizes 15 and 17 (650) (matrix p.5).
  • Untested branches:
    • the is_in_public_housing side of the OR at V/ms_liheap_eligible.py:24-26 (only receives_housing_assistance is tested, T/ms_liheap_eligible.yaml:11-22);
    • the ms_liheap amount for an eligible heat-in-rent household (Rule 6.4, manual PDF p.24; see Suggestion 1);
    • the max_(heating_expense, 0) floor at V/ms_liheap.py:49;
    • the size-0 branch where(size > 0, ...) at V/ms_liheap_income_limit.py:30 (T/integration.yaml:13-26 asserts size and benefit but not the limit);
    • the gap upper edges 17,663 (size 11) and 69,488 (size 15); only 17,664 and 69,489 are tested.

7. Axiom parity line uses n/a for a new program

  • Where: PR body line axiom: n/a: rulespec-us does not have this program yet; it should be added as a follow-up.
  • Source: CONTRIBUTING.md#axiom-parity states that parity "covers a new program". The stated reason describes the queued case, which requires a dispatch-ready rulespec-us issue labelled pe-parity with the module path (under us-ms/), corpus citation, verbatim law, required outputs, and companion tests drawn from the same external source as the YAML tests (the FY2026 matrix and manual).
  • Fix: open that issue and change the line to axiom: <rulespec issue> queued.

Suggestions

  1. Heat-in-rent households are eligible but receive $0. V/ms_liheap_eligible.py:21-23 marks them eligible; V/ms_liheap.py:49-54 pays min(cap, heating_expense) = 0. Rule 6.4 A/B (manual PDF p.24) pays "All energy-related costs included in the bill" or "$100 per intake until the amount of the benefit matrix has been met". The bias is downward and is documented at V/ms_liheap.py:50-53; a single-intake 6.4 B floor or a clearer flag would avoid an "eligible with $0" output.
  2. Unprinted matrix intervals return $0 to eligible households. P/income_bands/10.yaml above 72,507 (size-10 limit 74,050); 11.yaml 17,634-17,663; 15.yaml 68,489-69,488; 18.yaml above 84,849 (limit 86,392); 20.yaml above 60,075 (limit 89,477) (matrix p.4, p.5, p.6). The encoding matches the print (verified at 600 dpi); the bias is downward for very few households. The size-10 band-2 top of 35,325 (the 25%-FPG pattern gives 32,575) is encoded as printed but is not listed in the anomaly comment; add it.
  3. Citizenship test is broader than Rule 6.1 B for the applicant. V/ms_liheap_household_size.py:17 uses the federal 8 USC 1641 list (adds refugees, asylees, parolees, entrants, withholding cases). Rule 6.1 B (manual PDF p.22) and the MDHS page say "citizen or legal permanent resident"; Rule 6.3 C's "satisfactory immigration status" for other members (manual PDF p.23) is undefined, so the federal list is a defensible headcount proxy. Plan 17.3 (plan p.33) is generic ACF model wording. Impact is very small; document the choice and cite 6.1 B / 6.3 C.
  4. Unmodeled income types. P/unearned_income_sources.yaml omits miscellaneous_income, a possible proxy for prizes and awards (F(6)) and jury duty (F(11)) (manual PDF p.31); life-insurance proceeds (F(4)) have no variable. Following the manual over plan 1.9 (plan p.5) is appropriate. The comment at :13-15 names only gifts, rent and interest; broaden it.
  5. Self-employment and farm losses offset other members' wages. V/ms_liheap_income.py:17-34, tested at T/ms_liheap_income.yaml:136-166. Rule 6.11 I(1) (manual PDF p.32) refers to "net income available after deduction of business expenses ... Net Profit or (Loss) line", which can be read as a zero floor. The rule is ambiguous; netting biases countable income down (eligibility and awards up). Document it as a choice.
  6. Income-limit arithmetic and a leftover Indiana comment. V/ms_liheap_income_limit.py:25-30 computes smi() * floor(base*0.6) / base then floors; the division round trip can land just below an exact integer (FY2026 sizes 4 and 12 are exact: 51,424 and 77,136). Computing floor(base * rate) * size_share directly avoids a possible $1 loss in another SMI year (manual PDF p.58). The comment at :23-24 discusses "Indiana's size-first truncation", likely carried over from sibling PR Add Indiana EAP regular heating assistance #9728.
  7. P/income_limit.yaml duplicates gov.hhs.liheap.smi_limit (0.6), which DC, MA and Riverside use. A state parameter is defensible (plan p.8, section 2.1); either reuse the federal parameter or note why MS keeps its own.
  8. Years before FY2026 are backfilled. All values start 2025-10-01, so period 2025 uses FY2026 bands with FY2025 SMI. The FY2025 matrix p.1 covers sizes 1-3 with the same caps and bands at 25% of the 2024 FPG (size-1 tops 3,765 / 7,530 / 11,295 / 15,060 / 18,825 / 25,687). The backfill is documented (V/ms_liheap.py:10-13, verified_years: "2026"); optionally add 2024-10-01 values for sizes 1-3.
  9. Microsim defaults: heating_type SOLAR and UNSPECIFIED pay $0. heating_type defaults to HeatingType.UNSPECIFIED (policyengine_us/variables/household/expense/utilities/heating_type.py:26), which P/maximum_payment.yaml / P/payment_reduction.yaml map to a $0 cap. Bias direction and size: downward; if the microdata leaves heating_type unpopulated, every Mississippi SPM unit receives $0 (100% of the modeled total). SOLAR also maps to $0 although heating_expense treats SOLAR as grid electricity; for those units the bias is downward by the full cap that would otherwise apply (up to $1,500 at band 1 under Total Electric, or $400 under Wood/Other Fuel) (matrix p.1). receives_housing_assistance and heat_expense_included_in_rent default to False, which loosens the subsidized-housing gate only for heat-in-rent units that receive $0 anyway (no net bias). ms_liheap is not in any benefit aggregate (same as IL, MA and DC LIHEAP), so there is no net-income effect today.
  10. Test naming and comments. Case numbering repeats across files (Cases 4-5 in T/integration.yaml:44,79 and T/ms_liheap_income.yaml:57,88; Case 6 in T/ms_liheap.yaml:57 and T/ms_liheap_income.yaml:136) and no file has Cases 1-3; T/ms_liheap_income.yaml:32,38,45,50 use adult, child, unit, home instead of person1, person2, spm_unit, household; T/ms_liheap_income.yaml:119-121 places a comment between name: and period:; T/integration.yaml:28 says "with the earliest available state benefit matrix", but the case uses FY2026 values extrapolated backward (see Suggestion 8).
  11. Reviewer-directed comments and open action items in code. V/ms_liheap.py:21-24 ("That numerical pattern is not an authorized replacement") argues a review point; V/ms_liheap.py:35-36 ("agency clarification is needed before correcting the source") is an untracked action item. State the fact ("the matrix prints $350; encoded as published", matrix p.5) and track the clarification in an issue. Comment lines nearly equal code lines in V/ms_liheap.py; long scope caveats fit better in documentation and the PR body.
  12. Changelog wording. changelog.d/ms-liheap.added.md:1 ("... using existing inputs") uses internal jargon; e.g. "Add Mississippi LIHEAP regular heating assistance (FY2026)."
  13. maximum_table_size is coupled to the band files; numeric file names are unprecedented. V/ms_liheap.py:26 loops range(1, int(p.maximum_table_size) + 1) and indexes p.income_bands[str(n)]; raising P/maximum_table_size.yaml without adding income_bands/21.yaml raises a KeyError, and lowering it silently skips tables (manual PDF p.58). Iterate over the children of p.income_bands or add a consistency test. P/income_bands/1.yaml ... 20.yaml are the only bare-number parameter file names in the repo; size_1.yaml etc. would read better in reform paths.
  14. The manual URL blocks automated clients. https://www.sos.ms.gov/adminsearch/ACCode/00000693c.pdf returns HTTP 403 (Akamai) to scripted requests; 12 references (7 parameter files, all 5 variables) point to it. Consider a Wayback snapshot as a second reference (Wayback copy, PDF p.39). The MDHS-hosted copy is not a drop-in replacement (appendix offset differs by one page).
  15. programs.yaml ordering and sibling conflicts. The PR appends MS to the end of the coverage string (policyengine_us/programs.yaml:805) but inserts the state_implementations entry between IL and TX (:826). All six open hua7450 LIHEAP PRs (Add Kansas LIEAP regular heating assistance #9663, 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, Add Indiana EAP regular heating assistance #9728) rewrite line 805, and five insert into the same hunk after variable: il_liheap (:825). Agreeing on one order before merging (e.g. IL, IN, KS, KY, MS, NC, NE, TX in both fields) and resolving each conflict as a union avoids silently dropping a sibling's state.
  16. programs.yaml notes omit known gaps. policyengine_us/programs.yaml:833 mentions only crisis and cooling. If they remain unmodeled, name the Rule 7.8 D Electric allowance (manual PDF p.39), the $0 heat-in-rent result (manual PDF p.24) and the unprinted intervals for sizes 10, 11, 15, 18 and 20.

PDF Audit Summary

Item Source Checked Match Notes
income_bands/1-20 thresholds Matrix pp.1-6; manual PDF pp.59-64 78 finite thresholds + 5 deliberate zero brackets 78/78 Anomalies (31,313; 35,325; 17,633/17,664; 68,488/69,489; size-10/18/20 tops) re-verified at 600 dpi
maximum_payment (band-1 caps) Matrix p.1 8 sourced entries 8/8 Propane 800, Natural Gas 700, Wood/Other 400 (x5), Total Electric 1,500. SOLAR/NONE/UNSPECIFIED = 0 has no source (Suggestion 9)
payment_reduction Matrix pp.1-6 8 sourced entries 8/8 50 / 50 / 40 / 100
Matrix cells reproduced by the formula Matrix pp.1-6 93 rows x 4 encoded columns = 372 372/372 Electric and Maximum Benefit columns (2 x 93 cells) not encoded (Critical 1)
propane_exception_* (3 files) Manual PDF p.63; matrix p.5 3 3/3 Size 16, band 4, propane $350
income_limit Manual PDF p.22 (Rule 6.1 C); plan p.8 1 (0.6) 1/1
60% SMI limits (test) Manual PDF p.58; ACF FY2026 SMI table 20 sizes 20/20 floor(0.6 x 85,708) = 51,424 x size share, floored
adult_age Manual PDF pp.22, 30, 33 1 (18) 1/1 Rule 6.1 D, 6.11 D(3), J(1)
maximum_table_size Manual PDF pp.58, 64 1 (20) 1/1
unearned_income_sources Manual PDF pp.29-33 13 entries 13/13 traceable railroad_benefits omitted (Critical 3)
Test expected values (hand-computed spot checks) Matrix pp.1, 5-6; manual PDF pp.23, 58 8 8/8 Includes the 2022 backfilled case and the farm-loss case
Value mismatches surviving code-path trace and 600-dpi re-render 0 Logic gaps are Critical 1-3

Validation Summary

Check Result
CI 37/37 pass on b7885ff92a
Head moved since review No
Branch freshness 7 ahead, 12 behind main; no overlap with PR files
Changelog changelog.d/ms-liheap.added.md present; type added matches a new program
Hard-coded values None (only 0 and 1 literals)
Duplicate variables None; one parameter duplicates gov.hhs.liheap.smi_limit (Suggestion 7)
Vectorization Correct (where, enum/string parameter indexing, no Python if on arrays)
defined_for and entities Correct; AL row in T/integration.yaml:1-12 confirms state gating
Parameter values vs. sources 0 mismatches; 2 matrix columns not encoded (Critical 1)
Regulatory logic 3 verified mismatches: Rule 7.8 D, Rule 6.3 B, Rule 6.11 F(3)
References 9 in-file multi-page anchors plus 3 in the PR body; 1 skipped operative page; 6 titles without a rule; FY2026 plan absent from metadata
Tests 25 cases; 8/8 spot checks match; 1 test asserts a value contrary to Rule 6.3 B; 1 test asserts the Rule 7.8 D omission; 3 band tables without in-band tests
Test period format 14 cases use 2_026 / 2_022
Microsim / aggregates ms_liheap not in any benefit aggregate (consistent with IL/MA/DC LIHEAP); UNSPECIFIED default biases ms_liheap downward
SNAP immigration (#9661) Not used; immigration enters via is_citizen_or_legal_immigrant only
programs.yaml Entry present (verified_years: "2026", status partial); conflicts expected with 5 sibling PRs
Axiom line Present, but n/a for a new program (Should 7)

Review Severity: REQUEST_CHANGES

3 critical and 7 should-address findings. All parameter values reproduce the published FY2026 matrix and manual, and CI is green; the critical items are benefit and income logic that departs from Rules 7.8 D, 6.3 B and 6.11 F(3).

Next Steps

  1. Critical 1: encode the Electric and Maximum Benefit columns, pay the main-fuel cap plus the electric cap (within the Maximum Benefit) for non-total-electric households, broaden the energy-responsibility test, and update T/ms_liheap.yaml:12-22.
  2. Critical 2: restrict income to members 18 and older when the head is undocumented, and correct T/ms_liheap_income.yaml:55 to 12,000.
  3. Critical 3: add railroad_benefits to P/unearned_income_sources.yaml.
  4. Should 1-2: split multi-page anchors into one reference per page, add PDF p.31 and the Rule 6.1 D page, put rule numbers in titles, and add the FY2026 matrix and plan references.
  5. Should 3-5: change test periods to 2026 / 2022, convert ms_liheap_household_size to adds, and rewrite parameter descriptions and labels.
  6. Should 6: add band-threshold tests for sizes 10, 18 and 20 (including 35,325 / 35,326), propane-exception negative controls, and the untested branches.
  7. Should 7: open a dispatch-ready pe-parity issue in rulespec-us and update the axiom line to queued.
  8. Before merge, agree on programs.yaml ordering with the five sibling LIHEAP PRs, rebase on main, and confirm CI.

@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 (#9727 (comment)): 1 critical (Rule 7.8 D: non-total-electric households receive the Electric column plus their main-fuel column, capped at the Maximum Benefit; the PR pays the main fuel only, understating awards by up to $700), plus the should-address items in the report. All 78 income-band thresholds, 372 matrix cells and 20 income limits match the sources. Run /fix-pr 9727 or address the list.

hua7450 and others added 2 commits October 2, 2026 12:35
Pay the electric column to households not heated by electricity, in
addition to the main heating fuel column (Rule 7.8 D), and accept any
energy bill as responsibility (Rule 6.1 A). Count Railroad Retirement
benefits. Compute the income limit without a division round trip. Use
single-page references that name the rule, add FY2026 plan and matrix
references, fix test periods and names, make household size a plain sum,
rewrite descriptions and labels, and add boundary tests for the remaining
band tables, the propane exception and the housing branches.

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 review. Fixes are in 6a6a756310, followed by a merge of main (head e8df6fff93). Nothing was run locally; CI validates.

Critical

  1. Rule 7.8 D. Implemented. A home not heated by electricity is paid its fuel bill up to the fuel column plus its electric bill up to the Electric column (two new parameters: 700 in the first band, stepping down by 50). A total electric household is unchanged. Any energy bill now establishes responsibility under Rule 6.1 A. The Maximum Benefit column is not a separate parameter: it equals the propane and electric columns combined, so the two caps can never exceed it. New cases cover gas, propane and wood with both bills, all six one-person bands, and the manual's own Rule 7.8 C example.
  2. Rule 6.3 B. Kept. Rule 6.11 J(2) says a minor's Social Security or SSI "must be included and is listed under the parent or legal guardian in the household", so it is an adult's line and Rule 6.3 B does not remove it. Household size is the number of documented members; an undocumented member is left out of size and their income still counts. The comment now states this basis.
  3. Railroad Retirement. railroad_benefits added, with a case.

Should-address

  • 1, 2: one page in each link, with the rule or appendix table and the pages named in the title or in a comment above the URL. Page 31 and the Rule 6.1 D page are included, and the FY2026 plan and matrix references are added where you listed them.
  • 3: periods fixed. 4: household size uses adds. 5: descriptions and labels rewritten, including all 20 band files.
  • 6: boundary and branch tests added for sizes 10, 11, 15, 16, 18 and 20, the propane exception's neighbours, public housing, the bill floor and size 0.
  • 7: now axiom: TheAxiomFoundation/rulespec-us#1467 queued.

Suggestions

@DTrim99

DTrim99 commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Program Review — Round 2

PR #9727: Add Mississippi LIHEAP regular heating assistance (author: hua7450). Round 1 reviewed head b7885ff92a. This round reviews head e8df6fff93, which is fix commit 6a6a756310 ("Address Mississippi LIHEAP review") plus a merge of main.

Path abbreviations:

  • P/ = policyengine_us/parameters/gov/states/ms/mdhs/liheap/
  • V/ = policyengine_us/variables/gov/states/ms/mdhs/liheap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ms/mdhs/liheap/

Manual page numbers below are PDF pages of the January 2026 Title 18 Part 24 PDF (printed page + 1). Matrix page numbers refer to the FY2026 benefit matrix.

Status of round-1 findings

The posted round-1 report listed 3 critical and 7 should-address items. The formal review summary named only Critical 1, but all three critical items are assessed here.

# Round-1 finding Verdict Evidence
C1 Rule 7.8 D: households not heated by electricity get no Electric column FIXED Two new parameters, P/electric_maximum_payment.yaml:3 (700) and P/electric_payment_reduction.yaml:3 (50), reproduce the Electric column in all 93 matrix rows (matrix pp.1-6, re-read from the page images). V/ms_liheap.py:65-75 pays min(Electric cap, electric bill) on top of min(fuel cap, fuel bill) when heating_type is not ELECTRICITY. The responsibility test now accepts any energy bill (V/ms_liheap_eligible.py:26-33, Rule 6.1 A). The ELECTRICITY-as-total-electric proxy is documented at V/ms_liheap.py:62. Tests: T/ms_liheap.yaml:196-244 (Cases 11-12), T/integration.yaml:134-172 (Case 6), T/ms_liheap_eligible.yaml:33-46 (Case 3). The Maximum Benefit cap is checked separately below.
C2 Rule 6.3 B: minors' unearned income when the head is undocumented ACCEPTED-AS-RATIONALE Rule 6.11 J(2) (manual p.33) says a minor's Social Security or SSI "must be included and is listed under the parent or legal guardian in the household". On that reading the amount is part of an adult's income line, so Rule 6.3 B's "income for all household members eighteen (18) years of age and older" (manual p.23) already includes it. This reading reconciles both rules without treating either as overriding the other, so it is a defensible interpretation of the text. The basis is now stated at V/ms_liheap_income.py:40-45, in T/ms_liheap_income.yaml:66-70 and in the PR body.
C3 Railroad Retirement omitted from unearned income FIXED P/unearned_income_sources.yaml:6-7 adds railroad_benefits and cites Rule 6.11 F(3). Test: T/ms_liheap_income.yaml:182-209 (Case 8), where 21,600 + 9,600 = 31,200.
S1 Invalid multi-page anchors, skipped p.31, missing Rule 6.1 D page FIXED No #page=a,b anchor remains in any PR file or in the PR body. V/ms_liheap.py:20 now points to p.39 (Rule 7.8). V/ms_liheap_income.py:12-13 names Rule 6.3 B-C (p.23) and Rule 6.11 (pp.29-33). The P/unearned_income_sources.yaml:27 title names pp.29-33, which covers p.31. P/adult_age.yaml:9-10 cites Rule 6.1 D at p.22. The author used one link per citation, with the page range in the title, rather than one entry per page. That resolves the broken anchors; see Suggestion 3 for direct links.
S2 Titles without a rule; only the 2026-edition manual cited; plan not cited FIXED Titles now name rules and tables (P/adult_age.yaml:9, P/income_limit.yaml:9, P/maximum_table_size.yaml:9, the three P/propane_exception_*.yaml:9). New citations: plan p.8 in P/income_limit.yaml:11-12; matrix p.5 in P/propane_exception_*.yaml:11-12; plan pp.5-6 in P/unearned_income_sources.yaml:29-30; plan p.9 in V/ms_liheap_eligible.py:13-14, with the Rule 6.1 A landlord-evidence reading at :37-44.
S3 2_026 / 2_022 test periods FIXED None of the 5 test files has an underscored period.
S4 ms_liheap_household_size should use adds FIXED V/ms_liheap_household_size.py:25 uses adds = ["is_citizen_or_legal_immigrant"], and the explanation moved to documentation (:9-19).
S5 Parameter descriptions and labels FIXED All 20 band files now read "sets these income bands for an eligible household of N member(s)", with labels "for household size N". The zero-interval notes moved to YAML comments (sizes 10, 11, 15, 18, 20). The adult_age, payment_reduction and unearned_income_sources descriptions, the maximum_payment label and the "publishes" verbs are corrected.
S6 Missing boundary and branch tests FIXED T/ms_liheap.yaml adds Cases 13-19: sizes 10/18/20 in-band including 35,325/35,326; size 11 bands 2-4 and 17,663; sizes 15/16 bands 1-2 and 69,488; propane-exception negative controls; the bill floor; and heat in rent. Public housing is tested in T/ms_liheap_eligible.yaml:47-66, and the size-0 limit in T/integration.yaml:18-36 and T/ms_liheap_income_limit.yaml:11-20.
S7 Axiom line n/a for a new program FIXED The PR body now reads axiom: TheAxiomFoundation/rulespec-us#1467 queued. The issue is dispatch-ready: open; labelled pe-parity; module paths under us-ms/regulations/title-18/part-24/...; corpus citation (manifest us-ms-liheap-benefit-matrix-rule.yaml); verbatim Rules 6.1, 6.3, 6.4, 6.11, 7.4 and 7.8; required outputs for all 5 variables; companion tests derived from the manual and matrix, not from PolicyEngine output.

Tracked deferral: the heating_type default (UNSPECIFIED pays $0 unless heat is in rent, so microsimulation totals are $0) is tracked in open issue #9754 and disclosed in the PR body. This also covers the new "eligible but $0" outcome for an UNSPECIFIED-fuel household whose only bill is electric (V/ms_liheap_eligible.py:29-33 together with V/ms_liheap.py:66-68).

Round-1 suggestions the author reports as taken, all verified:

  • 2: the 35,325 anomaly is now in V/ms_liheap.py:28-30 and P/income_bands/10.yaml.
  • 3: the citizenship basis is documented in V/ms_liheap_household_size.py:9-19.
  • 6: the limit is computed as floor(floor(SMI × 0.6) × share) (V/ms_liheap_income_limit.py:25-33), the Indiana comment is gone, and all 20 limits are asserted in T/ms_liheap_income_limit.yaml:9-10.
  • 10: case numbers restart at 1 in each file, person/unit names follow convention, and the misleading backfill comment is fixed.
  • 11: the reviewer-directed comments are gone.
  • 12: the changelog wording is updated.
  • 15: the programs.yaml:805 coverage order now matches state_implementations.
  • Comment-only items 1, 5 and 9 are documented at V/ms_liheap.py:76-80, V/ms_liheap_income.py:20-25 and V/ms_liheap.py:62-64.

Rule 7.8 D logic vs. the matrix and the Rule 7.8 C example

Check Source Result
Electric column = 700 − 50 × (band − 1) Matrix pp.1-6, all 93 rows 93/93
Maximum Benefit = 1,500 − 100 × (band − 1) Matrix pp.1-6, all 93 rows 93/93
Fuel cap + Electric cap ≤ Maximum Benefit Matrix pp.1-6 93/93. Propane + Electric equals the Maximum Benefit in 92 rows. Natural Gas and Wood/Other are always lower. At size 16, band 4 (matrix p.5): Propane 350 + Electric 550 = 900, and Natural Gas 550 + 550 = 1,100, both under the printed 1,200. Leaving the Maximum Benefit out of the formula never changes a result.
$1,500 program-year cap (Rule 7.8 A, manual p.39) Matrix p.1 Largest award: 800 + 700 = 1,500 (propane, band 1)
Rule 7.8 C(1) PY2025 example: size 1, income $14,500, "maximum LIHEAP benefit ... is $1,100" Manual p.39; matrix p.1 row 11,739-15,650 (also band 4 under the FY2025 matrix, row 11,296-15,060) The listed caps (Propane 650, Electric 550 / Total Electric 1,200, Natural Gas 550, Wood 280) match band 4. Under the combined-column rule, $1,100 is the natural-gas total (550 + 550), asserted at T/ms_liheap.yaml:234,244 (Case 12, 4th entry). Propane + Electric = 1,200 matches the example's "overall cap of $1,200" and the band-4 Maximum Benefit. The example is therefore consistent with the implemented rule. This corrects the round-1 remark that called the $1,100 unreliable.
SOLAR / NONE / UNSPECIFIED P/maximum_payment.yaml:18-25; heating_expense maps SOLAR to the electric bill SOLAR and NONE get the Electric column only, and the fuel cap of 0 prevents double payment. UNSPECIFIED is paid only with heat in rent (T/ms_liheap.yaml:203-227, entries 6-9)

New and changed test expectations, hand-checked against the matrix page images: T/ms_liheap.yaml Cases 11-19; T/integration.yaml Case 6 (size 2, band 5: 600 + 500 = 1,100); T/ms_liheap_income.yaml Case 8; T/ms_liheap_eligible.yaml Cases 3-4. All match. The delta changes no existing threshold or amount: the only value lines added are the two new parameters.

Critical (Must Fix)

None.

Should Address (remaining)

None.

Suggestions

  1. The Maximum Benefit comment does not hold in one row. V/ms_liheap.py:60-61 and the PR body's "Payment (Rule 7.8 C and D)" bullet say the Maximum Benefit "equals the propane and electric columns combined". At size 16, band 4 the matrix prints Propane 350, Electric 550 and Maximum Benefit 1,200 (matrix p.5). The conclusion still holds: no fuel and electric pair exceeds the Maximum Benefit. A wording such as "every fuel column plus the Electric column is at most the Maximum Benefit column" would be exact.
  2. No combined-column test at the propane-exception row. T/ms_liheap.yaml:322-341 (Case 17) sets the size-16 natural-gas household's electric bill to 0, so the combined rule is never exercised where the Maximum Benefit and Propane + Electric differ. Two entries would lock in the cap reasoning: size 16, income 78,000 (band 4), with both bills above the caps. NATURAL_GAS should give 550 + 550 = 1,100 and PROPANE 350 + 550 = 900, both under the 1,200 Maximum Benefit (matrix p.5).
  3. Multi-page citations link only the first page. The page ranges are now named in titles and comments. Separate reference entries would let readers reach the operative pages directly:
    • P/unearned_income_sources.yaml:27-28 links p.29, but most entries rest on Rule 6.11 F(1)-(3) at manual p.31;
    • P/income_limit.yaml:9-10 links p.22, while the limits table is at manual p.58;
    • P/adult_age.yaml:9-10 covers pp.30 and 33, and P/maximum_table_size.yaml:9-10 covers p.64;
    • V/ms_liheap.py:18-20 covers p.36 and pp.59-64, V/ms_liheap_eligible.py:11-12 covers pp.23-24, and V/ms_liheap_income.py:12-13 covers p.23.
  4. programs.yaml notes (round-1 Suggestion 16, declined). policyengine_us/programs.yaml:833 does not mention three gaps the PR body discloses: the $0 heat-in-rent heating column, the unprinted intervals for sizes 10, 11, 15, 18 and 20, and $0 population totals until Default heating_type to electricity and remove the UNSPECIFIED-gated state heating adapters #9754. The registry feeds the public coverage page.
  5. Other declined round-1 suggestions remain open, at the author's discretion:
    • 4: miscellaneous_income as a prize and jury-duty proxy (P/unearned_income_sources.yaml:15-17);
    • 7: reuse of gov.hhs.liheap.smi_limit (P/income_limit.yaml);
    • 8: FY2025 values for sizes 1-3 (FY2025 matrix p.1);
    • 13: coupling of V/ms_liheap.py:32-37 to P/maximum_table_size.yaml, and the bare-number income_bands/N.yaml file names;
    • 14: a Wayback copy for the sos.ms.gov manual, which returns HTTP 403 to scripted clients.

CI Status

34 of 37 checks pass on e8df6fff93: lint, changelog, Quick Feedback with coverage, codecov patch and project, Household API Partners, microsimulation, smoke imports on 3.11-3.14, and all other baseline and contrib shards. All 39 Mississippi LIHEAP cases pass (6 + 19 + 4 + 8 + 2) in both Quick Feedback and Baseline states-shard-4.

Job State Classification Evidence
Full Suite - Baseline (states-shard-3) failure Runner kill exit code 143 and "The runner has received a shutdown signal"; batches finished before the kill passed (1,505 and 1,901 passed); 0 assertion failures
Full Suite - Baseline (ssa-usda) cancelled Runner kill exit code 143 and the shutdown-signal message; completed batches passed (14, 10, 88, 3 and 165 passed); 0 assertion failures
Full Suite - Baseline (states-shard-2) failure Infrastructure timeout, not a PR failure Batch 1 (CA through DC files) stopped producing output after DC tax tests at 17:35:47. It was killed by signal 15 at the 1,800 s batch timeout (peak RSS 15,107 MB). Batches 2-4 passed (1,794, 1,399 and 1,078 passed). There are 0 assertion failures, the shard runs no Mississippi tests, and the PR changes no file in those directories.

Concurrent runs for other PRs (in-eap, or-liheap) fail the same shards (states-shard-3 and ssa-usda), and PR #9758 ("Reduce USDA and state CI memory pressure without adding runners") is open. A re-run of the three jobs is still needed before merge.

Branch: 9 commits ahead of main and 2 behind, with no overlap between those commits and the PR files, programs.yaml, hhs.smi or is_citizen_or_legal_immigrant. GitHub reports the branch as mergeable. Sibling LIHEAP PRs still edit the same programs.yaml hunk, so later merges should resolve those conflicts by keeping every state's lines.

Review Severity: APPROVE

0 critical and 0 remaining should-address findings. Critical 1 and 3 and all 7 should-address items are fixed. Critical 2 is resolved by a documented reading of Rules 6.3 B and 6.11 J(2) that reconciles both rules. The new Rule 7.8 D logic reproduces the Electric column in all 93 rows, never exceeds the Maximum Benefit, and matches the manual's Rule 7.8 C example ($1,100 for a natural-gas household at $14,500). The three CI failures are runner kills and a batch timeout outside the PR's files, with no assertion failures. The suggestions are optional.

@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 (#9727 (comment)): the critical Rule 7.8 D item and all should-address items are resolved; rulespec-us#1467 is dispatch-ready. Remaining suggestions are optional.

…into ms-liheap

# Conflicts:
#	policyengine_us/programs.yaml
@hua7450
hua7450 merged commit e808f99 into PolicyEngine:main Oct 3, 2026
37 checks passed
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