Skip to content

Cap Montana's 2021–2023 state and local tax deduction once per joint return and at $5,000 per separate column - #10002

Open
MaxGhenis wants to merge 3 commits into
mainfrom
fix-mt-salt-cap-joint-return
Open

MaxGhenis wants to merge 3 commits into
mainfrom
fix-mt-salt-cap-joint-return

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Montana's 2021–2023 Itemized Deductions Schedule, line 5, caps state and local taxes at $10,000 for single, head of household and joint returns, and at $5,000 for married filing separately. PolicyEngine applied the cap to each person:

  • Joint return. mt_salt_deduction capped each spouse, and the joint aggregates added the spouses up. A 2023 couple with $8,000 of real estate taxes each deducted $16,000. The form allows $10,000.
  • Separately on the same form (status 2a). Each spouse's column took the cap for the tax unit's federal status (joint), so each column was capped at $10,000. Each column is a separate return and is capped at $5,000.

What Montana's forms say

  • 2021, 2022 and 2023 Form 2, Itemized Deductions Schedule, line 5 (2021, 2022, 2023, PDF p. 7, same text each year): "Add lines 5a through 5d, enter the total here, but not more than $10,000 if your status is single, head of household or married filing jointly; or $5,000 if you are married filing separately." Lines 5a–5d are general state and local sales taxes, local income taxes, real estate taxes, and value-based personal property taxes.
  • Line 5 instructions (2021 PDF p. 32, 2022 p. 34, 2023 p. 37): "You can deduct the state and local taxes you paid in 2023, excluding state income taxes."
  • Filing separately (2023 instructions PDF pp. 12–13): "When filing separately on the same form this allocation is done between Columns A and B, with each column constituting its own return." And: "Payments made from accounts owned by both spouses that pertain to expenses not attributable specifically to one spouse can be allocated to either spouse in any proportional amount."
  • Former MCA 15-30-2131(1)(a) (2021) allows the items in IRC sections 161 and 211, except "(ii) state income tax paid". The $10,000/$5,000 amounts are those of 26 USC 164(b)(6) for 2018–2024 ("$10,000 ($5,000 in the case of a married individual filing a separate return)", the text P.L. 119-21 sec. 70120(a)(2) struck for later years). The section's history note reads "(Repealed effective January 1, 2024--secs. 65, 70(1), Ch. 503, L. 2021.)".

Changes

  • mt_salt_deduction (Person). For 2021–2023 it is line 5 for the whole return, on the head: min(lines 5a + 5b + 5c over the tax unit, the cap for the filing status). The joint aggregates (mt_itemized_deductions_joint and mt_itemized_deductions_for_federal_itemization_joint) already add it over the tax unit, so they are not edited. From 2024 the formula is main's.
  • mt_salt_deduction_indiv (Person, new). Each status 2a column. A married couple's columns are each capped at $5,000; any other filer's single column is capped at the amount for their own status. Each column takes its own spouse's city taxes (line 5b). It replaces mt_salt_deduction in mt_itemized_deductions_indiv and mt_itemized_deductions_for_federal_itemization_indiv (one line each).
  • City taxes per person. New mo_kansas_city_earnings_tax_person, mo_st_louis_earnings_tax_person and pa_philadelphia_wage_tax_person hold each person's own tax, with the existing formulas. The three tax-unit variables now add them up. Main gives the same tax-unit amounts on the new city tests' households, and tests/core/test_local_employee_taxes.py (11 tests) passes.
  • Switch. gov.states.mt.tax.income.deductions.itemized.state_specific_deduction_applies (true 2021–2023, false from 2024). The file is byte-identical (blob 8b6d633) to the one Floor Montana's joint-return medical and standard deductions on the return's Montana AGI, and on federal AGI from 2024 #9903 and Deduct Montana's 2021–2023 medical and long-term care insurance premiums in full (Itemized Deductions Schedule lines 2–3) #9947 add, and to the one in the pending 2024+ branch (below). Whichever lands first, the others merge cleanly.

