Skip to content

Count disability benefits and 2020 excluded unemployment compensation in Montana's elderly homeowner/renter credit income - #10163

Merged
MaxGhenis merged 15 commits into
mainfrom
mt-elderly-credit-disability-unemployment
Oct 13, 2026
Merged

MaxGhenis merged 15 commits into
mainfrom
mt-elderly-credit-disability-unemployment

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Merged latest main at d965f222309f6da0a23c6879ff7404446c1d5ed4 after #10144 squash-merged as 7b7b418cbc, using merge commit b2259be4dd6a8e1331ac1a99d2faa13241c969dc and preserving the initial main merge 102e522dab4bb7ed29f096d7d1a97a78aba41922. The final gh/main..HEAD diff matches the approved f9d8d64b53..32fc16d292 delta byte-for-byte: 6 files, including this PR's changelog. #10144's resurrected changelog fragment was removed.

Montana's elderly homeowner/renter credit missed two kinds of income in gross household income:

  1. Disability benefits from employment (disability_benefits). The model leaves them out of federal AGI and out of dependent_gross_income, and the credit's list didn't name them, so they never counted.
  2. Unemployment compensation excluded from 2020 federal AGI by IRC 85(c) (up to $10,200). Gross household income starts from federal AGI and never added the excluded part back.

What the law and forms count

  • MCA 15-30-2337(9)(a): "federal adjusted gross income, without regard to loss ... plus all nontaxable income, including but not limited to" a list.
  • Disability benefits. The 2014, 2016 and 2017 Form 2EC instructions (2014 PDF p.53, 2016 p.74, 2017 p.64) and the 2020-2023 Form 2 instructions, line 8 (2020 p.45, 2022 p.48, 2023 p.52) list "Disability benefits including, but not limited to, veteran's disability, SSI payments, and workers' compensation". From 2024, Schedule 2EC (2024 pp.47-48, 2025 pp.45-46) has no disability line. Taxable benefits sit in line 1 (IRS Pub. 525, PDF p.18: disability pay from an employer-paid plan is reported as wages until minimum retirement age) or line 5 (a disability pension after that age). Nontaxable benefits from a plan the recipient paid for go on line 15, "all income that is not reported on lines 1 through 14". Every year counts them.
  • Unemployment compensation. The 2014-2017 worksheets list "Alimony, cash public assistance, unemployment" among income "taxable and nontaxable". 2024 and 2025 Schedule 2EC line 14: "Enter the total of unemployment compensation received during the year." For 2020, the Elderly Homeowner/Renter Credit Schedule starts at line 1, "the federal Adjusted Gross Income from Form 2, line 11", and line 8 takes "all items of income received by the members of the household that were not reported on lines 1 through 7". The instructions PDF was created on 16 December 2020, before ARPA (Pub. L. 117-2 § 9042, 11 March 2021) added §85(c), so it doesn't name the exclusion. Montana DOR's 17 March 2021 notice has filers report federal Schedule 1 line 8 "including the federal exclusion of unemployment compensation", so the excluded amount is not in Form 2 line 11. Under §2337(9)(a) it is nontaxable income and goes on line 8.

Change

gross_income_sources.yaml gains two items in every period (2009, 2017, 2023):

  • disability_benefits, counted once in full. The model has no taxable/nontaxable split for it, and every year's form counts both parts.
  • A new Person variable, mt_elderly_homeowner_or_renter_credit_unemployment_compensation_addback: the unemployment compensation a member received that neither federal AGI nor a dependent's own gross income counts. In 2020 it is a filer's §85(c) excluded share. Outside 2020 it is zero, except for a person recorded as both a dependent and the head or spouse, whom neither income path counts. That edge follows Count a filer's gains without netted losses and Social Security once in Montana's elderly credit income #10139's Social Security rule.

The formula file is untouched, so #10139, which rewrites it, does not conflict with this PR.

Why not tax_exempt_unemployment_compensation

