Skip to content

Count property tax and rent refunds paid outside the income tax in household and SPM benefits - #10725

Merged
MaxGhenis merged 5 commits into
mainfrom
nd-renters-refund-net-income
Nov 9, 2026
Merged

MaxGhenis merged 5 commits into
mainfrom
nd-renters-refund-net-income

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

North Dakota pays its renter's refund on its own application (SFN 24777), outside the income tax, yet the refund reached no household net income, SPM resources or poverty measure. nd_renters_refund sat only in state_property_tax_credits, which feeds taxsim_state_property_tax_credit and its deprecated alias and nothing else. Five other members of that list had the same gap: Minnesota's homestead credit refund, Montana's property tax rebate, Pennsylvania's property tax or rent rebate, Vermont's renter credit and the renter part of Utah's homeowner's and renter's relief.

Changes

  • gov.states.household.state_property_tax_refunds lists the six cash refunds by year, and the tax-unit variable state_property_tax_refunds adds them. Each refund enters in the year it enters state_property_tax_credits, so the new list stays inside that one in every year. The list opens with an empty block on that list's first date (1977). Without it, the model would copy the 2021 block back to 2015, its first modeled year, and count the Vermont renter credit it computes for 2015 to 2020 from its 2021 parameters.
  • household_state_benefits (2023 to 2026 blocks) and spm_unit_benefits, with its four reform copies, count state_property_tax_refunds, so each refund raises both net incomes by the same dollar.
  • ut_homeowner_renter_relief now adds two new variables. The Tax Commission pays ut_renters_credit from the General Fund (Utah Code 59-2a-205(5)), and counties apply ut_homeowners_credit against property taxes accrued (59-2a-302(2)(b) and 59-2a-304). The total keeps its place in state_property_tax_credits, and its amounts don't change.
  • vt_renter_credit now pays only tax units that paid rent. It gave every low-income Vermont tax unit a credit based on fair market rent, homeowners included, and only the TAXSIM comparison read that amount until this PR. Vermont gives the credit to a claimant "who rented the homestead" and requires six calendar months of renting (32 V.S.A. 6066(b)(1) and (c)(3)). The model has no months-rented input, so the new vt_renter_credit_eligible tests for rent above zero. A tenant whose subsidy covers all the rent gets nothing, because Vermont pays a subsidized tenant a share of the gross rent paid under (b)(1)(D). The 28 YAML cases that set rent: 0 while checking the credit amount now set rent, with the same expected amounts.
  • vt_renter_credit now runs for every household. Counting it in net income puts its formula on every household's path, and core runs a formula over the whole population whenever any row passes defined_for. The formula indexes tables by tax unit size and county, so tax units of nine or more and counties outside Vermont's tables raised ParameterNotFoundError. This PR takes the Vermont lookup guard from Guard county-keyed lookups in VT renter credit, MN CCAP, WI Shares and MD CCS #10116 word for word. Tax units above eight people use the eight-person values, as the Department of Taxes' calculator does (IF(Household_Size>8,8,Household_Size)), and a county outside Vermont's tables falls back to Addison. Vermont adopts HUD's limits in 32 V.S.A. 6061(19) and (20), and HUD adjusts them for larger families under 42 U.S.C. 1437a, so the eight-person cap stands in until the model encodes HUD's schedule.

2026 impact

We estimate these refunds add $525.8 million to household net income and to SPM net income in 2026, on the default Populace release (populace-us-2024-spm-20260909). Main raises an error when it computes vt_renter_credit for every household, so our baseline adds only the Vermont lookup guard to main at 9960edee31, and the branch at dc5083e7da runs against it. Main's net income never reads that variable, so the baseline's net income matches main's.

Net income change Households gaining Sample households Mean gain
United States $525.8M 556,700 244 $945
Minnesota $340.0M 245,100 123 $1,387
Pennsylvania $142.7M 258,000 60 $553
Vermont $28.2M 24,100 35 $1,168
Utah $9.1M 18,700 5 $487
North Dakota $5.7M 10,700 21 $534
Montana $0.0M 0 0 $0

Each household's and SPM unit's net income moves by its refunds to within $0.02, the rounding of the model's 32-bit totals. No household's net income falls, market income doesn't change, and every other refund and bill credit keeps its baseline amount. marginal_tax_rate reads household_net_income, so each income-tested refund's phase-out now shows in household marginal tax rates.

