Skip to content

Add Virginia EAP Fuel Assistance (LIHEAP regular heating) - #10303

Open
hua7450 wants to merge 7 commits into
mainfrom
va-liheap-wave1c
Open

hua7450 wants to merge 7 commits into
mainfrom
va-liheap-wave1c

Conversation

@hua7450

@hua7450 hua7450 commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Adds Virginia's Energy Assistance Program (EAP) Fuel Assistance, the state's regular LIHEAP heating benefit, for FFY2026. The model counts citizens and qualified noncitizens for household size and counts the gross income of every member, less $50 a month for each member aged 60 or older or disabled. It tests that income against 150% of the 2025 poverty guideline, or waives the test when every member receives SSI. The household must heat with an eligible fuel and pay for heat directly or through unsubsidized rent. It then scores the manual's point tables and pays points times the statewide value per point. In 2026, a person aged 65 in Fairfax County with $11,736 of Social Security and a $1,200 gas bill has $11,136 of countable income, 47% of the $23,472 limit. That scores 9 (size) + 15 (income) + 16 (Northern zone) + 20 (elderly) + 20 (living arrangement) + 10 (fuel) + 13 (energy burden) = 103 points, and 103 × $4.531665 = $466.76.

Fixes #10291

Regulatory authority

Eligibility

Requirement Source How modeled
Virginia residence Manual page 32 defined_for = StateCode.VA
At least one citizen or qualified noncitizen; others are left out of size but their income counts Manual pages 31 to 32; 22VAC40-680-20 va_eap_household_size adds is_citizen_or_legal_immigrant; size 0 is ineligible
Gross countable monthly income at or below 150% of the poverty guideline Manual page 28; plan 2.1 va_eap_income_limit = floor(guideline × 1.5 / 12) with a one-year guideline lag, reproducing the published $1,956 to $15,018 table for sizes 1 to 20 and the $687 step above 20
Households in which all members receive SSI are not held to the income maximum Manual page 28 va_eap_income_eligible waives the test when every member has ssi > 0 or receives_ssi
Primary fuel is electricity, oil, kerosene, natural gas, LP gas, wood or coal Manual page 26 heating_type list parameter; UNSPECIFIED, SOLAR, OTHER and NONE are ineligible (callers send heating_type)
Heating expense billed or included in rent Manual pages 26 and 50 has_heating_expense or heat_expense_included_in_rent
Subsidized households with heat in rent are ineligible; subsidized households paying their own heat are eligible Manual pages 26 to 27 heat_expense_included_in_rent with receives_housing_assistance or is_in_public_housing
Homeless households are ineligible Manual page 27 is_homeless
No asset test and no categorical eligibility Plan 1.4, 1.7 and 2.3 Not applied

Countable income

Item Source How modeled
Earned income Manual pages 37 to 40; plan 1.9 Wages and net self-employment, farm, SSTB and partnership or S corporation income, each floored at zero
Earnings of children under 18 Manual page 30, exemption 20 Exempt
Unearned income Manual pages 28 and 40; plan 1.9 Unemployment, strike benefits, Social Security (gross), railroad retirement, SSI, pensions, military retirement and survivor pay (military_retirement_income), retirement distributions, survivor benefits, general assistance, TANF received (tanf), rental and farm rent income, alimony, child support received, recurring cash contributions from outside the household (financial_assistance), veterans benefits, interest, ordinary dividends, workers' compensation and disability benefits
Interest and dividends Manual page 28 versus plan 1.9 Counted: the manual counts all gross unearned income and none of its exemptions covers them, while the plan leaves the box unchecked
$50 a month per elderly (60+) or disabled member, once per person Manual pages 31 and 52 Subtracted before the income test; disabled means is_disabled, Social Security disability, SSI receipt with is_ssi_disabled, or a 100% service-connected disabled veteran

Benefit

va_eap = points × value per point (22VAC40-680-30). Points (manual pages 65 to 66; matrix pages 6 to 7):

Factor Points
Household size 1 to 2: 9; 3 to 5: 12; 6 or more: 15
Income as a percent of the limit (whole percent) 0 to 19: 25; 20 to 29: 20; 30 to 39: 18; 40 to 49: 15; 50 to 59: 13; 60 to 69: 10; 70 to 79: 8; 80 to 89: 5; 90 to 94: 3; 95 to 100: 1; above 100 (all-SSI only): 0
Climate zone, derived from county (all 133 Virginia localities) Central Mountain 20; Southwestern Mountain 18; Northern 16; Western Piedmont 13; Eastern Piedmont 12; Tidewater 8
Vulnerability, highest one only Member 60 or older 20; disabled member 15; child under 6 12
Living arrangement (every eligible code) 20
Primary fuel 10, placeholder
Energy burden 13, placeholder
Season Value per point Source
FFY2023 $4.226444 FY2024 plan item 2.5
FFY2024 $4.957075 FY2025 plan item 2.5
FFY2025 $4.531665 FY2026 plan item 2.5
FFY2026 not yet published; model year 2026 uses $4.531665 VDSS sets it after the December calculation

