Skip to content

Read SNAP utility allowance incurrence from the canonical heating inputs - #9397

Draft
hua7450 wants to merge 2 commits into
PolicyEngine:mainfrom
hua7450:heating-snap-utility-incurrence
Draft

Read SNAP utility allowance incurrence from the canonical heating inputs#9397
hua7450 wants to merge 2 commits into
PolicyEngine:mainfrom
hua7450:heating-snap-utility-incurrence

Conversation

@hua7450

@hua7450 hua7450 commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Stage 2a of the heating-input consolidation (#9284, under the #9275 atomic-inputs rule), following the foundation PR #9360. This half moves SNAP's utility-allowance incurrence and the utility-count variables onto the canonical heating inputs and fixes two latent gaps in the individual and limited allowance paths. Stage 2b (Michigan home heating credit + the #8964 utilities-responsibility projection) is a separate PR because it carries a partner-facing default flip.

What the regulation says

7 CFR 273.9(d)(6)(iii):

  • (D)(1) The heating and cooling standard (HCSUA) must be available to households that incur heating or cooling expenses separately from their rent or mortgage.
  • (A)(1) An individual standard for each type of utility expense; FNS publishes one standard for gas and fuel together.
  • (A)(3) The LUA covers electricity and fuel for purposes other than heating or cooling, water, sewerage, trash, optionally telephone, and must include at least two utilities.

Changes

Variable Before After
has_cooling_expense New input (SPM unit, bool). Cooling is not derivable from an electricity bill, so it is its own fact.
has_gas_and_fuel_expense New derived bool: any of gas_expense, bottled_gas_expense, fuel_oil_expense, wood_expense, coal_expense, other_heating_fuel_expense, cooking_fuel_expense > 0. One SNAP standard, one incurrence flag.
has_heating_cooling_expense heating_cooling_expense > 0 (heating_expense > 0 & ~heat_expense_included_in_rent) | has_cooling_expense | (UNSPECIFIED & heating_cooling_expense > 0)
count_distinct_utility_expenses heating_cooling, electricity, gas, phone, trash, water, sewage same categories, with gas replaced by has_gas_and_fuel_expense so deliverable and cooking fuels count (once)
snap_individual_utility_allowance gas-and-fuel standard incurred only by gas_expense incurred by has_gas_and_fuel_expense
utility_expense adds of heating_cooling + gas + electricity + water/sewage/trash/phone formula: all per-fuel bills + water/sewage/trash/phone, plus heating_cooling_expense only for UNSPECIFIED households
IL AABD utility_types metered_gas_expense gas_expense (parameter node stays metered_gas, the regulation's term, via a key override in the formula)
metered_gas_expense read by IL AABD deprecated, no readers

Behavior preservation

The same UNSPECIFIED gate as #9360: a household that does not send heating_type keeps its pre-canonical signal (heating_cooling_expense > 0), which still ignores heat_expense_included_in_rent exactly as before. Live traffic check (household API analytics, 14 days to 2026-09-06): heating_cooling_expense + has_heating_cooling_expense ride on 65% of all requests as the SNAP signal, always without heating_type; metered_gas_expense, has_cooling_expense, and every deliverable-fuel column have zero traffic. So no partner value moves from the incurrence change.

Two value changes are possible only for households that send the affected columns:

  • A household sending a deliverable or cooking fuel bill (no metered gas) with no heating/cooling signal now incurs the gas-and-fuel standard on the IUA path and counts one more utility toward the LUA. This is the (A)(1) fix; there is no live traffic on those columns.
  • A household sending metered_gas_expense to IL AABD gets no metered gas allowance until it sends gas_expense (zero traffic; the one analytics-coverage fixture is migrated in this PR).

Canonical households (a known heating_type): heating incurrence follows the primary fuel's bill and is switched off by heat_expense_included_in_rent per (D)(1); a heat-in-rent home with gas and electricity bills lands on the LUA. A known type never reads heating_cooling_expense, so the migration contract from #9360 holds: send the canonical inputs, not both vocabularies.

Not in this PR

Tests

  • New: has_heating_cooling_expense (11 cases: legacy signal, per-fuel bill alone, heat in rent on canonical vs legacy paths, cooling with and without heating, known type ignoring the legacy input), has_gas_and_fuel_expense (6), utility_expense (4, including the no-double-count case).
  • Appended: count_distinct_utility_expenses (deliverable fuel counts, gas + propane counts once, cooking fuel), snap_individual_utility_allowance (fuel oil only, gas + propane once, wood + cooking fuel once), snap_utility_allowance_type (five end-to-end cases from canonical inputs through both derived variables), IL AABD (gas_expense pays the metered gas allowance; metered_gas_expense no longer read).

Refs: #9284, #9275, #9360, #7745.

🤖 Generated with Claude Code

has_heating_cooling_expense derives heating from heating_expense net of
heat_expense_included_in_rent and cooling from the new has_cooling_expense
input, keeping heating_cooling_expense as the signal for UNSPECIFIED
households. A new has_gas_and_fuel_expense treats metered gas and every
deliverable or cooking fuel as the one SNAP gas-and-fuel utility for both
the individual standard and the utility count. utility_expense sums the
per-fuel bills without double counting a known heating fuel, and Illinois
AABD reads gas_expense for its metered gas allowance, deprecating
metered_gas_expense.

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

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (f9b5549) to head (255a25a).
⚠️ Report is 52 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main     #9397    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            2         8     +6     
  Lines           20       126   +106     
  Branches         0         3     +3     
==========================================
+ Hits            20       126   +106     
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.

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.

1 participant