Lines 5a, 5b and 5d

  • 5a, general sales taxes: included. state_sales_tax and local_sales_tax are tax-unit amounts. They are zero for Montana households unless entered: every Montana entry in the IRS optional sales tax table is 0 for 2021–2023, and local_sales_tax is 20% of state_sales_tax outside its no-local-tax list. A couple's 2a columns split them equally, with the existing spouse_allocation_rate that the charitable deduction also uses. The form allows "any proportional amount".
  • 5b, local income taxes: included. The 2023 instructions say "No city in Montana imposes a local income tax", but a Montana resident can owe another city's. Of the six local taxes in local_income_tax, PolicyEngine levies three without a residence test: the Kansas City and St. Louis earnings taxes and the Philadelphia wage tax, each on its own taxable-earnings input. (NYC tax applies to in_nyc households, the Indiana county tax to Indiana households and the Wilmington tax to Delaware households.)
    • Joint return: the tax unit's local_income_tax goes into line 5. mt_itemized_deductions_joint already depended on local_income_tax through the federal income tax deduction (federal tax, then SALT, then local_income_tax), so this adds no new cycle. The federal-itemization variants feed only state_itemized_deductions, which no formula reads.
    • Each 2a column: "Deductions that are attributable to only one spouse must be claimed by that spouse" (2023 instructions PDF p. 13). Each column takes its own spouse's city taxes from the person-level variables, at that spouse's own rate and after their own credits. Any other local income tax of the tax unit (none for a Montana household) is divided like the sales taxes.
  • 5d, value-based personal property taxes: not modeled. PolicyEngine has no personal property tax input. The federal SALT sources are state_and_local_sales_or_income_tax and real_estate_taxes.

2024 and later: unchanged

From 2024 mt_salt_deduction keeps main's per-person amounts. mt_salt_deduction_indiv is zero because filing separately on the same form ended: mt_married_filing_separately_on_same_return_eligible is false from 2024, which already zeroed the separate-column aggregates on main. The Hypothesis file checks 2024 and 2026 against main's formula. The 2024+ regime (Form 2 starting from federal taxable income, Worksheet A) is in another session's branch, mt-federal-taxable-income-2024, not here.

Household example

A couple with wages of $80,000 and $60,000 and real estate taxes of $8,000 each, from Simulation runs on main (b7e4229d86) and this branch:

2023 main 2023 this PR 2024 main = this PR
mt_salt_deduction [8,000, 8,000] [10,000, 0] [8,000, 8,000]
mt_salt_deduction_indiv (n/a) [5,000, 5,000] [0, 0]
mt_itemized_deductions_joint 26,000 20,000 16,000
mt_itemized_deductions_indiv [13,000, 12,285.71] [10,000, 9,285.71] [0, 0]
mt_files_separately true true false
mt_income_tax 6,031.61 6,436.61 6,045.20

For 2021 the same household's Montana tax goes from 3,860.85 to 4,274.85. Each column loses $3,000 of SALT; each change is 6,000 times the rate (6.75% in 2023, 6.9% in 2021).

Tests

YAML. The expectations come from the line 5 arithmetic, which each case's comments show.

  • mt_salt_deduction.yaml: 8 new cases (joint above the cap, joint below it, joint with sales taxes, single with sales taxes, head of household, single with a Kansas City earnings tax, joint at the cap with one, joint with Kansas City and Philadelphia taxes). The 4 existing cases are unchanged.
  • mt_salt_deduction_indiv.yaml (new): 13 cases (couple above the cap, one spouse below it, sales taxes split, a dependent, single above and below the single cap, a married person on a separate form, 2024, and five city tax cases: one spouse's Kansas City tax, both spouses' with one column at the cap, Philadelphia nonresident and reduced rates, a St. Louis credit, and Kansas City plus Philadelphia with one column at the cap).
  • New city tax files, the first tests for these taxes: gov/local/mo/kansas_city/.../mo_kansas_city_earnings_tax.yaml, gov/local/mo/st_louis/.../mo_st_louis_earnings_tax.yaml and gov/local/pa/philadelphia/.../pa_philadelphia_wage_tax.yaml, with per-person and tax-unit amounts.
  • mt_itemized_deductions_indiv.yaml and federal_itemization/mt_itemized_deductions_for_federal_itemization_indiv.yaml: the SALT input is renamed to mt_salt_deduction_indiv. Expectations are unchanged.
  • On main (checked before the line 5b cases were added), 4 of the first 5 new mt_salt_deduction cases fail. The couple above the cap gets [8,000, 8,000]. The couple below the cap gets [6,000, 2,500], the right total but split across the spouses. The two sales tax cases leave out line 5a. With mt_salt_deduction standing in for the new variable, 5 of the 8 mt_salt_deduction_indiv cases fail: married columns cap at $10,000, a dependent keeps their own amount, and 2024 gives 8,000 each. The single and separate-form cases pass on main.
  • Run locally at 0a4846c7b1, one file at a time: mt_salt_deduction.yaml 12 passed, mt_salt_deduction_indiv.yaml 13, the three city files 1 + 1 + 2, and tests/core/test_local_employee_taxes.py 11. At 0222cabda4: mt_itemized_deductions_indiv.yaml 4, mt_itemized_deductions_joint.yaml 4, and the two federal-itemization files 4 each. CI at 9d184261a9 (run 37687652780, attempt 2) passed all 35 jobs, including every Baseline state shard; CI at 0a4846c7b1 is pending.