We estimate SPM poverty stays at 13.75% nationally and doesn't change at two decimal places in any of the six states. The sample holds 245 SPM units with a refund, 36 of them in poverty, and the one that crosses the line has a weight under 0.001. We estimate national deep poverty falls by 1,811 people, all from one Vermont record.

None of the six programs has a take-up variable, so the model pays every eligible tax unit. North Dakota's refund, Pennsylvania's rent rebate, Utah's renter credit and Vermont's credit read rent, and this release predates PolicyEngine/microcosm#1444, which places each rent draw on its own household's head. For example, we estimate 19% of Vermont's renter tax units and 16% of its owner tax units carry rent, and owner tax units receive 59% of Vermont's credit.

We estimate vt_renter_credit at $28.2 million for 24,100 tax units with the rent test and $178.3 million for 166,700 without it, an amount only the TAXSIM comparison read. The Department of Taxes reports 12,010 recipients and $7.2 million for the credits it paid in 2024 on 2023 income, and our 2026 estimate exceeds those by 101% and 290%. The Vermont lookup guard moves no Vermont amount in this release: every Vermont household has a Vermont county, and the largest Vermont tax unit has seven people.

In Act 169, Vermont raised its rate to 12.5% and its cap to $3,250 for claim year 2027, when it takes claims on 2026 income under 32 V.S.A. 6061(9) and (12). Main applies them to 2027, so the Vermont figure here uses the 10% rate and the $2,500 cap.

Every member of state_property_tax_credits

Route Member Basis
Cash refund, now in both benefit totals nd_renters_refund N.D.C.C. 57-02-08.1(2)(b) and (d): the Tax Commissioner pays the refund
mn_homestead_credit_refund Minn. Stat. 290A.07, subd. 1, and the M1PR instructions: the commissioner pays claims by check or direct deposit
mt_property_tax_rebate Montana DOR rebate report (May 2024) and SB 542 sec. 3: the department pays the rebate on its own claim; $0 from tax year 2025
pa_property_tax_or_rent_rebate PA-1000 booklet, Line 20: the Department of Revenue deposits or mails the rebate
vt_renter_credit Vermont pays the credit on Form RCC-146, and Schedule IN-112 leaves it out of the refundable credits it carries to Form IN-111
ut_renters_credit, part of ut_homeowner_renter_relief Utah Code 59-2a-205(5)
Property tax bill reduction, in neither total nd_primary_residence_credit N.D.C.C. 57-02-08.10(1): the county auditor applies it on the property tax statement
dc_senior_disabled_property_tax_relief D.C. Code 47-863(b)(1): a 50% deduction in computing real property tax liability
in_blind_disabled_property_tax_credit, in_over_65_property_tax_credit IC 6-1.1-51.3-0.5 (SEA 1, 2025): applied to property tax liability
in_supplemental_homestead_credit IC 6-1.1-20.6-7.7(e): the county auditor applies it to property tax liability
ut_homeowners_credit, part of ut_homeowner_renter_relief Utah Code 59-2a-302(2)(b) and 59-2a-304
State income tax credit, already in both net incomes az_property_tax_credit, ct_property_tax_credit, dc_ptc, il_property_tax_credit, ma_senior_circuit_breaker, me_property_tax_fairness_credit, mi_homestead_property_tax_credit, mn_renters_credit, mo_property_tax_credit, mt_elderly_homeowner_or_renter_credit, nj_property_tax_credit, nm_property_tax_rebate, ny_real_property_tax_credit, ok_ptc, ri_property_tax_credit, wi_homestead_credit, wi_property_tax_credit, wv_homestead_excess_property_tax_credit Each state's refundable or non-refundable credit list, or mt_refundable_credits and nj_refundable_credits

Neither net income total subtracts property tax, so a credit that lowers the bill moves neither. household_net_income counts utility bill discounts (CARE, FERA, EZ-SAVE, SHARE) as benefits, and we leave open whether property tax bill credits should follow.

Invariants

