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>
@DTrim99

DTrim99 commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Cap Montana's 2021–2023 state and local tax deduction once per joint return and at $5,000 per separate column

Summary

The fix is right in law. I checked the 2021, 2022 and 2023 Form 2 Itemized Deductions Schedule, line 5 (PDF p. 7 each year). It reads: "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". The filing-separately rules say a status 2a return is split "between Columns A and B, with each column constituting its own return" (2021 instructions PDF p. 10, 2022 p. 11, 2023 p. 12).

  • A joint return now caps the couple's combined line 5 once, at $10,000. Main capped each spouse separately.
  • Each 2a column is now capped at $5,000. Main capped each column at the joint $10,000.
  • Lines 5a (general sales taxes) and 5b (local income taxes) are now included.
  • Per year and filing status: single, head of household and joint get $10,000; separate forms (PE SEPARATE) and each 2a column get $5,000; a federal qualifying surviving spouse, who files Montana status 1, gets $10,000.
  • 2024+ is unchanged.

I hand-checked 23 expectations and the PR body's household table.

The branch is 729 commits behind main and conflicts with it in two YAML test files. I resolved them the obvious way (keep both renames) and ran a merged tree locally:

  • the PR's and the affected Montana YAML files: 100 cases pass;
  • the source-conflict and other relevant code-health guards: pass;
  • main's newer Montana property tests: pass;
  • the PR's own Hypothesis file: passes.

Two points remain open, both SHOULD:

  1. A new term in mt_salt_deduction_indiv is not pinned by any test.
  2. One reference tuple lists the same PDF twice, which the CLAUDE.md reference rules don't allow.

Critical (must fix)

None.

Should address

  1. mt_salt_deduction_indiv.py:61 and :64: the other_local_tax term is not pinned.

    • The term is other_local_tax = max_(0, local_income_tax - person.tax_unit.sum(own_local_tax)). Each married column then adds sales_tax_share * other_local_tax.
    • No YAML case and no Hypothesis draw ever makes local_income_tax larger than the sum of the three person-level city taxes. The differential in test_mt_salt_deduction_invariants.py doesn't model this term either. So deleting it (using sales_tax_share * sales_tax) fails no test.
    • It is reachable: local_income_tax can be entered directly, and the NYC, Indiana county and Wilmington taxes it adds have no person-level split here.
    • Fix: add a mt_salt_deduction_indiv.yaml case for 2021–2023. Example: an MT couple with tax-unit input local_income_tax: 2_000, no city earnings and real estate taxes of 4,500 and 0. Expected columns: [min(4,500 + 1,000, 5,000), 1,000] = [5,000, 1,000].
    • Alternatively, drop the term. For a Montana household PolicyEngine levies no other local income tax, as the PR body says.
    • Side note: the instructions say deductions "attributable to only one spouse must be claimed by that spouse" (2023 instructions PDF p. 13). An equal split of another city's income tax is a convention, not that rule, so say so in the comment if you keep the term.
  2. mt_salt_deduction_indiv.py:20–23: the same document is listed twice.

    • The 2023 Form 2 instructions appear at #page=12 and again at #page=37.
    • CLAUDE.md: "One reference entry per source document; URLs that differ only by #page= are one source". Also: "Variable reference tuples have no title: put a # PDF pages 61-62, 67 comment above a multi-page href."
    • The code comments also quote PDF p. 13 ("Deductions that are attributable to only one spouse must be claimed by that spouse"), which no reference covers.
    • Fix: one entry, ...2023_..._Instructions.pdf#page=12, with # PDF pages 12-13, 37 above it. Optionally, add the 2021 (PDF p. 10) and 2022 (PDF p. 11) instructions too, since the variable applies to 2021–2023. Their allocation text is identical.