tax_exempt_unemployment_compensation would double count dependents. The model applies the tax unit's exclusion to every member's compensation, dependents included, and allocates the taxable part by share. dependent_gross_income already counts a dependent's compensation in full, without the exclusion, so adding the dependent's excluded share again would count it twice. Each role, with U received and T the model's taxable allocation:

Role In federal AGI In dependent gross income Added here Total
Head or spouse T 0 U − T U
Member on their own return T 0 U − T U
Dependent (not head or spouse) 0 U 0 U
Recorded as dependent and head 0 0 U U

Tests

Two new YAML files, kept separate from mt_elderly_homeowner_or_renter_credit.yaml and the gross household income file because #10139 and #10144 append cases to those, and a scratch merge with #10139 conflicts on appended cases. They can fold into the variable-named files after both land.

mt_elderly_homeowner_or_renter_credit_disability_and_unemployment.yaml, each case worked line by line from that year's form:

Case TY Credit before → after
Claimant's disability benefits 2,500 (Gross Household Income Worksheet line 9) 2016 740.40 → 580.20
Son's disability benefits 5,000 on his own return (worksheet line 7) 2017 860.70 → 515
Spouse's disability benefits 4,000 (line 8) 2023 848.80 → 605.20
Son's disability benefits 7,000 on his own return (line 17) 2024 1,150 → 720
Claimant's own disability benefits 6,000 2025 1,150 → 905.20
Claimant's unemployment compensation 9,000, all excluded (line 8) 2020 948 → 475
Daughter's 14,000 on her own return: 3,800 in AGI, 10,200 excluded 2020 1,000 → 535
Dependent grandson's 4,000 counts once, not again as excluded income 2020 675 (guard)
No exclusion in 2021 2021 495 (guard)
2024 line 14, all in AGI, counted once 2024 872.40 (guard)

mt_elderly_homeowner_or_renter_credit_unemployment_compensation_addback.yaml: 9 cases for the new variable (under and over the cap, over the $150,000 cutoff, 2021, 2024, a joint return, a dependent, and the dependent/head edge in 2024 and 2016, each also asserting gross household income).

Fails on the base (~/reviews/mt-ehrc-disability-uc/fails-on-base.log, revert confirmed with git status): credit cases 1-7 fail on gross household income; cases 8-9 fail only because they also pin the new variable; case 10 passes. Mutation check: dropping the new variable from the 2023 list fails case 8, and from the 2009 list case 9.

