Skip to content

Align Montana separate-filing deductions and the elderly credit's income with the forms - #9877

Merged
MaxGhenis merged 7 commits into
mainfrom
fix-mt-separate-filing-allocations
Oct 12, 2026
Merged

MaxGhenis merged 7 commits into
mainfrom
fix-mt-separate-filing-allocations

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Most of this PR is already on main. It landed through #9915 (squash 6c62c7f217, in release 2.38.17), which was stacked on this branch. After merging main (d8c12befc2), this PR adds two small code changes and their tests.

Spouses who filed separately on the same Montana form (status 2a, through 2023) each take half of a couple's deduction. Two formulas hard-coded that half. They now read gov.states.mt.tax.income.deductions.itemized.spouse_allocation_rate, the parameter that mt_itemized_deductions_indiv already uses:

  1. mt_child_dependent_care_expense_deduction: each spouse's column of the Form 2441-M line 7 amount.
  2. mt_itemized_deductions_for_federal_itemization_indiv: each spouse's part of the couple's charitable deduction. This helper decides federal itemization; the actual deduction already used the parameter, so the two disagreed under a reform of the rate.

The parameter is 0.5 from 2021, so no baseline output changes.

What landed through #9915

For the record, these parts of the original PR are on main and are no longer in this diff:

  • The federal income tax deduction attributed to the spouse who earned the income, then capped at $5,000 each (mt_federal_income_tax_deduction_share, mt_federal_income_tax_deduction_indiv, and the federal-itemization variant on each spouse's own withholding estimate).
  • The care deduction's phase-out on the couple's combined Montana AGI, the full deduction for unmarried filers, no deduction on a separate form, and mt_child_dependent_care_expense_deduction_joint.
  • The elderly homeowner/renter credit's income without regard to loss.

Where main and this branch overlapped, the merge keeps main's version:

  • mt_federal_income_tax_deduction_share gives a zero share when neither filer has positive federal AGI. This branch had given the whole amount to the head. That test is dropped.
  • mt_elderly_homeowner_or_renter_credit_gross_household_income and both elderly credit test files are main's.

Tests

Three test files differ from main:

  • mt_child_dependent_care_expense_deduction.yaml, case 29. At a reformed rate of 0.25 and a $2,400 line 7 amount, each column is $600 and the joint deduction stays $2,400. Main's formula multiplies by 0.5 whatever the parameter says (line 45 on main), which gives $1,200 per column.
  • mt_itemized_deductions_for_federal_itemization_indiv.yaml, five charitable cases. Donations of $0, $400 and $800 at rates 0, 0.25, 0.5, 0.75 and 1. Both separate paths must give each spouse rate × donation, and both joint paths must keep the full donation. Main's mortgage interest case (from Count only deductible mortgage interest in Montana, give Maryland spouses their own losses, report the whole Virginia EITC #10004) is kept above them.
  • mt_federal_income_tax_deduction_indiv.yaml, one case. A spouse's negative federal AGI cannot produce a negative share: wages of $60,000 and a $20,000 business loss give shares [1, 0] and deductions [1_410, 0]. This passes on main too; it guards main's floor at zero.

What has been run:

  • The fix round that added the grid (bfb8c55b5a) reported running the charitable file with and without the helper fix. Without it, the four reformed-rate cases failed in the helper (at rate 0.25 and $800 of donations it returned $400 per spouse, not $200); with it, all cases passed. Main's helper has the same hard-coded 0.5 (line 27 on main).
  • At the merge head, nothing was run locally: the shared host is overloaded. The five merged test files parse and hold no duplicate names, ruff format --check and ruff check pass on the Montana variables, and CI at d8c12befc2 is the test run.
  • The claim that cases fail on current main (ad02ee1b2f) comes from reading main's two lines above, not from a run on main.

No partner contract test files are changed.

Impact

No baseline output changes in any period, so no microsimulation was run for this remainder. That follows from the diff, not from a measurement:

  • The two replaced constants were 0.5, and spouse_allocation_rate is 0.5 from 2021-01-01 with no later value.
  • The care deduction's own parameters also start in 2021, and it is switched off from 2024 (in_effect), before the formula reads the rate.
  • The federal-itemization helper's defined_for gate, mt_married_filing_separately_on_same_return_eligible, reads married_filing_separately_on_same_return_allowed: true for 2021–2023 and false from 2024. Core skips a formula whose gate is false for every person, and mt_itemized_deductions_indiv on main already reads the same rate behind the same gate.

The hub's earlier microsimulation table for this PR (one record, +$537,083 of Montana income tax in 2025) measured the elderly credit change. That change is now on main through #9915 and is not part of this diff. The earlier body is kept at ~/reviews/us-hub/state-allocation-fixes/pr/mt-body-before-main-merge.md on the hub host.

Invariants

  • Care deduction. For a married couple, each column is rate × line 7, so the columns sum to 2 × rate × line 7: exactly line 7 at the default 0.5. The joint deduction uses its own line 7 on mt_agi_joint and does not depend on the rate. Case 29 checks this at 0.25.
  • Charitable split. With the other itemized components at zero, both separate paths give each spouse rate × donation and dependents zero, and both joint paths keep the donation. The five cases check this as a grid, including linearity in the donation and in the rate.
  • Agreement of the two separate paths. mt_itemized_deductions_for_federal_itemization_indiv and mt_itemized_deductions_indiv allocate the charitable deduction identically at every rate. The grid asserts both in each case.

The grid is example-based (15 points). A Hypothesis test was not added for a one-factor multiplication.

Methodology note

  • One rate for two rules. The care deduction's half comes from Form 2441-M line 7 ("enter one-half of the amount from line 7 in columns A and B") and the 2022 instructions, Itemized Deductions Schedule line 14 (PDF p. 35): "your deduction must be divided equally between both spouses". The parameter describes itself as "a mathematical representation of 'divided equally'" for itemized deductions on the same form, and the care deduction is a line of that schedule. So one parameter serves both. It is not a policy lever in Montana law.
  • Charitable allocation is still an approximation. The model has the charitable deduction only for the tax unit, so it splits it by the rate. This PR makes the two paths consistent; it does not claim that a gift made by one spouse belongs equally to both.
  • No positive filer income. When neither spouse has positive federal AGI, main allocates no federal tax to either spouse. The instructions say the spouse who earned the income reports the tax, and do not say what happens here. This branch's earlier choice (the head) is withdrawn in favor of main's.
  • Not modeled, unchanged from before: the §461(l) excess business loss recompute at the single threshold for 2021–2023 (2022 instructions PDF p. 21; 2023 PDF p. 23).

Dependencies

axiom: n/a: no rule changes at the enacted values (two hard-coded one-half factors now read an existing parameter whose value is 0.5). The provisions themselves stay queued in TheAxiomFoundation/rulespec-us#1512.

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 6, 2026 07:08
… the forms

- Federal income tax deduction (through 2023): attribute federal tax to the
  spouse who earned the income and cap each spouse at $5,000, instead of
  prorating the jointly capped amount by Montana AGI (2022 Form 2
  instructions, Itemized Deductions Schedule, line 4).
- Child and dependent care expense deduction (through 2023): phase out on the
  couple's combined Montana AGI and split the result equally only for spouses
  filing separately on the same form; a single filer and a joint return take
  the whole deduction (Form 2441-M lines 3-7).
- Elderly homeowner/renter credit: count federal AGI without regard to losses
  (MCA 15-30-2337(9)(a); Form 2EC line 9).

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

Each spouse filing separately on the same form deducts their own federal
income tax up to $5,000, so both spouses itemize and Montana tax is 6,295.56.

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

- A married person filing on a separate form (federal filing status
  SEPARATE) cannot take the child and dependent care expense deduction:
  "married couples shall file a joint return or file separately on the same
  form" (MCA 15-30-2131(1)(c)(vi)(A)); 2022 instructions, line 14.
- The 2023 credit schedule was the Elderly Homeowner/Renter Credit
  Schedule; it became Schedule 2EC in 2024.

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

DTrim99 commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Align Montana separate-filing deductions and the elderly credit's income with the forms

PR #9877 (author: MaxGhenis, head 6eefe271dc, 3 commits). PR #9882 ("Count household members' income in Montana's elderly homeowner/renter credit") is stacked on this PR: this PR's head is an ancestor of #9882's head. This review covers only #9877's own diff. #9882 is reviewed separately.

Summary

The PR changes three Montana rules:

  1. Federal income tax deduction, spouses filing separately on the same form (TY2021–2023). Each spouse's federal tax is now attributed first, by their share of positive federal AGI, and then capped at $5,000. The old code capped the couple at the $10,000 joint cap before prorating. The federal-itemization variant now uses each spouse's own mt_withheld_income_tax estimate.
  2. Child and dependent care deduction (TY2021–2023).
    • The phase-out now uses the couple's combined Montana AGI (Form 2441-M line 3).
    • The deduction is halved only for married couples filing separately on the same form. Before, every filer got half, including single and head-of-household filers.
    • A married person filing on a separate form now gets no deduction.
    • The new _joint variable feeds the two joint itemized paths. The per-column variable feeds the two separate-column paths.
  3. Elderly homeowner/renter credit (all years). Gross household income now adds back loss_ald, so it is federal AGI "without regard to loss".

The law is applied correctly in all three areas, and I found no wrong result for a household in a year the PR claims.

  • All 78 cases in the 8 changed test files pass on the extracted head tree.
  • CI is green (35/35), and the branch merges cleanly.
  • I hand-checked 14 expectations, including the re-pinned 2021 integration case. Its change is explained by the law.

Two items need attention, both small:

  • S1: the new mt_federal_income_tax_deduction_share has three guard branches that no test exercises, and the test named for one of them does not test it.
  • S2: the care deduction hard-codes 0.5, although the repo has a parameter for exactly this "divided equally" split.

Critical (must fix)

None.

Should address

S1. The guard branches of the new share variable are untested, and the "dependent" case does not test the dependent filter.

  • Where:
    • policyengine_us/variables/gov/states/mt/tax/income/deductions/itemized/federal_tax/mt_federal_income_tax_deduction_share.py:24-33
    • policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_federal_income_tax_deduction_indiv.yaml:78-100
  • What's wrong: the new variable has three guards. No test reaches any of them.
    1. The head_or_spouse filter (lines 24-25). The case "A dependent's income does not draw a share of the parents' federal tax" gives the dependent adjusted_gross_income_person: 0 (line 89). [2_000, 0, 0] therefore passes with or without the filter.
      • Through formulas a dependent's AGI is always 0, because irs_gross_income zeroes dependents. So only a direct input can reach this branch.
      • rulespec-us#1512 noticed the same thing: "Despite its name, this fixture gives the dependent zero income."
    2. The max_(…, 0) floor (lines 25-27). This guard is load-bearing for households that actually occur.
      • On a joint federal return, adjusted_gross_income_person splits loss_ald 50/50 between the spouses, so one spouse's AGI can be negative.
      • Head-tree probe (2022): head wages $60,000 and spouse Schedule C −$20,000. Person AGI is [50_000, -10_000] and federal tax is $1,410.
      • With the floor, the share is [1, 0] and the deduction is [1_410, 0].
      • Without the floor, the shares would be 1.25 and −0.25, and the spouse would get a negative deduction of −$352.50.
    3. The no-positive-income fallback (line 33), where(total_income > 0, share, head), has no test.
  • Source: the 2022 Form 2 instructions, line 4 (PDF p.33), say "the federal income tax withheld should be reported by the spouse who earned the income". A negative share would allocate a negative deduction to a spouse, which that rule cannot produce.
  • Fix: add or adjust these cases (2022, state_filing_status_if_married_filing_separately_on_same_return: SEPARATE). Every expected value was confirmed on the head tree.
    • Dependent filter: give person3 adjusted_gross_income_person: 10_000. Expected:
      • mt_federal_income_tax_deduction_share: [1, 0, 0]
      • mt_federal_income_tax_deduction_indiv: [2_000, 0, 0]
      • Without the filter this would be 1,600 / 0 / 400.
      • Or rename the case to describe what it actually tests.
    • Negative-AGI floor: person1 employment_income: 60_000, person2 self_employment_income: -20_000, no tax input. Expected:
      • mt_federal_income_tax_deduction_share: [1, 0]
      • mt_federal_income_tax_deduction_indiv: [1_410, 0]
      • The input-only version also works: AGI 60,000 / −10,000 with federal tax 6,000 gives [5_000, 0].
    • Zero-income fallback: both adjusted_gross_income_person: 0, income_tax_before_refundable_credits: 1_000. Expected:
      • mt_federal_income_tax_deduction_share: [1, 0]
      • mt_federal_income_tax_deduction_indiv: [1_000, 0]

S2. The care deduction hard-codes the spouse split as 0.5, although spouse_allocation_rate exists for this split.

  • Where: policyengine_us/variables/gov/states/mt/tax/income/deductions/cdcc/mt_child_dependent_care_expense_deduction.py:45:
    share = where(married, head_or_spouse * 0.5, head * 1.0)
  • What's wrong:
    • The repo already has gov.states.mt.tax.income.deductions.itemized.spouse_allocation_rate (policyengine_us/parameters/gov/states/mt/tax/income/deductions/itemized/spouse_allocation_rate.yaml:1-4, value 0.5). Its description reads: "Share of tax unit itemized deductions allocated to each spouse when married filing separately on the same form. This is not a policy parameter but a mathematical representation of 'divided equally'."
    • The sibling mt_itemized_deductions_indiv.py:22 uses that parameter.
    • The care deduction is an itemized (miscellaneous) deduction, and the statute uses the same wording: MCA 15-30-2131(1)(c)(vi)(D), "the deduction for employment-related expenses must be divided equally between the spouses when filing separately on the same form" (MCA 2019).
    • The house rule allows only 0, 1 and −1 as literals. The old formula also used * 0.5, but this PR rewrote the line, so the literal is now the PR's own code.
    • The value 0.5 is correct. Only the pattern is wrong.
  • Fix: a one-line change, share = where(married, head_or_spouse * p_itemized.spouse_allocation_rate, head), with p_itemized = parameters(period).gov.states.mt.tax.income.deductions.itemized. head * 1.0 can simply be head.
    • Optional, since the line is untouched: mt_itemized_deductions_for_federal_itemization_indiv.py:21 hard-codes * 0.5 for the charitable split. It could use the same parameter.

Suggestions

  1. Line 3 of the per-column care deduction sums column AGI after a zero floor.
    • Where: mt_child_dependent_care_expense_deduction.py:36-38. combined_agi sums mt_agi_indiv, which floors each column at 0 (mt_agi_indiv.py:16,26).
    • What the form says:
      • Form 2441-M (2022) line 3: "Add the amounts in columns A and B, line 14 of Form 2" (PDF p.1).
      • Form 2 line 14 has no floor; only line 17 says "If zero or less, enter 0" (2022 Form 2 p.1).
    • When it matters: only when one column is negative. The joint path, which uses mt_agi_joint, is unaffected, and the model takes the lower tax.
    • Fix: sum the unfloored column AGI, or note the simplification in a comment. The floor itself lives in mt_agi_indiv, which predates this PR.
  2. The gainful-employment condition for married filers is not modeled (pre-existing).
    • The law: MCA 15-30-2131(1)(c)(vi)(B): "if the taxpayer is married during any period of the tax year, employment-related expenses incurred are deductible only if: (I) both spouses are gainfully employed … or (II) the spouse is a qualifying individual".
    • The new fixture: Case 25 (mt_child_dependent_care_expense_deduction.yaml:647-676) gives the spouse mt_agi_indiv: 0. Read literally, that is a non-working spouse who is not incapacitated, yet the case expects [700, 700, 0].
    • Fix: add a comment that the fixture assumes the employment conditions are met, as rulespec-us#1512 does. Track the employment test as a follow-up.
  3. Wording about the federal-tax attribution.
    • The federal-itemization variant:
      • mt_federal_income_tax_deduction_for_federal_itemization_indiv.py:22-23 and its test header (…/federal_itemization/mt_federal_income_tax_deduction_for_federal_itemization_indiv.yaml:1-3) cite the federal line 4a withholding rule.
      • But mt_withheld_income_tax is a Montana-rate estimate on each person's AGI (mt_withheld_income_tax.py:13-18). It stands in for federal tax only to choose federal itemization without a circular reference.
      • Suggested wording: "a Montana-rate estimate standing in for federal tax, used only to choose federal itemization".
      • Its unit counterpart mt_federal_income_tax_deduction_for_federal_itemization.py:23 still sums dependents' estimates (pre-existing), while the indiv variant now excludes them.
    • The scope of "the spouse who earned the income":
      • In the 2022 instructions that sentence covers line 4a withholding only (PDF p.33).
      • Lines 4b–4d fall under the general rule (PDF p.11): "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."
      • The AGI-share proxy is reasonable. Say in the code comment and the methodology note that it approximates withholding attribution.
  4. The re-pinned integration case uses the exact bracket subtraction, not the official table's $599.
    • Where: integration.yaml:567-572.
    • The difference: the 2021 tax table says to multiply by 6.9% and subtract $599 (2021 instructions PDF p.15). The marginal brackets subtract $599.20, which gives 6,295.56 against 6,295.96 from the table.
    • This is pre-existing (the old 6,319.02 used it too). Optionally note it in the comment.
  5. Gate the loss add-back on loss_ald being deducted.
    • Where: mt_elderly_homeowner_or_renter_credit_gross_household_income.py:34-35.
    • Risk: the add-back is unconditional. Under a reform that removes loss_ald from gov.irs.ald.deductions, it would add losses that were never subtracted.
    • Fix: mirror dependent_taxable_ss_magi.py:60 ("loss_ald" in parameters(period).gov.irs.ald.deductions). Baseline is correct.
  6. Source-hygiene nits.
    • Reference titles in misc_deductions.yaml:
      • Line 22, "Montana Tax From Instructions 2021", has a typo and links the 2022 instructions; retitle it "2022 Montana Form 2 Instructions, line 14".
      • Line 16's title says "Form 2" but links the instructions.
      • Both titles are pre-existing, but the file is touched.
    • Comments in the care deduction: mt_child_dependent_care_expense_deduction.py:33-34, 41-42 could cite (1)(c)(v) "combined adjusted gross income of the taxpayers" and (1)(c)(vi)(D) next to the form lines.
    • Test name: "2023 Elderly Homeowner/Renter Credit Schedule worked case" (mt_elderly_homeowner_or_renter_credit.yaml:227) can read as an official DOR example. Consider "computed through the 2023 schedule lines".
    • PR body: "Microsim impact: Pending". Fill it in or drop it before merge.
  7. Optional extra tests. All values were hand-computed or confirmed on the head tree.
    • Care deduction, both spouses under $18,000 alone (2021). Columns mt_agi_indiv 15,000 / 15,000, one child, care $3,000. Combined AGI is 30,000; 2,400 − 0.5 × 12,000 is below 0, so the result is [0, 0, 0]. Main gives [1_200, 1_200, 0]. This is the clearest regression guard for the combined-AGI fix.
    • Elderly credit, loss larger than income (2025). A single filer with a pension and a larger Schedule C loss: gross household income equals the pension alone.
    • Joint care variable, unmarried filer (2022). HOH with mt_agi_joint: 20_000, one child, care $3,000 gives 1,400 (full, not halved).

Verified correct

  • Federal income tax deduction: attribute first, then cap.
    • Former MCA 15-30-2131(1)(b) reads "not to exceed $5,000 for each taxpayer filing singly, head of household, or married filing separately".
    • The 2022 instructions line 4 (PDF p.33) say: "you are each limited to the $5,000 federal tax deduction and you cannot arbitrarily allocate this deduction between spouses."
    • mt_federal_income_tax_deduction_indiv.py:29-37 applies min_(tax × share, cap[SEPARATE]) and is gated by applies (false from 2024).
    • Hand checks:
      • 60k/40k with tax 3,000 → [1_800, 1_200].
      • 80k/20k with tax 30,000 → [5_000, 5_000]. The old code gave 5,000 / 2,000.
      • 50k/50k plus a $20k Montana subtraction → [3_000, 3_000].
      • 2024 → [0, 0].
      • The federal-itemization variant: 3,000 / 1,000 → [3_000, 1_000], and 7,000 / 1,000 → [5_000, 1_000].
  • The re-pinned 2021 integration case is explained by the law, not just re-pinned.
    • Federal tax: MFJ, 160,000 − 25,100 = 134,900. 1,990 + 7,338 + 11,847 = 21,175.
    • Attribution: 90/160 and 70/160 give 11,911 and 9,264, so each spouse is capped at 5,000. Both exceed the $4,830 standard deduction, so both itemize.
    • Taxable income: after 2,580 exemptions, 82,420 and 62,420.
    • Tax: 0.069 × 82,420 − 599.20 = 5,087.78, and 0.069 × 62,420 − 599.20 = 3,707.78. After the two $1,250 rebates the total is 6,295.56.
    • The old 6,319.02: the spouse got 10,000 × 70/160 = 4,375, which is below 4,830, so the spouse took the standard deduction. That gave taxable income 62,590 and tax 3,719.51.
    • Path choice: the joint path gives 6,894.76, so filing separately still wins.
  • Child and dependent care deduction.
    • Combined AGI: Form 2441-M line 3 adds columns A and B, and the statute's (1)(c)(v) speaks of "combined adjusted gross income of the taxpayers".
    • Halving: line 7 halves only "If you are married filing separately on the same form", which matches (1)(c)(vi)(D).
    • Separate forms: (vi)(A) says "married couples shall file a joint return or file separately on the same form".
    • Legally separated individuals: (vi)(C) says they "may not be considered as married". Federally they are unmarried (§7703(a)(2)) and file single or HOH, so the SEPARATE gate does not reach them.
    • Hand checks:
      • Case 25: 2,400 − 0.5 × (20,000 − 18,000) = 1,400, so [700, 700, 0]. Main gives [700, 1_200].
      • Case 26: joint 1,400.
      • Case 27: 2024 → 0.
      • Case 28: separate form → 0 on both variables.
      • Cases 21 and 24 have combined AGI of exactly 18,000, so there is no reduction (this pins the boundary).
      • The 14 re-pinned single-filer cases now get the full line 7 amount, for example 2,400 − 0.5 × 2,000 = 1,400.
    • Head probe: HOH with $20k gives 1,400 on both itemized paths. Before, both paths gave half.
  • Wiring and callers (grep at head).
    • mt_child_dependent_care_expense_deduction feeds only the two separate-column aggregates, and _joint feeds only the two joint aggregates.
    • mt_misc_deductions now holds only casualty_loss_deduction and is used only by those four aggregates.
    • The unchanged mt_itemized_deductions_indiv.yaml case 1 (inputs casualty 400 and care 123, expecting 9,188) would fail on a double count (it would give 9,311). So exactly-once inclusion is guarded on the separate path, and the changed joint aggregate tests cover the joint path.
    • mt_federal_income_tax_deduction_share has one caller. The federal-itemization variants feed only state_itemized_deductions, which is the federal itemization choice.
  • Elderly credit gross household income.
    • The law: MCA 15-30-2337(9)(a) reads "federal adjusted gross income, without regard to loss". The 2023 schedule line 9 says "The gross household income cannot be reduced by any losses" (PDF p.52).
    • Why the add-back works: irs_gross_income counts only positive amounts, and loss_ald holds the deducted losses, so AGI + loss_ald is AGI without regard to loss. The add-back sits once, on the head, and both consumers sum over the tax unit.
    • Hand checks:
      • 2023 worked case: AGI 17,000 + untaxed SS 10,000 + loss 3,000 = 30,000. Then (30,000 − 12,600) × 0.05 = 870, and 2,000 − 870 = 1,130 (under the $1,150 cap). Main gives 1,150.
      • 2023 capital-loss-only case: 20,000.
      • 2024 spouse Schedule C loss: person AGI is 17,500 / −2,500 plus the 5,000 add-back, a household total of 20,000. (20,000 − 12,600) × 0.035 = 259 on the head.
  • Code patterns otherwise clean.
    • All formulas are vectorized (where, max_, the masked division). Python if appears only on scalar parameters (in_effect, applies).
    • Entities and periods are right (Person columns, TaxUnit joint and eligible-expense variables, YEAR).
    • defined_for fits each variable.
    • Every reference is a proper tuple (the old one-string-in-parens references gained their commas).
    • Every new #page=N was opened and shows the quoted text.
    • All test periods are plain.
  • Tests run. On the extracted head tree, the 8 changed YAML files ran in one process: 78/78 passed (local policyengine-core 3.26).
  • Changelog. changelog.d/fix-mt-separate-filing-allocations.fixed.md is top-level, matches the branch name, and has type fixed. Its text matches the behavior.

Axiom line assessment

  • The line: axiom: former MCA 15-30-2131(1)(b)-(c), MCA 15-30-2337(9)(a) TheAxiomFoundation/rulespec-us#1512 queued. The format is valid.
  • The issue: rulespec-us#1512 is OPEN and labelled pe-parity. It was updated 2026-10-06T22:57Z, after head 6eefe271dc, and includes Case 28 and the schedule-name correction.
  • Dispatch-readiness: it is dispatch-ready on the model of Add AMT parameters for 2020-2023 #1467. It has:
    • module paths: us-mt/statutes/15-30-2131/1/b.yaml, …/1/c.yaml, and 15-30-2337/2338/2341;
    • corpus citations;
    • verbatim §2131(1)(b) and (c)(i), (v), (vi)(A)/(D), and §2337(4), (8), (9);
    • required outputs;
    • companion tests from Form 2441-M, the line 4 instructions, and the 2023/2024 schedules.
  • Prerequisites: the issue states its corpus prerequisites explicitly (historical §2131 ingestion and form ingestion), as Add AMT parameters for 2020-2023 #1467 does.
  • Current behavior: its descriptions match head behavior. Neither S1 nor S2 changes behavior, so the issue stays accurate after the fixes.

CI status

All green: gh pr checks 9877 shows 35/35 passing at head 6eefe271dc.

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.

The law and form alignment checks out (attribute-then-cap, the care deduction on combined AGI split only on a same-form return, the re-pinned 2021 case, the 2EC loss add-back), and rulespec-us#1512 is dispatch-ready. Two should items (see the comment above): S1, the new federal-tax-deduction share has three untested guards (the dependent filter, the max(AGI, 0) floor, the zero-income fallback), and S2, the care-deduction split hard-codes 0.5 where deductions.itemized.spouse_allocation_rate already exists. #9882 is stacked on this PR and is reviewed separately.

@MaxGhenis
MaxGhenis marked this pull request as draft October 8, 2026 23:18
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 9, 2026 05:37
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

@DTrim99, both should-items from your 10/7 review are addressed at head bfb8c55b5a:

  • S1, the three untested guards in mt_federal_income_tax_deduction_share: mt_federal_income_tax_deduction_indiv.yaml now has a case for each guard.
    • "A dependent's income does not draw a share of the parents' federal tax" tests the dependent filter.
    • "A spouse's negative federal AGI cannot produce a negative tax share" tests the max_(…, 0) floor.
    • "With no positive federal AGI the head receives the federal tax deduction" tests the zero-income fallback.
  • S2, the hard-coded 0.5: the care deduction now uses gov.states.mt.tax.income.deductions.itemized.spouse_allocation_rate (mt_child_dependent_care_expense_deduction.py:46). The federal-itemization charity helper's 0.5 literal is replaced the same way, and a rate grid (0, 0.25, 0.5, 0.75, 1) tests it.

Measured impact is in the body. In 2025, mt_income_tax is +$537,083 on one record, weighted to 1,330 households. That is the elderly credit's loss add-back; the deduction changes end after 2023. Could you re-review?

@MaxGhenis
MaxGhenis requested a review from DTrim99 October 9, 2026 05:38
@DTrim99

DTrim99 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Align Montana separate-filing deductions and the elderly credit's income with the forms

PR #9877 (author: MaxGhenis). Round 1 reviewed head 6eefe271dc. This round reviews head bfb8c55b5a. Three commits were added, with no merges:

  • c302dd3089: the care deduction uses spouse_allocation_rate.
  • dd4537620b: tests for the guards in the federal-tax share variable.
  • bfb8c55b5a: the federal-itemization helper's charitable split uses spouse_allocation_rate, with a five-rate test grid.

PR #9882 is still stacked on this branch.

Summary

Both should-address items from round 1 are fixed.

  • S1: each of the three guards in mt_federal_income_tax_deduction_share now has its own test. By hand calculation, each test would fail if its guard were removed (see the checks below).
  • S2: the care deduction reads the spouse allocation parameter instead of a hard-coded 0.5. The optional cleanup I raised in round 1, the same literal in the federal-itemization charity split, is done too.

The charitable "fix" in bfb8c55b5a.

  • What it changes: the federal-itemization helper now reads the same parameter as the actual separate-column deduction. Before, it hard-coded 0.5, while the actual deduction already read spouse_allocation_rate.
  • Effect: none at baseline, because the rate is 0.5. Results change only if a reform moves the rate, and then both separate paths stay in step.
  • The law:
    • For spouses filing separately on the same form, ARM 42.15.524(3)(b) lets a deduction "not clearly attributable to one spouse ... be divided as provided on the form".
    • The 2021–2023 instructions say such payments "can be allocated to either spouse in any proportional amount".
    • So for charity, 0.5 is a modeling convention, not a statutory split, and the PR's methodology note says this correctly.

Tests and CI.

  • Tests: on the extracted head tree, the three changed YAML files ran in one process and 45/45 passed (local policyengine-core 3.26). That covers all 29 care cases, all 9 federal-itemization cases and all 7 federal-tax cases.
  • CI is green at bfb8c55b5a (26/26 jobs).

One action before merge. The branch now conflicts with main. #10004 landed after CI ran and appended a test case to the same YAML file as the new charity grid. The fix is mechanical (keep both blocks). After that, CI has to re-run.

I found no new critical or should-address problems. Two new suggestions follow.

Round-1 items

Item Status Evidence at bfb8c55b5a
S1. Three untested guards in mt_federal_income_tax_deduction_share ADDRESSED mt_federal_income_tax_deduction_indiv.yaml. (1) The dependent case (lines 78-103) now gives the dependent adjusted_gross_income_person: 10_000 (line 91) and asserts mt_federal_income_tax_deduction_share: [1, 0, 0] (line 102). (2) "A spouse's negative federal AGI cannot produce a negative tax share" (lines 105-129) asserts [1, 0] and [1_410, 0]. (3) "With no positive federal AGI the head receives the federal tax deduction" (lines 131-151) asserts [1, 0] and [1_000, 0]. These match the round-1 proposals exactly.
S2. Care deduction hard-codes 0.5 ADDRESSED mt_child_dependent_care_expense_deduction.py:45-46: p_itemized = parameters(period).gov.states.mt.tax.income.deductions.itemized and share = where(married, head_or_spouse * p_itemized.spouse_allocation_rate, head). head * 1.0 is now head. New Case 29 (mt_child_dependent_care_expense_deduction.yaml:753-783) reforms the rate to 0.25 and checks that the formula reads it.
S2 option: the charity 0.5 in mt_itemized_deductions_for_federal_itemization_indiv.py ADDRESSED Lines 18-23 now use p.spouse_allocation_rate, and the unused gov.irs.deductions handle is gone. No * 0.5 literal remains under variables/gov/states/mt.
Sugg. 1: care line 3 sums the floored column AGI NOT ADDRESSED (optional) mt_child_dependent_care_expense_deduction.py:36-38 is unchanged, with no comment.
Sugg. 2: gainful-employment condition (pre-existing) NOT ADDRESSED (optional) Case 25 (mt_child_dependent_care_expense_deduction.yaml:647-676) has no assumption comment.
Sugg. 3: federal-tax attribution wording NOT ADDRESSED (optional) mt_federal_income_tax_deduction_for_federal_itemization_indiv.py:22-23 and its test header (lines 1-3) are unchanged. The methodology note in the PR body does describe the helper as a withholding estimate.
Sugg. 4: comment on the integration case's $599 vs $599.20 NOT ADDRESSED (optional) integration.yaml is untouched this round.
Sugg. 5: gate the loss add-back on loss_ald being deducted NOT ADDRESSED (optional) mt_elderly_homeowner_or_renter_credit_gross_household_income.py:35 is unchanged. Baseline is correct.
Sugg. 6: source-hygiene nits PARTIALLY The PR body now has a measured microsimulation table, so "Pending" is gone. Still open: misc_deductions.yaml:22 ("Montana Tax From Instructions 2021", which links the 2022 PDF) and line 16; the comments in the care deduction; and the test name at mt_elderly_homeowner_or_renter_credit.yaml:227.
Sugg. 7: optional extra tests NOT ADDRESSED (optional) None of the three proposed cases was added.

Max's 2026-10-09 comment responds only to S1 and S2. The suggestions were optional and do not block.

Critical (must fix)

None.

Should address

None open.

New suggestions

  1. Trim the allocation-rate grid. It uses up the Montana test tree's override budget for little added coverage.
    • Where:
      • mt_itemized_deductions_for_federal_itemization_indiv.yaml:78-295: five near-identical cases of about 44 lines each, at rates 0.5, 0, 0.25, 0.75 and 1.
      • Case 29 in mt_child_dependent_care_expense_deduction.yaml (rate 0.25).
    • Why it costs:
      • Each distinct dotted-path override makes policyengine-core deep-copy and cache a whole tax-benefit system for the life of the test process.
      • CI batches gov/states by whole state subdirectory, so every Montana YAML file shares that process.
      • The Montana tree had no overrides on main. It now has four distinct values (0, 0.25, 0.75, 1), which is the practical ceiling before states shards start stalling.
      • The next Montana test that needs an override would push the tree past that ceiling.
    • Why most of it is redundant:
      • Rate 0.25 alone already tells the fixed helper from the old one. The PR body says the pre-fix failures were "at rate 0.25 and $800 of donations".
      • The 0, 0.75 and 1 cases repeat the same linear product.
      • At 0.75 and 1, the two columns together claim 150% and 200% of the couple's charity, which is not an allocation the form allows.
    • Fix: keep the default-rate case and the 0.25 case. That value is shared with Case 29, so the tree drops to one distinct override. Optionally, fold the two cases into one with a charitable_deduction input instead of axes.
  2. Optional: say in the parameter that the charity split is a modeling convention.
    • The two uses now carry different legal weight:
      • Care deduction: the law requires an equal split, "the deduction for employment-related expenses must be divided equally between the spouses when filing separately on the same form" (MCA 15-30-2131(1)(c)(vi)(D)).
      • Charity: for filing status 2a, ARM 42.15.524(3)(b) says a deduction "not clearly attributable to one spouse may be divided as provided on the form". The form allows "any proportional amount" (2022 Form 2 instructions, PDF p.11; also 2021 PDF p.10 and 2023 PDF p.13).
      • Under (3)(a), "an itemized deduction clearly attributable to one spouse may be claimed only by the spouse to whom it is attributable".
    • What the parameter says: spouse_allocation_rate.yaml:1 calls the value a "mathematical representation of 'divided equally'". That is accurate for care and only an approximation for charity.
    • Its references: the ARM 42.15.524 link points to a rule repealed effective 1/25/2025 (2025 MAR 42-1088). The second reference is a landing page.
    • Fix: add a comment in the two charity formulas, or in the parameter, that 0.5 approximates the "any proportional amount" choice. Point the reference at the instructions page above.
    • The parameter itself is pre-existing, and the PR body's methodology note already says this. This would put it in the code.

Charitable allocation fix: verification

  • The law (TY2021–2023, filing status 2a).
    • Montana's charitable deduction is the federal one, subject to a Montana-AGI percentage limit: "The charitable contributions allowed as a deduction in computing your net income for Montana income tax purposes are those same contributions allowed as a deduction for federal income tax purposes" (2022 instructions, PDF p.35; 2023 PDF p.38).
    • Between spouses, the rule is: "Deductions that are attributable to only one spouse must be claimed by that spouse ... 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" (2022 PDF p.11).
    • ARM 42.15.524(3)(a)-(b) says the same.
    • A 50/50 split of a joint gift is therefore a permitted allocation, and the model's existing approximation stays in place. The PR body discloses both the approximation and the omitted Montana-AGI limit.
  • The code.
    • mt_itemized_deductions_for_federal_itemization_indiv.py:21-23 now matches mt_itemized_deductions_indiv.py:21-23 line for line.
    • The joint variants still claim the full charitable_deduction on the head (..._joint.py:18,34).
    • Callers: the helper feeds only state_itemized_deductions, through the federal-itemization choice, and the parameter has only these three readers. Baseline output is unchanged.
  • Hand checks (2022; wages $2,000 per spouse, so federal tax and the Montana withholding estimate are both 0; SALT here counts only real estate tax, which is 0).
    1. Rate 0.25, $800 of donations: each spouse gets 0.25 × 800 = 200 on both separate paths, the dependent gets 0, and the joint paths give the head 800. This matches […, 200, 200, 0] / […, 800, 0, 0].
    2. Rate 0.75, $400 of donations: 300 per spouse, matching […, 300, 300, 0, …].
    3. Rate 0: both separate paths give 0, and the joint path is still 400 / 800.
    4. Care Case 29: line 2 = min(2,400, 2,400) = 2,400. Combined AGI is 9,000 + 9,000 = 18,000, which is not over the threshold, so line 7 = 2,400. Each column gets 0.25 × 2,400 = 600, and the joint deduction is 2,400 (mt_agi_joint 18,000). This matches [600, 600, 0] and 2_400.
    5. S1 floor case: MFJ federal AGI is 60,000 − 20,000 = 40,000. Less the $25,900 standard deduction, taxable income is 14,100, and 10% gives 1,410. Person AGI is 50,000 / −10,000; with the floor the shares are [1, 0], giving [1_410, 0]. Without the floor the shares would be 1.25 / −0.25, giving 1,762.50 / −352.50, so the case discriminates.
    6. S1 dependent case: income [40,000, 0, 0] gives shares [1, 0, 0] and [2_000, 0, 0]. Without the filter the shares would be 0.8 / 0 / 0.2.
    7. S1 fallback: no positive income, so the share goes to the head, giving [1, 0] and [1_000, 0]. Without the fallback both would be 0.
  • Changelog. The fragment changelog.d/fix-mt-separate-filing-allocations.fixed.md gained one accurate sentence on the shared allocation rate. It is still top-level and still type fixed.
  • Axiom line. It is unchanged and still valid: rulespec-us#1512 is OPEN and labelled pe-parity. No round-2 commit changes baseline behavior, so the issue (updated 2026-10-06) still matches the PR.

CI status

Branch status

Verdict: APPROVE

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

Round 2: both should items are addressed. The three share guards are now tested directly, and the care deduction uses deductions.itemized.spouse_allocation_rate. The charity-allocation change is baseline-neutral and consistent with ARM 42.15.524(3)(b). Two optional suggestions, notably trimming the five-rate grid to stay under the per-state override memory ceiling. Before merging: the branch now conflicts with main (#10004) in one test file, where both sides appended a case and you should keep both. Please merge main and re-run CI; #9882 is stacked on this.

Most of this branch landed on main through #9915. Main's versions win
where the two overlap:

- mt_federal_income_tax_deduction_share keeps main's zero allocation
  when neither filer has positive income; the branch's head-fallback
  test is dropped.
- mt_elderly_homeowner_or_renter_credit_gross_household_income and both
  elderly credit test files are main's.

What the branch still adds over main:

- The care deduction and the federal-itemization charitable split use
  gov.states.mt.tax.income.deductions.itemized.spouse_allocation_rate
  in place of a hard-coded half.
- Tests: care deduction case 29, the five-rate charitable grid, and the
  floor on a spouse's negative federal AGI in the tax share.

The changelog fragment now describes only that remainder; the earlier
text was released in 2.38.x.

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

Copy link
Copy Markdown
Contributor Author

Hand-off to the US + core hub

Head: d8c12befc23410fd185bd1c51ee31c95d99a8784, a plain merge of main (ad02ee1b2f) into the branch. There was no force-push or rebase, so #9888's stack is intact.

Scope now: most of this PR landed through #9915. The rest is 6 files, +284/−3:

  • the care deduction and the federal-itemization charitable split read spouse_allocation_rate in place of a hard-coded 0.5;
  • tests for those two changes.

No baseline output changes in any period, so there's no impact run. The body has the details.

Review: an independent review at this head gave APPROVE.

  • It was a Subfleet job (20261011-205135-us9877-r4), run on Opus 5.5 after the GPT-6.1 Sol attempt timed out on a Codex lane.
  • The review is saved at ~/reviews/us-hub/state-allocation-fixes/review/mt/review-r4.md on the hub host.
  • It found no P1 or P2 issues. It raised two body points (the helper's gate wording, and where the mortgage case came from); both are now fixed in the body, and I checked each against the code and git history.
  • DTrim99's approval is from bfb8c55b5a, the previous head.

Checks at this head: passed:

  • PR validation (lock, guard tests, lint, changelog);
  • Package and compatibility.

Still pending in run 38187744873: the Baseline, Contrib and Python suites, Household API Partners, and Quick Feedback. No partner test files changed. Mergeable: MERGEABLE.

Dependencies:

Holds and decisions: none.

Follow-ups:

Axiom: n/a, since no rule changes at the enacted values. The provisions stay queued in TheAxiomFoundation/rulespec-us#1512.

@MaxGhenis
MaxGhenis merged commit b686c9f into main Oct 12, 2026
26 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#9877 at d8c12befc23410fd185bd1c51ee31c95d99a8784 (squash): Montana's separate-filing care deduction and charitable split read the spouse allocation rate.

@MaxGhenis
MaxGhenis deleted the fix-mt-separate-filing-allocations branch October 12, 2026 09: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.

2 participants