Tests execute each of these for every input they draw:

  1. Each member of state_property_tax_credits takes exactly one route in every year from 2023 to 2026, and the state lists named for income tax credits hold them (test_every_property_tax_credit_has_one_documented_route, test_property_tax_credit_routes_have_no_stale_entries). state_property_tax_refunds holds nothing outside state_property_tax_credits in any year either list changes (test_refunds_stay_inside_the_credits_list_in_every_year).
  2. At $500,000 of wages, each earner owes enough state income tax to use a non-refundable credit in full. There, a dollar of any member moves household_net_income and spm_unit_net_income by a full dollar for a refund or income tax credit and by nothing for a bill reduction. test_property_tax_credits_move_both_net_incomes_together draws the amount with Hypothesis and runs one simulation with a pair of households for every member.
  3. A dollar of a cash payment adds a dollar to SPM resources and to household market income plus benefits. test_a_dollar_of_cash_adds_a_dollar_to_both_totals draws the payment from CASH_PAYMENTS, which now holds the six refunds.
  4. Utah's two parts add to its total, and the benefit lists still differ only where HOUSEHOLD_ONLY and SPM_ONLY document it. Every reform copy of spm_unit_benefits keeps state_property_tax_refunds (SHARED_CASH_PROGRAMS in test_aggregate_list_integrity.py). The same file checks that each member of the new list names a defined variable with a state gate, and that no later year block drops a member.
  5. Vermont pays nothing to a tax unit with no rent, in any county, family size or income the test draws, and an unsubsidized renter's credit stays the same at any rent above zero (test_a_household_that_pays_no_rent_gets_no_credit, test_unsubsidized_credit_does_not_depend_on_rent_paid).

New YAML cases in gov/states/tax/property/state_property_tax_refunds.yaml put each refund in both totals, run North Dakota's worked example, start Pennsylvania's rebate in 2025 and Vermont's renter credit in 2021, split a Utah owner who also rents, and keep North Dakota's and Indiana's bill credits out of both totals. vt_renter_credit_eligible.yaml covers a renter, a homeowner, a tenant whose subsidy covers all the rent, a couple where the spouse pays the rent, and a renter outside Vermont. vt_renter_credit_lookups.yaml runs out-of-state, unknown-county and nine-or-more households in one simulation. Utah's relief cases now assert both parts.

Related PRs

Flaky test on main

test_cash_income_never_lowers_spm_net_income can fail on main when Hypothesis draws Connecticut with a small amount, because $1 of partnership income at $500,000 of wages raises Connecticut income tax by $50.07 through a recapture step.

axiom: TheAxiomFoundation/rulespec-us#1810 queued (32 V.S.A. 6066(b)(1) and (c)(3) rent test); TheAxiomFoundation/rulespec-us#1678 queued (household sizes above the eight-person tables); n/a for the refund lists, Utah's split and the county fallback: aggregation and lookup plumbing, no provision's rules change

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits November 9, 2026 04:37
…usehold and SPM benefits

North Dakota's renter's refund sat only in state_property_tax_credits, which
feeds the TAXSIM comparison and nothing else, so it reached no household net
income, SPM resources or poverty measure. Five other members of that list
had the same gap: Minnesota's homestead credit refund, Montana's property tax
rebate, Pennsylvania's property tax or rent rebate, Vermont's renter credit
and the renter part of Utah's homeowner's and renter's relief. Each state
pays these on its own claim, outside the income tax return.

A new year-gated list, state_property_tax_refunds, holds the six, and a
tax-unit total of the same name adds them. household_state_benefits and
spm_unit_benefits (with its four reform copies) count the total, so both net
incomes move by the same dollar.