Hypothesis, test_mt_salt_deduction_invariants.py. YAML cannot cover these: many mixed tax units (single, head of household, separate, joint, joint with dependents, some with sales taxes, Kansas City and St. Louis earnings, and per-person St. Louis credits) in one vectorized simulation, results under changed inputs, and the 2024+ comparison.

  • 7 tests: 6 drawn batches of 5–30 tax units, plus a seeded population of 150 units, for 2021–2023 (schedule) and for 2024 and 2026 (unchanged). Draws include Kansas City and St. Louis earnings and per-person St. Louis credits, checked against each person's own tax.
  • Local cost at 0a4846c7b1 (macOS, heavily loaded machine, /usr/bin/time -l): 18.1 s in pytest, 52.6 s wall with imports, maximum resident set size 1.08 GB.
  • CI cost: it runs twice, as other .py files under gov/states already do: in the Rest Python policy group, and in Full Suite - Baseline (states-shard-4), where the batch runner passes whole folders to pytest. In shard 4 of run 37687652780 its tests took at least 14.9 s of call time. In run 37687652780 its 7 tests took 9.1 s of the group's 308.7 s of test time. The group took 7:15 wall with peak RSS 14.19 GB. Six recent runs without this file on other branches (excluding trim-rest-test-cost, which cuts this group) took 7:45–9:12 wall with peak RSS 14.19–14.27 GB, so the file adds no measurable memory. (These are Linux CI figures; the local figures above are macOS.)
  • Mutation checks, each of which fails the file: main's per-person joint amount; a column cap taken from the tax unit's status; the pooled cap given to every member; the full sales tax in each column; line 5b left out of the joint return; local income tax split like sales tax; and the proportional split of city taxes used at 9d184261a9.

Invariants

  • For 2021–2023 a return's line 5 is min(lines 5a, 5b and 5c summed over the return, the cap for its filing status). It sits on the head; other members get 0.
  • A married couple's 2a column is at most $5,000 and at most that spouse's own real estate and city taxes plus half the sales taxes.
  • A couple's two columns never deduct more than their joint return.
  • Raising any member's real estate taxes never lowers the joint line 5 or any column.
  • Swapping which spouse is the head leaves the joint line 5 unchanged and moves each column with its spouse.
  • From 2024 every value equals main's.

Merge order and conflicts

git merge-tree from 0a4846c7b1 against each open Montana PR head (and it merges cleanly into main at 7b956eb401): #9877 (6eefe271dc), #9888 (e352ed05bb), #9903 (7fb88f0abf), #9947 (4a5802c1a8), #9981 (d4641289a0) and #9890 (eaabd72547) all merge cleanly. #9585 (b584e8b160) conflicts with main itself in Arkansas files and shares no Montana file with this PR. #10004 (880058056e, opened after this PR) conflicts in mt_itemized_deductions_indiv.yaml and its federal-itemization twin. In case 3 it renames the line above this PR's renamed SALT input (mortgage_interest to deductible_mortgage_interest). Keep both renames: with that resolution both files and mt_salt_deduction_indiv.yaml pass in a merge of 0a4846c7b1 with #10004 at 880058056e (5 + 5 + 13 cases). #9888's new separate-path cases set mt_salt_deduction: 0. After both merge that input no longer reaches the columns, but the computed mt_salt_deduction_indiv is also 0 there. In an actual merge with #9888, the two separate-column itemized files, the joint itemized file, its casualty file and this PR's new file pass (29 cases).

Microsim

None. The default dataset (populace_us_2024.h5, tag populace-us-2024-spm-20260909) stores inputs for 2024 only (_time_period = 2024), so a 2021–2023 run computes from empty inputs and shows no change. From 2024 the formulas are main's (see above).