policyengine_us/tests/test_mt_elderly_credit_nontaxable_income.py: a grid invariant test in one vectorized Simulation. A claimant aged 66 lives with another adult, in three arrangements (a 20-year-old student dependent, a 45-year-old on their own return, a 60-year-old spouse on a joint return). It runs 2019, 2020, 2021 and 2024, with compensation under and over $10,200 and pensions under and over $150,000. 216 households in all.

  • Why Python. YAML pins worked examples. This checks every arrangement and year against a reference written from the statute (raw receipts, not the model's income paths), so a double count through federal AGI, dependent_gross_income or the 2020 exclusion shows up wherever it occurs.
  • Cost. One Simulation, released with gc.collect() when the fixture finishes. Locally (macOS, a loaded host) the test took about 8 s and peaked at 2.0 GB, against 1.28 GB for a bare model import and one-household calculation. These are local figures, not CI. It runs in the Rest job's remaining group (239 s in the Makefile's timing). A recent remaining run peaked at 13.96 GiB (run 38184073570's resource report: 14,638,840 KB), so whether the group keeps its headroom is checked on this PR's CI, not assumed.

Invariants

  • Each household member's unemployment compensation and disability benefits enter gross household income exactly once, whichever return they are on and whatever their role.
  • Gross household income, net of refundable credits received, is the same whether the other adult is a dependent, on their own return, or a spouse on a joint return.
  • The credit equals an independent reading of the schedule, lies between 0 and the cap, is 0 at $45,000 or more, and never rises with more income.

Validation after merging latest main

Validated final merge head b2259be4dd6a8e1331ac1a99d2faa13241c969dc on 2026-10-13 (local macOS; Python 3.13.9; Core 3.33.2; pandas 3.0.6; pytest 9.1.1). All required files passed at this head, each in a separate foreground process, sequentially. OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS, and NUMEXPR_NUM_THREADS were each 4; OMP_WAIT_POLICY=PASSIVE. Wall times below include process startup and shutdown on this local host; they are not Linux CI measurements.

YAML files in policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/:

File Passed Wall time (s)
mt_elderly_homeowner_or_renter_credit.yaml 36 331.44
mt_elderly_homeowner_or_renter_credit_dependent_deductions.yaml 6 117.38
mt_elderly_homeowner_or_renter_credit_disability_and_unemployment.yaml 10 111.35
mt_elderly_homeowner_or_renter_credit_eligible.yaml 3 91.87
mt_elderly_homeowner_or_renter_credit_federal_refundable_credits.yaml 4 152.62
mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml 15 119.49
mt_elderly_homeowner_or_renter_credit_net_household_income.yaml 3 70.29
mt_elderly_homeowner_or_renter_credit_pre_one_claimant.yaml 1 84.33
mt_elderly_homeowner_or_renter_credit_property_tax_rebate_received.yaml 6 103.10
mt_elderly_homeowner_or_renter_credit_selected_claimant.yaml 9 95.10
mt_elderly_homeowner_or_renter_credit_unemployment_compensation_addback.yaml 9 75.91

Python files in policyengine_us/tests/ (including this PR's invariant test):

File Passed Wall time (s)
test_mt_elderly_credit_dependent_deductions.py 2 39.99
test_mt_elderly_credit_government_assistance.py 2 46.25
test_mt_elderly_credit_household_members.py 3 28.02
test_mt_elderly_credit_nontaxable_income.py 13 39.45
test_mt_elderly_credit_one_claimant.py 2 63.50
test_mt_elderly_credit_rebate_receipts.py 2 38.09
test_mt_elderly_credit_refundable_credits.py 2 32.88

Total: 102 YAML cases and 26 Python tests across 11 YAML and 7 Python files. make format passed (7,208 files unchanged; Ruff lint passed), and git diff --check gh/main HEAD passed. The final patch is byte-for-byte identical to the approved PR delta; no partner contract tests changed.

The sole initial merge conflict was gross_income_sources.yaml. git diff f9d8d64b53 95eefa51e577922d56fc6c1effce98a242fbd6f4 -- <file> was empty, so the PR side was retained per the owner's hand-off. The duplicate changelog.d/mt-elderly-credit-government-assistance.fixed.md was removed. The second merge of latest main was conflict-free and preserves that first merge.

Coordination: #10139 may land in either order and also edits gross_income_sources.yaml. Whichever of this PR and #10185 lands second must switch disability_benefits to tax_exempt_disability_benefits in all three list periods and rerun the invariant test.

Earlier local runs

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base d965f22230 against this PR's head b2259be4dd, for 2024 and 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.

Output 2024 change 2026 change 2024 records changed (weighted) 2026 records changed (weighted)
mt_elderly_homeowner_or_renter_credit +0 +0 0 (0) 0 (0)
mt_elderly_homeowner_or_renter_credit_gross_household_income +35.23M +38.51M 4 (5,340) 4 (5,428)
mt_income_tax +0 +0 0 (0) 0 (0)
state_income_tax +0 +0 0 (0) 0 (0)
income_tax +0 +0 0 (0) 0 (0)
household_net_income +0 +0 0 (0) 0 (0)
household_benefits +0 +0 0 (0) 0 (0)

Measured against current main, which now includes #10144. Gross household income rises for 4 records in each year, but none of them changes the credit or Montana tax.

Comparison files are in fixes/10163-impact/ on the hub host.

Hub microsimulation impact (historical, before these main merges)

Real microsimulations on the default dataset (populace_us_2024), #10144's head f9d8d64b53 against this PR's head e3c78ee4dd, so the figures cover only this PR's commits. The runs went one at a time under the shared heavy-job lock. Sums are weighted. The later head 32fc16d292 renames the add-back variable and changes tests only, with the formula and list unchanged.

Output 2024 change 2026 change 2024 records changed (weighted) 2026 records changed (weighted)
mt_elderly_homeowner_or_renter_credit +0 +0 0 (0) 0 (0)
mt_elderly_homeowner_or_renter_credit_gross_household_income +35.23M +38.51M 4 (5,340) 4 (5,428)
mt_income_tax +0 +0 0 (0) 0 (0)
disability_benefits +0 +0 0 (0) 0 (0)
unemployment_compensation +0 +0 0 (0) 0 (0)
state_income_tax +0 +0 0 (0) 0 (0)
income_tax +0 +0 0 (0) 0 (0)
household_net_income +0 +0 0 (0) 0 (0)
household_benefits +0 +0 0 (0) 0 (0)

Four Montana person records gain gross household income (up to $13,200 in 2024). Each gain is either disability benefits or the unemployment compensation of a person recorded as both a dependent and the head, which is the only way the add-back is nonzero outside 2020. No record's credit, Montana income tax or net income changes in either year.

2020. A 2020 pair also ran, but in it unemployment_compensation and disability_benefits both sum to zero: the default dataset holds 2024 records, and the 2020 run has none of these inputs. Every output shows no change, so it cannot size the 2020 exclusion.

2024 counterfactual: the 2020 exclusion applied to 2024. To size the unemployment add-back, both sides ran 2024 with the 2020 IRC 85(c) parameters (exclusion amount $10,200; $150,000 cutoff for every filing status), set through IMPACT_REFORM_JSON. It uses the federal exclusion as currently modeled, before #10175 (one $10,200 cap per return, dependents' compensation pooled in, the compensation subtracted twice in the $150,000 test), so the add-back total will move when #10175 lands.

Output 2024 change 2024 records changed (weighted)
mt_elderly_homeowner_or_renter_credit -0 1 (0)
mt_elderly_homeowner_or_renter_credit_gross_household_income +127.80M 55 (22,561)
mt_income_tax +0 1 (0)
unemployment_compensation +0 0 (0)
tax_exempt_unemployment_compensation +0 0 (0)
state_income_tax +0 1 (0)
income_tax +0 0 (0)
household_net_income -0 1 (0)
household_benefits +0 0 (0)

In the counterfactual, one record's credit falls by $345; its weight is near zero (−$0.29 weighted).

Comparison files are in fixes/10163-impact/ and fixes/10163-impact-cf2020/ on the hub host.

Federal gaps found, filed separately

Review

Independent GPT-6.1 Sol reviews (Subfleet, --task review --tier hard, workspace-write):

  • Round 1, head 02f4fabb25: lost in a host restart before its verdict. Its draft had four P3 findings and no blockers, all addressed (line 5 for disability pensions, the add-back's role edge in the documentation, wiring tests for each list period; the separate test file kept as explained above).
  • Round 2, head e3c78ee4dd: APPROVE. It hand-checked every YAML case against the forms, verified every cited page, traced each role's income paths and ran the new tests (19 YAML, 13 invariant). Its optional P3s: rename the helper (done), supply Case 7's taxable allocation as an input (done), record the test's cost (above).
  • Round 3, delta e3c78ee4dd..32fc16d292: APPROVE, no findings; the rename is complete and Case 7's values check by hand (19/19 passed).

Reports: ~/reviews/mt-ehrc-disability-uc/review/ on the hub host.

axiom: TheAxiomFoundation/rulespec-us#1512 queued (extension comment: TheAxiomFoundation/rulespec-us#1512 (comment))

🤖 Generated with Claude Code

MaxGhenis and others added 11 commits October 12, 2026 06:02
… income

MCA 15-30-2337(9)(a) counts all nontaxable income, including (vi) cash
public assistance and relief. Schedule 2EC line 16 (2024, 2025) names SSI
and workers' compensation, and the 2014-2023 instructions list SSI
payments and workers' compensation as disability benefits. Add ssi,
tanf_person (the SPM unit's TANF per capita, so the household sum counts
it once) and workers_compensation to every period of the gross income
sources, with worked Schedule 2EC / Form 2EC cases for 2016-2025.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For a claimant, another adult and a child in four arrangements (one return
or two, one SPM unit or two, and more assistance), gross household income
adds every member's SSI and workers' compensation and the TANF once; the
credit matches an independent reading of Schedule 2EC lines 18 to 30, does
not depend on the arrangement, stays within the cap and never rises with
more assistance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gross household income now counts SSI, and the model computes SSI for a
claimant aged 65 or older with little income (take-up defaults to true).
Three gross household income cases and three property-test builders are
about other items, so they now input no SSI; SSI has its own cases in the
government assistance tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-ups: gi_cash_assistance is outside federal AGI and
15-30-2337(9)(a) counts all nontaxable income (a government-funded pilot
is also "any other government assistance" on Schedule 2EC line 16), so it
joins every period of the sources. The worked cases move into
mt_elderly_homeowner_or_renter_credit.yaml (and the SNAP guard into the gross
household income file), with a guaranteed income case, the 2016 worksheet
total corrected to line 10 and the references consolidated to one per
document, with the 2009 MCA text for the 2009 period.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…al AGI in Montana's elderly credit income (WIP)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; add changelog

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nsation count once in every arrangement and year

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#9900's one-claimant property test (now on main) builds claimants aged 65
or older with little income, for whom the model computes SSI, which gross
household income now counts; its reference leaves SSI out, so the builder
inputs none. Reference titles follow the repo format: no page in a
single-page title, and quoted titles ending in "#page N-M" for several.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Heads-up from #10175 (2020 unemployment compensation exclusion fix), which changes two pins in this PR's mt_elderly_homeowner_or_renter_credit_nontaxable_unemployment_compensation.yaml Case 7.

#10175 makes each person's exclusion their own (up to $10,200 per spouse) and keeps a dependent's compensation on the dependent's own return, where the dependent excludes up to $10,200 if their own §85(c)(2) AGI is under $150,000. For Case 7 (spouse $8,000, dependent $4,000):

  • taxable_unemployment_compensation: [0, 1_200, 600] → [0, 0, 0]
  • mt_elderly_homeowner_or_renter_credit_nontaxable_unemployment_compensation: [0, 6_800, 0] → [0, 8_000, 0]

dependent_gross_income stays [0, 0, 4_000], and gross household income does not change (12,000 either way: before, 1,200 in AGI + 6,800 here + 4,000 in the dependent's gross income; after, 0 + 8,000 + 4,000). The Case 7 comment about the tax unit's shared $10,200 and allocation by share also goes stale.

Whichever PR lands second updates these pins after merging main.

…e, and test each list period's wiring

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ment_compensation_addback; supply Case 7's taxable allocation as an input

Both review rounds noted that the helper adds more than nontaxable compensation
for a person recorded as both a dependent and the head. Case 7 no longer pins
the federal exclusion's allocation, which #10175 changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis changed the base branch from mt-elderly-credit-government-assistance to main October 12, 2026 23:58
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 12, 2026 23:58
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hand-off to the US + core hub.

…ity-unemployment

# Conflicts:
#	policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml
@MaxGhenis
MaxGhenis merged commit b3568cb into main Oct 13, 2026
26 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10163 at b2259be4dd6a8e1331ac1a99d2faa13241c969dc (squash): Count disability benefits and 2020 excluded unemployment compensation in Montana's elderly homeowner/renter credit income.

@MaxGhenis
MaxGhenis deleted the mt-elderly-credit-disability-unemployment branch October 13, 2026 13:16
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