Utah's relief splits into ut_renters_credit, which the Tax Commission pays,
and ut_homeowners_credit, which counties apply against the property tax bill
(Utah Code 59-2a-205(5) and 59-2a-302(2)(b)). The other bill reductions in
the list (North Dakota's primary residence credit, DC's senior and disabled
relief and Indiana's three credits) stay out of both totals, as no net
income total subtracts property tax. The 18 remaining members already reach
both totals through state income tax credit lists.

The parity test gains invariant 7: every member of state_property_tax_credits
has exactly one documented route, and a differential simulation over all of
them checks that both net incomes move together, by the full amount for a
refund and not at all for a bill reduction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new test walks every year either list changes, from the refund list's
first block, and fails when a refund enters state_property_tax_refunds
before it enters state_property_tax_credits. A copy of the list with
Pennsylvania's rebate in the 2024 block fails it at 2024.

The list's comment no longer says each refund enters in the first year PE-US
defines its parameters: North Dakota's cap and income limit start in 2009,
and Montana's rebate amount in 2021 at zero. It now says that years before
2021 take the 2021 block, as household_state_benefits' first block covers
the years before it, and that policyengine-core reads an empty block as no
list, which breaks adds. Vermont's comment names Schedule IN-112.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the whole dollar

cc9a076 dropped the empty first block from state_property_tax_refunds and
said policyengine-core reads an empty block as no list. That was wrong. An
empty list block works with adds. The crash came from my own test loop,
which started at 1976, before any block and before PE-US copies parameters
back to 2015, so the parameter returned None.

Without the block, PE-US copied the 2021 block back to 2015 and counted the
Vermont renter credit that the model computes for 2015 to 2020 from its 2021
parameters. The list now opens with an empty block on 1977-01-01, the first
date of state_property_tax_credits, and a YAML case keeps the 2020 credit out
of both benefit totals.

test_property_tax_credits_move_both_net_incomes_together let an income tax
credit lose part of its value to federal tax through the SALT deduction.
Each of the 18 income tax credits moved both totals by the full amount, at
$500 and $4,321, so the test now requires the full amount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vt_renter_credit had no renter test, so it gave every low-income Vermont tax
unit a credit based on fair market rent, homeowners included. Only the TAXSIM
comparison read the amount until this branch counted the credit in benefits.
The AABD integration case then showed the result: $1,324 more in
household_state_benefits for a person who pays no rent.

32 V.S.A. 6066(b)(1) gives the credit to "an eligible claimant who rented the
homestead", and 6066(c)(3) requires at least six calendar months of renting.
vt_renter_credit_eligible applies the rent test. PE-US has no months-rented
input, so rent paid stands in for the six-month test.

PE-US #10044 adds the 6066(c)(2) dependent bar with its joint-return reading,
so this commit leaves that bar to it.

The YAML cases and department-table tests set rent to 0 while checking the
credit amount. They now set rent, and new cases and properties cover a tax
unit that pays no rent and a tenant whose subsidy covers all the rent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Counting the credit in net income puts its formula on every household's path.
Core runs a formula over the whole population whenever any row passes
defined_for, and the formula indexes tables by tax unit size and county. A 2026
microsimulation raised ParameterNotFoundError on a tax unit of nine, and a
county outside Vermont's tables raises the same way.

This takes the Vermont guard from #10116 word for word: tax units above eight
people use the eight-person values, and a county outside Vermont's tables falls
back to Addison. The department's 2025 calculator looks up
IF(Household_Size>8,8,Household_Size) and accepts family sizes 1 to 10. The
fallback changes nothing for a household in a Vermont county.

#10116's four Vermont YAML cases move to vt_renter_credit_lookups.yaml with
rent set, since this branch gives no credit to a tax unit that paid no rent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis force-pushed the nd-renters-refund-net-income branch from 41ec5a8 to dc5083e Compare November 9, 2026 09:37
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 9, 2026 10:08
@MaxGhenis
MaxGhenis merged commit dcc3d42 into main Nov 9, 2026
43 checks passed
@MaxGhenis
MaxGhenis deleted the nd-renters-refund-net-income branch November 9, 2026 11:28
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

This PR needed admin rights to merge at dc5083e7da, because the repo requires one approving review and GitHub doesn't let an author approve their own PR. All 36 jobs in run 42598692963 passed, and the three skipped lines in gh pr checks come from the draft run of the same commit. An independent Claude Opus 5.5 reviewer agreed with the code and this description at that head.

MaxGhenis added a commit that referenced this pull request Nov 9, 2026
#10725 added the refund to household and SPM benefits, so the income
changes in this pull request now reach both net incomes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis pushed a commit that referenced this pull request Nov 9, 2026
GitHub reported the branch as conflicting with main. Both conflicts keep
both sides:

- repeal_state_dependent_exemptions.yaml: main's three California repeal
  cases from #10053 follow this branch's WV, WI and RI cases.
- vt_renter_credit.py: main's #10725 added the rent test, the household
  size cap and safe_county. The fair market rent now indexes on this
  branch's exemption count and main's safe_county, while the income
  limits keep main's capped size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 9, 2026
…ome and the CBO measures

#10725 added state_property_tax_refunds to household benefits. Appendix A
leaves out tax refunds, and CBO's lists name no state tax relief, so both
concepts leave the refunds out, as they do New Jersey's ANCHOR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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