Suggestions

  1. Inert mt_salt_deduction: 0 inputs once merged with main.

    • Main's new separate-column cases set mt_salt_deduction: 0 to isolate other lines: the split-mortgage case and both premium cases. That is 6 inputs in mt_itemized_deductions_indiv.yaml and 6 in federal_itemization/mt_itemized_deductions_for_federal_itemization_indiv.yaml.
    • After this PR those inputs no longer reach the columns. The cases still pass only because the computed mt_salt_deduction_indiv is 0 there.
    • Rename them to mt_salt_deduction_indiv when resolving the merge. Add mt_salt_deduction_indiv to ZERO_PERSON_COMPONENTS in main's tests/core/test_mt_mortgage_interest_federal_limit_properties.py for the same reason. It passes on the merged tree today.
  2. Reuse the existing separate-filing status.

    • cap = where(married, p.cap.SEPARATE, p.cap[filing_status]) rebuilds the mapping that state_filing_status_if_married_filing_separately_on_same_return already provides (JOINT to SEPARATE, others unchanged).
    • Montana's other column variables use that variable (mt_standard_deduction_indiv, mt_federal_income_tax_deduction_indiv, mt_regular_income_tax_indiv). p.cap[state_filing_status_if_married_filing_separately_on_same_return] would match them.
  3. Dependents are treated asymmetrically.

    • On a joint return, line 5 now sums every tax-unit member's real estate taxes and, through local_income_tax, their city taxes. A 2a column drops dependents' amounts entirely.
    • Main's joint itemized total already excludes dependents' mortgage and investment interest ("A joint return deducts the interest either spouse paid, excluding dependents").
    • Line 5c is real estate taxes "that you paid ... on real estate that you own" (2023 instructions PDF p. 37). Consider counting only the head and spouse on the joint return too, or record this as a methodology note. It matches the federal salt convention, and the PR body documents it, so this is not a finding.
  4. Sales taxes and local income taxes together (optional).

    • Under 26 USC 164(b)(5)(A), electing general sales taxes applies 164(a) "without regard to the reference to State and local income taxes". Former MCA 15-30-2131(1)(a) incorporates the section 161 items.
    • Form 2 nonetheless lists 5a and 5b as separate lines with no election, and the PR follows the form.
    • The overlap needs an entered sales tax for a Montana household (PE's table amount is 0) plus a Kansas City, St. Louis or Philadelphia tax, so the reach is negligible. If you read the statute as importing the election, record a candidate. Otherwise a one-line comment noting that the form allows both would help the next reader.
  5. Joint payments allocated freely (follow-up). The instructions let spouses allocate jointly paid taxes "in any proportional amount" (2023 instructions PDF p. 13). With per-person real_estate_taxes, a 2a couple with 8,000/2,000 deducts 7,000. Allocating jointly paid taxes up to each column's $5,000 would give 10,000. This is documented as a methodology choice in the body and the axiom issue; just flagging it as a follow-up.

  6. PR body housekeeping.

  7. Axiom issue visibility. Use a reform instead of a parameter to simulate taxsim's incorrectnesses #1520's title and body cover only line 1 and the standard deduction. Line 5 is in an extension comment. Consider adding "line 5" to the title or a pointer in the body, so the dispatcher sees it.

Verified correct

Law

  • 2021–2023 Form 2 page 7, line 5, gives the cap text quoted above (identical each year).
  • Line 5 instructions are at 2021 PDF p. 32, 2022 p. 34 and 2023 p. 37: "State and Local Taxes Limited to $10,000 ... excluding state income taxes". 5a, 5b, 5c and 5d are cumulative lines. Line 6 (vehicle registration fees) and line 7 (livestock fees) are outside the cap and untouched.
  • Former MCA 15-30-2131(1)(a) (MCA 2021) allows the IRC section 161/211 items except "(ii) state income tax paid". The section is repealed in MCA 2023 (Secs. 65, 70(1), Ch. 503, L. 2021). The $10,000/$5,000 amounts are 26 USC 164(b)(6) for 2018–2024.
  • 2023 instructions PDF p. 12: status 2a "allocation is done between Columns A and B, with each column constituting its own return". PDF p. 13 has the attributable-deduction and joint-account allocation rules. The same text is in 2021 (p. 10) and 2022 (p. 11).

Formulas

  • mt_salt_deduction (2021–2023) is is_head * min_(sum over the unit of 5a + 5b + 5c, cap[filing_status]), and the joint aggregates add it over the unit. 2024+ keeps main's per-person amount.
  • mt_salt_deduction_indiv caps married columns at SEPARATE and other filers at their own status's cap. It splits sales tax by spouse_allocation_rate (0.5), gives each column its own spouse's city tax and zeroes dependents. Its defined_for eligibility ends in 2024, so it is zero from 2024.
  • Vectorized throughout; the state_specific_deduction_applies switch is a scalar parameter.
  • Entities and periods are correct: Person for the deductions and city taxes, TaxUnit sums through adds, all YEAR.
  • No new state-gated int or enum key (filing_status is not state-gated), so the NH Add New Hampshire state supplementation for 2026 #10156 pattern doesn't apply.
  • state_specific_deduction_applies.yaml is byte-identical to main's (blob 8b6d633).

Hand checks

mt_salt_deduction.yaml:

  1. HoH 2022, 12,000: min(12,000, 10,000) = 10,000.
  2. Joint 2023, 8,000 + 8,000: min(16,000, 10,000), [10,000, 0]. mt_itemized_deductions_joint = [10,000, 0].
  3. Joint 2022: 6,000 + 2,500 = 8,500.
  4. Joint 2021: 9,000 + 1,500 + 300 = 10,800, capped to 10,000.
  5. Single 2023: 2,000 + 1,800 = 3,800.
  6. Single 2023 with Kansas City tax: 2,000 + 1% × 100,000 = 3,000.
  7. Joint 2022: 9,500 + 1,000 = 10,500, capped to 10,000.
  8. Joint 2023: 4,000 + 1,000 + 3.44% × 100,000 = 8,440. The nonresident rate is 0.0344 from 2022-07-01, which is in effect at 2023-01-01.

mt_salt_deduction_indiv.yaml:
9. [min(8,000, 5,000), min(8,000, 5,000)] = [5,000, 5,000]; mt_itemized_deductions_indiv = [5,000, 5,000].
10. [5,000, 2,000].
11. 4,800 + 500 = 5,300, capped to 5,000; column B 500.
12. Dependent case: [3,000, 1,000, 0].
13. Single: min(12,000, 10,000) = 10,000; 8,000.
14. SEPARATE: min(6,000, 5,000) = 5,000.
15. 2024: [0, 0].
16. [3,000 + 1,000, 3,000] = [4,000, 3,000].
17. [min(4,500 + 1,000, 5,000), 500] = [5,000, 500].
18. [3.44% × 100,000, 1.5% × 100,000] = [3,440, 1,500].
19. [1,000 − 1,000, 1,000] = [0, 1,000].
20. [min(4,000 + 1,000, 5,000), 3,440] = [5,000, 3,440].

City tax files:
21. Kansas City 2024: [1,000, 500], total 1,500.
22. St. Louis: [max(0, 1,000 − 1,200), 1,000] = [0, 1,000], total 1,000, before credit 2,000.
23. Philadelphia 2024: 3,750 + 3,440 + 600 = 7,790.

PR body table:

  • 2023 joint itemized: 26,000 − 6,000 = 20,000. Each column falls by 3,000.
  • 2023 mt_income_tax: 6,436.61 − 6,031.61 = 405 = 6,000 × 6.75%.
  • 2021: 4,274.85 − 3,860.85 = 414 = 6,000 × 6.9%.

Other checks

  • Changelog: a top-level changelog.d/fix-mt-salt-cap-joint-return.fixed.md, type fixed.
  • Tests are in variable-named YAML files.
  • The Python file has a stated purpose (vectorized mixed units, monotonicity, head swap, 2024+ equality), an independent reference calculation with caps from the forms, and reported cost.
  • No partner-test files are touched.
  • Callers: on main mt_salt_deduction is read only by the four Montana itemized aggregates. The two separate-column ones switch to mt_salt_deduction_indiv, and the two joint ones sum the head-only amount correctly.
  • No new cycle: the city taxes depend only on their inputs.
  • The new comments add nothing that matches the source-conflict ratchet pattern.

Axiom line assessment

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

  • Use a reform instead of a parameter to simulate taxsim's incorrectnesses #1520 is OPEN and labelled pe-parity.
  • Its 2026-10-07 extension comment, last edited 2026-10-08, names this PR and head 0a4846c7b1. It gives:
    • the module path (us-mt/statutes/15-30-2131/1/a.yaml, state and local tax component, composing us/statutes/26/164.yaml);
    • the corpus pin and rows;
    • verbatim Form 2 line 5, the instructions, the MCA and P.L. 119-21 sec. 70120(a);
    • required outputs 5–9;
    • a review_finding;
    • 14 companion cases from the form's line 5 arithmetic, matching this PR's YAML (including the person-level city tax cases at this head).
  • The blocker is recorded. The TY2018–2024 text of 164(b)(6) is not in the corpus, and each encoder run needs approval. The 2026-10-19 comment shows the Form 2 and MCA 2021 rows have since been ingested.
  • See suggestion 7 on the title.

CI status

  • All 26 checks pass at 0a4846c7b1 (run 37795653696, 2026-10-08). Nothing is pending.
  • That run used the CI configuration of early October. It predates main's Rest sharding, the YAML-only batch collection and several new guards, so it does not cover today's main.
  • On a locally merged tree (main e53b426082 plus this head, with the two YAML conflicts resolved), run one process at a time:
    • YAML: 11 files, 100 cases pass. These are mt_salt_deduction, mt_salt_deduction_indiv, both separate-column and both joint itemized files, mt_tax_unit_itemizes, Montana integration.yaml and the three city tax files.
    • Code-health guards: test_source_conflicts.py, test_reference_urls.py, test_no_group_variables_in_person_adds.py and test_no_builtin_sum_over_entity_calls.py pass. One synthetic tokenizer probe in test_source_conflicts.py (test_unscannable_files_fail_closed) fails locally under Python 3.11; it reads no repository file and is unrelated to this PR.
    • Main's Montana tests: tests/core/test_mt_mortgage_interest_federal_limit_properties.py (6 pass), test_state_income_tax_accounting_invariants.py::test_formula_states_keep_the_properties_in_2023 (pass, including at least one Montana unit filing separately) and test_state_tax_head_spouse_swap_invariance.py::test_seeded_population for 2021–2023 (3 pass).
    • The PR's own file: test_mt_salt_deduction_invariants.py (7 pass).
  • Other main-side guards don't apply: Integrate modeled state unemployment insurance into income flows #9558, Count military SBP annuities in state benefit income lists #10317 and Count each Social Security benefit once in Pennsylvania CCW countable income #10408 (income source lists), test_programs_registry and the reform combo budget.

Branch status

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.

Requesting changes: 0 critical, 2 should (the other_local_tax term is unpinned; the 2023 instructions are cited twice instead of one entry with a page-list title). The cap logic matches Form 2 line 5 for 2021-2023 and every filing status, and the merged tree passes locally. Branch note: 729 behind, with conflicts in two Montana YAML files (keep both renames). 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.

2 participants