Placeholders. VDSS sets the primary fuel points each year from a vendor price survey and a Virginia Tech consumption study: the most expensive fuel gets 20 and the others get less in proportion to cost. The energy burden is the survey's average fuel cost for the household's fuel divided by household income. VDSS publishes only the method and the burden bands, not the survey results. The model gives every household 10 fuel points (half of 20) and 13 burden points (the 50% to 69% band). Together they add $104.23 to each benefit at $4.531665 a point. The true values can range from about 2 to 45 points ($9 to $204). Both placeholders are single parameters, so the published values can replace them directly.

Years covered

Model year Season Guideline One-person limit (monthly / annual) Value per point
2026 FFY2026 (October 1, 2025 to September 30, 2026) 2025 $1,956 / $23,472 $4.531665 (FFY2025, latest published)

The registry entry records verified_years: "2026". Earlier years use parameter backfilling and are unverified historical estimates.

Not modeled and source notes

  • Annual amounts stand for twelve times the prior calendar month's income; the weekly and biweekly conversions and per-member rounding are not modeled.
  • Social Security and railroad retirement are counted gross; the Medicare premium exemption has no input.
  • Roomer and boarder income, live-in attendants, institutions, group homes, one-room occupants, the SSN rule and the subsidized excess-usage-only arrangement have no inputs.
  • The vendor-payment limit to charges through March 31 is not applied, because payment method is not an input.
  • Crisis, cooling, weatherization, equipment repair and replacement, and security deposits are out of scope.
  • The June 2017 Appendix A page drops Salem city (775); the January 2003 page lists it in Central Mountain, which the model follows. Bedford City (now part of Bedford County) and Clifton Forge (now a town in Alleghany County) have no county values of their own.
  • The manual compilation still carries the 2003 Appendix B page based on 130% of the guideline; the 2021 matrix page (150%) controls.

Test plan

  • 12 YAML files with 124 cases under policyengine_us/tests/policy/baseline/gov/states/va/dss/eap/, with expected values hand computed from the manual tables and the published limits in case comments. The suite was not run locally; CI runs it.
  • YAML tests whose expected values come from an external source (statute, official form or worksheet, official calculator)
  • make format
  • Changelog fragment in changelog.d/

axiom: needed

🤖 Generated with Claude Code

hua7450 and others added 5 commits October 20, 2026 01:55
Virginia Energy Assistance Program Fuel Assistance for FFY2026: 150% FPG
income test with the all-SSI exception, countable income with the $50
elderly or disabled disregard, climate zones from county, the manual's
point tables, placeholder fuel and energy-burden points, and the latest
published value per point.

Fixes #10291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Count recurring cash contributions (financial_assistance) as unearned
income, treat 100% service-connected disabled veterans as disabled, move
the end-to-end cases into integration.yaml, add a two-person all-SSI case,
and tidy one reference and the income-points comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now models military retirement and survivor pay as the separate
military_retirement_income total, so add it to Virginia's countable
unearned income with countable-income and benefit cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 requested a review from DTrim99 October 21, 2026 16:30
@DTrim99

DTrim99 commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add Virginia EAP Fuel Assistance (LIHEAP regular heating)

PR #10303 (author: hua7450), head 1396baf7fe. This is a first review. The PR adds Virginia's Energy Assistance Program (EAP) Fuel Assistance benefit for FFY2026: 26 parameters, 11 variables, 12 test files (124 cases), a programs.yaml entry and a changelog fragment. There are no comments on the PR yet. It touches no partner test file.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/va/dss/eap/
  • V/ = policyengine_us/variables/gov/states/va/dss/eap/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/va/dss/eap/

Sources read for this review:

Summary