Review

  • Round 1 (Subfleet, GPT-6.1 Sol, read-only, at 0222cabda4): REQUEST_CHANGES. P2: the claim that local_income_tax is always zero for a Montana household was false (a Kansas City earnings tax has no residence test), so line 5b is now included (9d184261a9). P3: describe years before 2021 precisely (done above). It verified the law, formulas, 2024+ equality, all 33 YAML expectations by hand, the household table and the parameter blobs; it could not run tests on the loaded machine. Full text: ~/reviews/us-hub/mt-salt-cap/review-r1.md.
  • Round 2 (one Claude Opus 5.5 agent in this session, read-only, at 9d184261a9; no Subfleet lane could serve it): REQUEST_CHANGES. P2: dividing city taxes by share of taxable earnings is exact only with one rate and no credits, but PolicyEngine sets Philadelphia's rate per person and St. Louis credits per person. Fixed in 0a4846c7b1 with the person-level city taxes. P3s fixed here: the Invariants wording, the second CI job, the dependents note, the measuring tool, and the docstring. Full text: ~/reviews/us-hub/mt-salt-cap/review-r2.md.

Methodology notes

  • Allocating jointly paid taxes in 2a. PolicyEngine treats each person's real_estate_taxes as theirs. The form lets a couple allocate jointly paid taxes "in any proportional amount", which matters at the $5,000 cap. This PR does not optimize that, and neither do the other separate-column deductions.
  • Dependents. On a joint return every tax-unit member's real estate and city taxes count, as in the federal salt. In 2a columns dependents have none, as in the other separate-column deductions.
  • Before 2021. PolicyEngine models from 2015, and Montana's parameters start in 2021, so earlier years read the 2021 values. The line 5 rule therefore also applies to 2015–2020, with the federal cap parameter of each year: no cap for 2015–2017 and $10,000/$5,000 for 2018–2020.

axiom: former MCA 15-30-2131(1)(a) (Itemized Deductions Schedule line 5) TheAxiomFoundation/rulespec-us#1520 queued

🤖 Generated with Claude Code

…5,000 per separate column

Form 2, Itemized Deductions Schedule, line 5 (2021-2023) adds lines 5a
through 5d and caps the total at $10,000 (single, head of household,
married filing jointly) or $5,000 (married filing separately).

- mt_salt_deduction: a joint return's taxes are pooled and capped once,
  on the head. It had capped each spouse separately, so a couple with
  $8,000 each deducted $16,000.
- mt_salt_deduction_indiv (new): each column of a return filed
  separately on the same form (status 2a) is capped at $5,000. The
  separate-columns path had used the tax unit's joint cap ($10,000).
- Line 5a: add the tax unit's general sales taxes (state_sales_tax,
  local_sales_tax), split equally between a couple's columns.
- From 2024 (schedule discontinued) the per-person amounts are as
  before; the 2024 Worksheet A change is separate.

The 2021-2023 switch is gov.states.mt.tax.income.deductions.itemized.
state_specific_deduction_applies, byte-identical to the file in #9903
and #9947.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 7, 2026 17:11
…ction

Review r1 found that local_income_tax is not always zero for a Montana
household: PolicyEngine levies the Kansas City and St. Louis earnings
taxes and the Philadelphia wage tax on earnings in those cities without a
residence test. Line 5 adds "any local income taxes that you paid".

- mt_salt_deduction: the joint return's line 5 adds local_income_tax.
- mt_salt_deduction_indiv: each column takes the tax unit's local income
  tax in proportion to the person's own earnings taxed by those cities
  ("Deductions that are attributable to only one spouse must be claimed
  by that spouse"); without such earnings it is divided like the sales
  taxes.
- YAML: Kansas City cases for a single return, a joint return at the
  cap, and two separate-column cases. The Hypothesis file adds Kansas
  City earnings to its draws and reference calculation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r2: dividing the tax unit's local income tax by each spouse's
share of taxable city earnings is exact only when everyone pays the same
rate with no credit. PolicyEngine sets Philadelphia's rate per person and
subtracts St. Louis credits per person.

- New Person variables mo_kansas_city_earnings_tax_person,
  mo_st_louis_earnings_tax_person and pa_philadelphia_wage_tax_person
  hold each person's own tax, with the existing formulas. The tax-unit
  variables now add them up, so their values do not change.
- mt_salt_deduction_indiv: each column takes its own spouse's city tax.
  Any other local income tax of the tax unit (none for a Montana
  household) is divided like the sales taxes.
- YAML: first tests for the three city taxes; Montana cases for a
  Philadelphia reduced rate, a St. Louis credit, two cities with one
  column at the cap, and a joint return with two cities.
- Hypothesis: St. Louis earnings and per-person credits in the draws.

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

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.

1 participant