What is right. The published FFY2026 parts are encoded correctly:

  • Income limits. floor(1.5 × 2025 guideline / 12) reproduces all 20 published monthly limits and the $687 step. I checked every size.
  • Point tables. Household size, income, climate zone, vulnerability and living arrangement all match.
  • Climate zones. All 133 localities match Appendix A, including Salem from the 01/03 page.
  • Value per point. The FFY2023–FFY2025 values match plan item 2.5.
  • Other rules. The $50 medical deduction, the eligible fuels, the subsidized and heat-in-rent rules, the homeless exclusion and the all-SSI waiver all match the manual and 22VAC40-680.
  • Tests. The tests are thorough. I hand-checked 15 cases below, and CI passed all 124.
  • Batch checks. The PR already uses total_unemployment_compensation (Integrate modeled state unemployment insurance into income flows #9558) and military_retirement_income (Wire military retirement pay and survivors into federal and state tax (#10181) #10182), and its comments pass the source-conflict ratchet on the merged tree.

There are no critical findings and five should-address items:

  • S1. Medicare premiums. Social Security and railroad retirement count gross. Manual exemption 15, the application and plan item 1.9 all exclude the Medicare premium. The PR says there is "no premium input", but medicare_part_b_premium exists, and the merged KY, NJ, OH, RI and SD LIHEAP formulas subtract it. Example: a 66-year-old with $25,000 of Social Security gets $0 at the head and $412.38 under the rule.
  • S2. Rental losses. A rental or farm-rent loss offsets wages. The unearned list is summed with signs, although the PR's own earned-income rule says losses do not offset other income. Example: $30,000 of wages and an $8,000 rental loss get $303.62 instead of $0.
  • S3. Placeholder points. The fuel and energy-burden placeholders (10 + 13 points for everyone) cannot reproduce the published benefit range.
    • Published. Plan item 2.5 prints FFY2025's maximum and minimum benefits, $634.43 and $181.26. These are exactly 140 and 40 points.
    • Model. The model's range is 60–123 points ($271.90–$557.39).
    • Regulation. 22VAC40-680-30 requires fuel type and energy burden to vary the benefit.
  • S4. Untested income sources. 15 of the 26 income-source entries never take a nonzero value in any test. They include tanf and total_unemployment_compensation.
  • S5. Axiom line. axiom: needed is not valid for a staff author, and there is no rulespec-us issue for Virginia EAP.

There are also six suggestions. They include adding Virginia's Auxiliary Grant (va_ag, merged in #10172) to the unearned list (item 17) and the heat-in-rent UNSPECIFIED fuel convention.

Status.

  • CI: 15 checks pass and 3 are still pending; none has failed. The Virginia EAP files passed in states-shard-4.
  • Branch: 7 commits ahead of main and 2 behind, and it merges cleanly.
  • Siblings: the 11 open LIHEAP sibling PRs conflict only in programs.yaml.

Critical (must fix)

None.

Should address

S1. Medicare premiums are not exempted: Social Security and railroad retirement count gross, although exemption 15 excludes the premium and medicare_part_b_premium exists

  • Where:
    • P/income/sources/unearned.yaml:16-19: "Exemption 15 lets the worker deduct Medicare premiums from Social Security or Railroad Retirement; there is no premium input, so these benefits are counted gross."
    • V/va_eap_countable_income.py:41-50 subtracts only the $50 disregard.
    • The PR body's not-modeled list says "the Medicare premium exemption has no input".
  • Sources:
    • Manual B.6.b.15 (p. 30) exempts "Medicare Premiums when deducted from Social Security or Railroad Retirement checks or when the household member is responsible for payment of the premium. NOTE: This is not an automatic exemption and therefore must be manually deducted from gross income. The client's statement is accepted as to the amount of the premium."
    • Manual C.2.c (p. 36): "Deduct the Medicare premium listed on the application from the preprinted income amount." C.1.a (p. 35) accepts the applicant's statement for the "Medicare premium".
    • Application question 10 (p. 2): "Does anyone pay for Medicare Part B ___ or D ___ insurance? ... How much? $___".
    • FY2026 plan item 1.9 (p. 6, rendered): "Social Security Administration (SSA) benefits" is checked, with "Excluding MediCare deduction" checked. The PR's own comment at unearned.yaml:4-5 reads the box the same way.
  • Why "no premium input" does not hold:
    • medicare_part_b_premium (person, annual) is on main and at this head's base. It holds the out-of-pocket Part B premium, net of Medicare Savings Program (MSP) coverage, and it is zero for non-enrollees.
    • Merged KY, NJ, OH, RI and SD LIHEAP already subtract it.
    • Netting MSP coverage is what exemption 15 asks for: a premium that MSP pays is neither withheld from the check nor paid by the member.
  • Effect (hand-traced from the code for 2026). A single person aged 66 in Fairfax County has $25,000 a year of Social Security and natural gas heat with a bill. Their income is $2,083 a month; after the $20 disregard that is above 135% of the guideline, so MSP pays nothing. The premium is 12 × $202.90 = $2,434.80.
    • Head: 25,000 − 600 = 24,400 > 23,472, so $0.
    • Exemption 15: 25,000 − 2,434.80 − 600 = 21,965.20 ≤ 23,472, so the household is eligible. 93.58% gives 3 income points, and 9 + 3 + 16 + 20 + 43 = 91 points gives $412.38.
    • Points change within the limit too. With $22,800 of Social Security, the head gives 94% (3 points, $412.38). The rule gives 84% (5 points, $421.44).
    • Reach. Medicare enrollees whose premium MSP does not cover, at incomes from about 135% of the guideline up to the limit plus the premium. The existing retiree cases appear to be MSP-covered in the model, so their expectations would likely not change; CI will confirm.
  • Severity: should. Wave 1 rated the same pattern should: Add New Jersey LIHEAP regular heating assistance #9745 S1 (New Jersey, also a "not wired in" comment) and Add New York HEAP regular heating assistance #9744 S1.
  • Fix:
    • Subtract each member's medicare_part_b_premium from gross income before the floor, either as a deductions list like Ohio's or in the formula.
    • Replace the comment at unearned.yaml:16-18. Keep Part D as a not-modeled row, since it has no separate input.
    • Add a case like the one above.
    • Correct the body row and the countable-income documentation.

S2. Rental and farm-rent losses offset wages and other income

  • Where:
    • V/va_eap_countable_income.py:41 sums p.income.sources.unearned with add(), keeping signs. That list includes rental_income and farm_rent_income (P/income/sources/unearned.yaml:31-32).
    • Only the earned entries are floored (lines 35-36). The household total is floored only at the end (line 50).
    • The PR's own rule, at P/income/sources/earned.yaml:6-9, says "each source is floored at zero in the formula so a business loss does not offset other income. Rental income is counted with the unearned sources."
  • Sources:
    • Rental property is self-employment. Manual C.5.a.4.b (pp. 38-39) lists "Rental property" among the self-employment profit determinations. C.5.a.3.b says "Profit from self-employment is the countable income."
    • Countable income is income received. C.5.a.3: "Countable income must be the combined total monthly income received by all members of the household." A loss is not income received.
    • Nothing authorizes netting. 22VAC40-680-30 scores "Gross monthly income", and no source lets a loss in one source reduce another.
  • Effect (hand-traced for 2026). A single person aged 40 in Henrico County has $30,000 of wages and an $8,000 rental loss, with gas heat and a bill.
    • Head: countable income is 22,000, or 93.73% of the limit: 3 income points, 9 + 3 + 12 + 0 + 43 = 67 points, $303.62.
    • With the loss floored: 30,000 > 23,472, so $0.
  • Tests: no case sets a nonzero rental_income or farm_rent_income.
  • Severity: should, as in Add New Jersey LIHEAP regular heating assistance #9745 S2 (New Jersey).
  • Fix:
    • Floor rental_income and farm_rent_income per person. Either move them into the floored earned-style loop or floor each unearned entry.
    • Add a rental-loss case like the one above.

S3. The flat fuel and energy-burden placeholders cannot reproduce the published benefit range, and the registry marks 2026 verified

  • Where:

    • P/points/primary_fuel.yaml (10 points) and P/points/energy_burden.yaml (13 points).
    • V/va_eap_points.py:86-94 adds both to every household.
    • The programs.yaml VA entry (line 965) records verified_years: "2026".
  • The rule:

    • 22VAC40-680-30: "Benefit levels shall be established based on income in relation to household size, fuel type, and geographic area, with the highest benefit given to households with the least income and the highest energy need." The section lists "Primary heat type" and "Energy burden" as point factors.
    • 22VAC40-680-10 defines energy burden as "the average fuel cost for the primary fuel type used by a household divided by the income of the household".
    • Appendix B (matrix p. 7) scores the burden from 0 to 25 points and gives the costliest fuel 20.
  • Published check (not in the PR). Plan item 2.5 prints each season's extremes:

    Season Source Maximum Minimum Value per point Points
    FFY2025 FY2026 plan, p. 9 $634.43 $181.26 $4.531665 exactly 140 and 40
    FFY2024 FY2025 plan, p. 9 $703.90 $198.28 $4.957075 142 and 40
    FFY2023 FY2024 plan, p. 9 $600.15 $185.96 $4.226444 142 and 44
  • What the model can produce:

    • The five published factors (size, income, zone, vulnerability, living arrangement) sum to between 37 (9 + 0 + 8 + 0 + 20) and 100 (15 + 25 + 20 + 20 + 20).
    • So in FFY2025 the fuel and burden points were at most 3 for the lowest-benefit household and at least 40 for the highest.
    • The model gives every household 23. Its range is 60–123 points, or $271.90–$557.39; integration Case 7 is the model maximum.
    • Size of the gap. The model understates the top by at least 17 points (about $77) and overstates the bottom by at least 20 points (about $91).
  • Only one input is missing. The burden is driven by household income, which the PR already computes. Only VDSS's average fuel cost per fuel is unpublished. A flat 13 removes the income gradient the regulation requires ("the highest benefit given to households with the least income").

  • Severity: should, as in Add Ohio HEAP regular heating assistance #9975 S3 (Ohio). There, the 2026 coefficients could not reproduce the printed FY2026 range, and the fix was a labelled estimate fitted to that range.

  • Fix: either of these.

    • (a) Model the burden. Compute the burden as a labelled, estimated average seasonal fuel cost divided by monthly countable income, banded per Appendix B. Keep fuel points by fuel type if they can be estimated. Calibrate both so that the model's extremes reproduce the published 40- and 140-point households. Add cases that pin $634.43 (140 × $4.531665) and $181.27 (40 points; see G6).
    • (b) Document the gap. Cite the plan 2.5 extremes in both parameter files, the body and the registry note, and state the model's $271.90–$557.39 range. Drop verified_years: "2026", or limit the note to eligibility and the published tables, so the registry does not present estimated benefit amounts as verified.

S4. 15 of the 26 income-source entries have no test that would fail if they were dropped

  • Where: P/income/sources/earned.yaml and P/income/sources/unearned.yaml.
  • The untested entries. No case gives these a nonzero value:
    • Earned: sstb_self_employment_income, farm_operations_income and partnership_s_corp_income.
    • Unearned: total_unemployment_compensation, strike_benefits, railroad_benefits, retirement_distributions, survivor_benefits, general_assistance, tanf (every case sets tanf: 0), rental_income, farm_rent_income, alimony_income, workers_compensation and disability_benefits.
  • The tested entries: wages, self-employment, Social Security, SSI, pensions, military retirement, child support, cash contributions, veterans' benefits, interest and dividends.
  • Why it matters: this is checklist item 13. total_unemployment_compensation is the Integrate modeled state unemployment insurance into income flows #9558-guarded entry, and tanf is the take-up-gated aggregate the comment describes; neither is pinned.
  • Severity: should, as in Add Alabama LIHEAP regular heating assistance #9967 S3 (Alabama) and Add Oklahoma LIHEAP regular heating assistance #9970 S6 (Oklahoma).
  • Fix: add one countable-income case that sets every listed source to a distinct amount, as Alabama's Case 18 does. Include a positive tanf and reported unemployment compensation. The S2 rental-loss case can sit beside it.

S5. axiom: needed is not a valid line for a staff author

  • Where: the PR body's last line.
  • The rule: CLAUDE.md "Axiom Parity" reserves axiom: needed for external contributors. Ziming is staff.
  • The issue state:
    • rulespec-us has no Virginia EAP issue. I searched titles and the open pe-parity list.
    • rulespec-us main (5796861e58) has no Virginia EAP module: us-va/policies/ holds only cms and income_tax.
  • Precedent. The wave 2a siblings and this PR's wave 1c companions use queued lines: rulespec-us Add Oregon integration test #1532 (Alabama) through Remove complex pass-through reform #1540 (Ohio), Updated Oregon tax parameters for 2022 #1833 (Arizona, PR Add Arizona LIHEAP Standard benefit #10293) and Too many EITCs #1834 (Iowa, PR Add Iowa LIHEAP regular heating assistance #10292). Each is open, labelled pe-parity and dispatch-ready.
  • Fix:
    • File a dispatch-ready, pe-parity-labelled rulespec-us issue in the Too many EITCs #1834 format. It needs:
      • module paths and corpus status;
      • the verbatim 22VAC40-680-10, -20 and -30, manual B.5–B.7, C.5, D.4–D.5 and Appendices A–B, and FY2026 plan items 1.9, 2.1 and 2.5;
      • the required outputs;
      • companion tests taken from the manual tables and published limits, not from PE output;
      • the blockers.
    • Change the line to axiom: TheAxiomFoundation/rulespec-us#<n> queued.
    • Update the issue for S1–S3 once they are settled.

Suggestions

G1. Count Virginia's Auxiliary Grant (va_ag, checklist item 17).

  • What exists on main. Add Virginia state supplementation for 2026 #10172 (merged, and already in this head's base) added va_ag, Virginia's state supplement. It lists va_ag in the SNAP and HUD unearned lists, household_state_benefits and spm_unit_benefits. The EAP unearned list omits it.
  • The rule counts it.
    • Manual B.5.a (p. 25) names "auxiliary grants to the aged, blind and disabled" as public assistance.
    • None of the 27 exemptions covers it.
    • Exemption 25 follows SNAP, which counts it.
  • Example (hand trace, 2026). A person aged 70 in Norfolk gets the full $994 of SSI. They are certified for an Auxiliary Grant in supportive housing (outside Northern Virginia) and have a heating bill.
    • The grant. The care rate is 994 + 1,136 = $2,130. The need is $2,130 + $87 = $2,217, so the grant is $2,217 − $994 = $1,223 a month.
    • Head: 11,328 is 48% of the limit: 15 income points, 95 points, $430.51.
    • Counting va_ag: 26,004 is 111%. The all-SSI waiver keeps the household eligible, at 0 income points: 80 points, $362.53.
  • Why only a suggestion:
  • Fix: add va_ag beside ssi.

G2. A heat-in-rent household with UNSPECIFIED fuel.

  • The input convention. main's heating_type documentation says UNSPECIFIED "is also the right value when heat is included in rent and the fuel is unknown". The PR makes that household ineligible (V/va_eap_eligible.py:37-38; eligibility Case 14).
  • The rule. B.5.c.1.b admits unsubsidized heat-in-rent households without listing a fuel; only D.1.b asks the household to designate one.
  • Why it can be changed. The model's points do not depend on the fuel. The merged AL, OH and OK formulas treat UNSPECIFIED as eligible.
  • Fix: consider eligible_fuel | (heat_in_rent & UNSPECIFIED), or record the choice against the input convention in the body.

G3. Backfilled years (unclaimed and documented, as in NJ S3).

  • The income rate. P/eligibility/fpg_rate.yaml starts at 1.5 on 2021-05-01 and carries back. The 01/03 Appendix B (p. 65) bases the levels on "130% of the Poverty Income Guidelines".
  • The value per point. The FFY2023 value carries back to 2015–2022, and FFY2022 was an ARPA year.
  • Fix: consider a 2003-01-01: 1.3 entry, if an older plan confirms it, or keep the backfill and keep the body note.

G4. References polish.

  • The nine 22VAC40-680 entries point to the whole-chapter page. Section URLs (.../chapter680/section10/, section20/, section30/) would anchor the cited text.
  • If S3 takes option (b), add the plan 2.5 extremes as a reference.

G5. Optional tests.

  • A mixed-state case. No va_eap case runs a Virginia and a non-Virginia SPM unit in one simulation, as Alabama's Case 6 does. It is safe today: va_eap_climate_zone defaults to TIDEWATER, a valid key in p.climate_zone[zone]. A mixed case would keep it pinned (item 15).
  • The default zone. A Virginia household with an unlisted county would pin the TIDEWATER default of the select.

G6. Cent truncation. The printed benefits truncate to the cent rather than round:

  • 40 × 4.531665 = 181.2666 is printed as $181.26.
  • 142 × 4.226444 = 600.155 is printed as $600.15.

va_eap does not round, so the difference is under one cent. Flooring to the cent is optional.

Verified correct

Values against sources:

  • Income limits (manual B.6.a p. 28, Information Sheet 05/25, matrix p. 2). floor(1,956.25 + 687.5 × (n − 1)) gives 1,956, 2,643, 3,331, 4,018, 4,706, 5,393, 6,081, 6,768, 7,456, 8,143, 8,831, 9,518, 10,206, 10,893, 11,581, 12,268, 12,956, 13,643, 14,331 and 15,018. That matches all 20 published sizes. Above 20 the step is +687 (floor 687.5).
  • The lag. The one-year guideline lag matches "new income limits ... in June of each year" (matrix p. 1). fpg() reads the guideline at 2025-01-01 for period 2026.
  • Point tables (matrix pp. 6-7, rendered):
    • size: 9 / 12 / 15;
    • income: the ten bands from 25 down to 1;
    • zones: 20 / 18 / 16 / 13 / 12 / 8;
    • vulnerability: 20 / 15 / 12, not cumulative;
    • living arrangement: codes A, C, E, G and P at 20.
    • The 05/21 page says "150%", and the PR correctly sets aside the manual's 01/03 copy (130%).
  • Climate zones (matrix pp. 3-5 and manual pp. 62-64): all 133 localities (95 counties and 38 cities) match. Salem comes from the 01/03 page, Bedford City is a town since 2013, and Clifton Forge is a town since 2001. Climate-zone Case 10 pins all 133.
  • Value per point:
    • The three values match plan item 2.5 in FY2024, FY2025 and FY2026, each on p. 9.
    • Period 2026 carries the FFY2025 $4.531665. That is the latest published value, as disclosed.
    • No FY2027 plan or matrix is posted: the Clearinghouse returned 404 on 2026-10-21, and the VDSS page still links the 05/25 Information Sheet.
  • Eligibility:
    • Fuels and expense: the seven fuels (B.5.c.1.a, p. 26; application question 12), heat in rent for unsubsidized households (B.5.c.1.b), and subsidized households paying their own bill (B.5.c.1.c).
    • Exclusions: subsidized households with heat in rent (B.5.c.2.a; plan 2.3, p. 8), homeless households (B.5.c.2.g).
    • Income: the at-or-below test (22VAC40-680-20(B)(1)) and the all-SSI exception (B.6.a).
    • Not required: there is no asset test (plan 2.3), no categorical eligibility (plan 1.4 "No", rendered p. 5) and gross income applies (plan 1.8).
  • Income:
    • The deduction. $50 a month per elderly (60+) or disabled member, once per person (B.6.c, p. 31). The disability definition matches 22VAC40-680-10. Using is_disabled for railroad disability and Medicaid permanent-and-total certification follows merged Ohio and Florida.
    • Exempt earnings. Members under 18 (exemption 20; plan 1.9 box unchecked).
    • Household size. Ineligible noncitizens are left out of the size, but their income counts (B.7, p. 31; C.5.a, p. 41).
    • Gross wages (C.5.a.3.a).
    • Interest, dividends and cash contributions are counted under the manual, against the unchecked plan boxes. That is documented and defensible: C.5.a.4.c names "cash contributions", and exemption 19 excludes only one-time gifts.
  • Benefit: points × value per point (22VAC40-680-30(4)). There is no minimum or maximum other than the points.

Hand-checked expectations (15 cases). At $4.531665 a point and 43 points of living arrangement, fuel and burden:

  • Integration Case 1: 11,736 − 600 = 11,136, or 47.44%: 15 points. Fairfax is Northern (16). 9 + 15 + 16 + 20 + 43 = 103, so $466.76.
  • Integration Case 2: 26,562 + 3,600 = 30,162 against 39,972, or 75%: 8 points. Norfolk is Tidewater. 12 + 8 + 8 + 12 + 43 = 83, so $376.13. Heat in unsubsidized rent is eligible.
  • Integration Case 3: the 16-year-old's $4,000 is exempt. 52,000 against 72,972 is 71%: 8 points. 15 + 8 + 12 + 12 + 43 = 90, so $407.85.
  • Integration Case 4: 18,960 − 600 = 18,360, or 78%. The subsidized household with its own bill is eligible. 9 + 8 + 12 + 15 + 43 = 87, so $394.25.
  • Integration Case 5: SSI 438 (994 − 957.50 = 36.50 a month). 24,438 − 600 = 23,838, or 101%: 0 points. All-SSI keeps it eligible. 9 + 0 + 18 + 15 + 43 = 85, so $385.19.
  • Integration Case 6: size 2 (undocumented parent excluded). 25,000 against 31,716 is 78%. 9 + 8 + 16 + 12 + 43 = 88, so $398.79.
  • Integration Case 7: 11,400 against 64,716 is 17%: 25 points. Augusta is Central Mountain. 15 + 25 + 20 + 20 + 43 = 123, so $557.39 (the model maximum; S3).
  • va_eap.yaml Case 5 (2025): floor(15,060 × 1.5 / 12) = 1,882, or 22,584. 12,600 is 55%: 13 points. 9 + 13 + 12 + 20 + 43 = 97, so $439.57.
  • va_eap.yaml Case 7: the −60,000 loss floors to 0. 15,000 is 63%: 10 points, 74 points, $335.34.
  • va_eap.yaml Case 8: military retirement of 18,000 is 76%: 8 points, 76 points, $344.41.
  • Income-limit Cases 1 and 4: all listed sizes, including 21 (188,460) and 22 (196,704). For 2025, sizes 1–4 give 1,882, 2,555, 3,227 and 3,900; size 20 gives 14,660; size 21 adds 672.
  • Income-points Cases 1–2: 37,691 / 188,460 = 19.99947%, rounded to four places and floored, gives 19, so 25 points. Each exact band start lands in its band.
  • Countable Case 19: 9,600 (SBP annuity in military_retirement_pay_survivors) + 12,000 − 600 = 21,000.
  • Income-eligible Case 9: the couple's SSI is 17,892 − (34,000 − 240 − 780) / 2 = 1,402. 34,000 + 1,402 − 1,200 = 34,202 > 31,716, but both members receive SSI, so eligible.
  • Eligibility Cases 13–17: heat in unsubsidized rent is eligible. A voucher or public-housing household with heat in rent is not. A subsidized household with its own bill is.

Batch checks:

  • Integrate modeled state unemployment insurance into income flows #9558: the list reads total_unemployment_compensation. The guard's four tests passed in CI (Rest shard-1). It is unpinned by any case (S4).
  • Wire military retirement pay and survivors into federal and state tax (#10181) #10182 (item 16): the list reads military_retirement_income, and countable Case 19 pins the survivor input.
  • Document disjoint health premium inputs and correct consumer treatment #10046 (item 14): not applicable to payroll premiums. Virginia counts gross wages from employment_income and deducts no health premiums, apart from Medicare (S1). ssi counts the actual benefit.
  • State-gated keys (item 15): safe.
    • va_eap_climate_zone is an enum gated to Virginia whose default, TIDEWATER, is a valid key in p.climate_zone[zone].
    • va_eap_household_size (int, default 0) feeds only .calc() brackets and arithmetic.
    • The Microsimulation job passed. A mixed-state test is optional (G5).
  • Federal 110% floor: never binds. The limit uses the same lagged guideline as gov.hhs.liheap.fpg_floor, and floor(1.5g / 12) × 12 ≥ 1.5g − 12 is about 1.36 times 1.1g at every size. For size 1 in 2026 that is $23,472 against $17,215; above 20 members, +$8,244 a year against +$6,050. Not wiring the floor matches Alabama and Apply the federal 110% poverty floor to state LIHEAP programs (DC, MA, MS, NJ, OR) #10279's treatment of 150% states.
  • IIJA (item 12): Virginia pays no separate supplement; the value per point comes from the whole allocation.
  • Source-conflict ratchet (Tie source-conflict markers to the register shared with Axiom #10287):
    • I ran the comment readers and CONFLICT_COMMENT_RE from main's test_source_conflicts.py over the PR's 37 new parameter and variable files. They give 0 matching lines and no marker attempts. A control file (Florida's base_amount.yaml) gave its expected 1.
    • In CI, test_conflict_sounding_comments_do_not_grow passed on the merge with main at a6fafb06c2. main's two newer commits touch no code-health file.
  • Wiring:
    • Registry. The programs.yaml VA entry is in order after TX, and VA is added to the coverage line. test_programs_registry passed.
    • Aggregates. No aggregate wiring, which matches every merged LIHEAP state.
    • Gating. defined_for = StateCode.VA is on 10 variables, and va_eap is gated by va_eap_eligible.
    • Take-up. None beyond tanf's.
    • Changelog. changelog.d/va-liheap-wave1c.added.md is top level and typed added.
  • Descriptions (item 6): all 26 end "under the Energy Assistance Program", Virginia's program name, as Ohio's end "under the Home Energy Assistance Program".
  • References (item 7): every anchor I opened shows its value:
    • matrix pp. 1, 3-7;
    • plan pp. 5, 6, 8 and 9;
    • manual pp. 26, 28, 30, 31, 52 and 62-66;
    • application pp. 1-2.
    • Multi-page titles use #page 28,38-40 and #page 31,52, and variable tuples use # PDF pages comments.

Checklist results (LIHEAP batch):

  1. State plan and manual: pass for every published table. The value per point is the latest published; S3 covers the placeholders.
  2. Income definition: fail on two items. Medicare premiums are not exempted (S1), and rental losses net (S2). Gross wages and Social Security otherwise follow the manual.
  3. Backdating: pass for 2026. Earlier years are documented backfill (G3), and no FY2027 table is published.
  4. Household and arrangement rules: pass. Every eligibility branch has a test; heat in rent with UNSPECIFIED fuel is G2.
  5. Tests: 124 cases, 15 hand-checked, periods plain. Source coverage is S4.
  6. Descriptions: pass.
  7. References: pass (polish in G4).
  8. Wiring: pass.
  9. Axiom: invalid (S5).
  10. Branch and CI: a clean merge; 15 pass and 3 pending, none failed.
  11. Integrate modeled state unemployment insurance into income flows #9558: pass (pinning in S4).
  12. IIJA / FY2027: not applicable; nothing published for FY2027.
  13. Mutation check: every eligibility, limit, points and vulnerability branch is pinned. 15 source entries are not (S4).
  14. Document disjoint health premium inputs and correct consumer treatment #10046: not applicable (gross wages, no premium deduction other than S1).
  15. State-gated index defaults: safe (default key valid).
  16. Wire military retirement pay and survivors into federal and state tax (#10181) #10182: pass.
  17. Merged state SSPs: va_ag is missing (G1).
  • Federal 110% floor: never binds.

Axiom line assessment

Invalid: axiom: needed (S5).

  • The rule. CLAUDE.md allows needed only for external contributors; the author is staff.
  • No issue exists. rulespec-us has no issue for Virginia EAP, and main (5796861e58, 2026-10-20) has no us-va EAP module.
  • The companions are queued. Updated Oregon tax parameters for 2022 #1833 (Arizona) and Too many EITCs #1834 (Iowa) are open and labelled pe-parity. Each records its blockers (sources missing from the corpus, an unchecked signed release, encoder approval) and its companion tests.
  • What the new issue needs. File the Virginia issue in that format, matched to the head after S1–S4 are settled. Its required outputs should name medicare_part_b_premium (S1) and the per-source floors (S2). Its interpretations should record the placeholder or estimated fuel and burden points (S3), the plan-versus-manual choice for interest, dividends and cash contributions, and Salem's zone.

CI status

15 pass, 3 pending, 0 failed. Run 39635445749 tests merge commit 9f707dcb, which merges head 1396baf7fe into a6fafb06c2 (pull_request, started 2026-10-21 17:24 UTC). I re-checked gh pr checks 10303 at the end of this review.

  • Passed: all four states shards, household, irs, ssa-usda, rest-a and rest-b baseline shards, Rest (Python, shard-1), Microsimulation, Household API Partners, PR validation, Package and compatibility, and Quick Feedback.
  • Still pending: Baseline contrib-hhs, Baseline reform, and Rest (Python + variables). None of them runs the Virginia EAP files.
  • Where the Virginia tests ran. Quick Feedback deferred to the full suite: "12 files > 8" changed test files. States-shard-4 (batch 12/13) ran all 12 Virginia EAP files: 7 + 8 + 10 + 19 + 10 + 23 + 6 + 9 + 6 + 7 + 7 + 12 = 124 passed.
  • Guards. Rest shard-1 passed:
  • Known main-caused failure. Not applicable: the run started after 13:24 UTC, and the ratchet passed.
  • Locally: I ran no tests or probes. The examples in S1–S3 and G1 are hand traces of the code at the head.

Branch status

0 critical, 5 should and 6 suggestions are open at this head.

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The FFY2026 limits, every Appendix B point table, all 133 climate zones and the eligibility rules check out, and #9558, #10182 and the ratchet pass. Five should-address items: (1) Social Security and RRB count gross, though VA excludes Medicare premiums (age 66 with $25k SS: $0 vs $412.38); (2) rental and farm-rent losses offset wages, against the PR's own no-offset rule; (3) the flat fuel and burden placeholders don't reproduce plan 2.5's range; (4) 15 of 26 income sources are never exercised in a test; (5) axiom: needed is invalid for staff, so file a pe-parity issue and mark it queued. Six suggestions. Full report in the comment above.

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.

Implement Virginia Energy Assistance Program Fuel Assistance (LIHEAP)

2 participants