From 8ccea67e37cc50c8ae3153641a132eeb2c780604 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 8 Nov 2026 14:59:15 -0500 Subject: [PATCH 01/11] Count tier 1 railroad benefits in Minnesota's Social Security provisions Minn. Stat. 290.0132 subd. 26(i) gives "Social Security benefits" the IRC 86(d)(1) meaning, so the line 12 worksheet runs on title II and tier 1 benefits together. Provisional income now starts from gross income less all taxable benefits and adds half of the head's and spouse's section 86 benefits; the simplified method reduces all taxable benefits and then takes out the tier 1 benefits that the 231m subtraction removes (steps 26-29), and below the threshold line 12 is the title II share. The elderly and disabled subtraction applies 290.0802 subd. 1: the reduction counts all of the head's and spouse's railroad retirement benefits, and AGI leaves out the taxable ones. The homestead refund adds nontaxable tier 1 benefits to M1PR line 2, and the marriage credit gets tests for leaving out taxable tier 1 benefits, which Minnesota taxable income never includes. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 1 + .../total_benefit_fraction.yaml | 2 +- .../mn_elderly_disabled_subtraction.yaml | 90 ++++++++++ .../mn/tax/income/mn_marriage_credit.yaml | 64 +++++++ .../mn_social_security_subtraction.yaml | 157 ++++++++++++++++++ ...estead_credit_refund_household_income.yaml | 28 ++++ .../mn_elderly_disabled_subtraction.py | 46 ++++- .../mn_social_security_subtraction.py | 77 ++++++--- ...omestead_credit_refund_household_income.py | 18 +- 9 files changed, 449 insertions(+), 34 deletions(-) create mode 100644 changelog.d/state-sseb-worksheets.fixed.md diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md new file mode 100644 index 00000000000..77babfeec93 --- /dev/null +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -0,0 +1 @@ +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, elderly and disabled subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0802, 290.0675 and 290A.03 define them, without subtracting them twice. diff --git a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml index cba19854f21..0d8ffbf280e 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml @@ -1,4 +1,4 @@ -description: Minnesota allows subtraction from federal adjusted gross income of the lesser of US-taxable social security and an alternative amount with the complex calculation of the alternative subtraction amount including this fraction which is applied to total social security benefits. +description: Minnesota counts this fraction of the head's and spouse's social security benefits, title II and tier 1 railroad retirement benefits together (Form 1040 line 6a), in provisional income for the alternate social security subtraction. values: 2021-01-01: 0.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml index 88fbcdcd603..620b4281a0e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_elderly_disabled_subtraction.yaml @@ -120,3 +120,93 @@ state_code: MN output: mn_elderly_disabled_subtraction: 0 # = 12_000 - 0.5 * (42_000 - 18_000) + +# Minn. Stat. 290.0802, subd. 1(c): nontaxable retirement and disability +# benefits are the amounts section 22(c)(3) counts (title II and tier 1 +# benefits section 86 leaves out, and the tax-free rest of the railroad +# annuity) plus pension and annuity benefits in federal gross income that the +# state cannot tax (the taxable railroad benefits, 45 U.S.C. 231m(a)). +# Subd. 1(a): adjusted gross income leaves out those taxable railroad benefits. +- name: MN elderly/disabled subtraction counts all railroad retirement benefits in the reduction + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + railroad_social_security_equivalent_benefits: 6_000 + taxable_railroad_social_security_equivalent_benefits: 0 + taxable_railroad_pension_benefits: 2_000 + tax_exempt_railroad_pension_benefits: 500 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 12_000 + households: + household: + members: [person1] + state_code: MN + output: + # Reduction: 6,000 + 500 + 2,000 = 8,500, so 9,600 - 8,500 = 1,100. + # AGI: 12,000 - 2,000 = 10,000, under the $14,500 threshold. + mn_elderly_disabled_subtraction: 1_100 + +- name: MN elderly/disabled subtraction takes taxable railroad benefits out of AGI + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 66 + social_security: 4_000 + taxable_social_security: 600 + railroad_social_security_equivalent_benefits: 3_000 + taxable_railroad_social_security_equivalent_benefits: 500 + taxable_railroad_pension_benefits: 1_500 + tax_exempt_railroad_pension_benefits: 200 + spouse: + is_tax_unit_spouse: true + age: 66 + tax_units: + tax_unit: + members: [head, spouse] + adjusted_gross_income: 21_000 + households: + household: + members: [head, spouse] + state_code: MN + output: + # Reduction: (4,000 - 600) + 3,000 + 1,500 + 200 = 8,100, so + # 12,000 - 8,100 = 3,900. + # AGI: 21,000 - (500 + 1,500) = 19,000; both spouses qualify, so the + # threshold is 14,500 + 3,500 = 18,000, and half the 1,000 excess is 500. + # 3,900 - 500 = 3,400. + mn_elderly_disabled_subtraction: 3_400 + +- name: MN elderly/disabled subtraction leaves out a dependent's social security + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 70 + social_security: 5_000 + taxable_social_security: 0 + grandchild: + age: 12 + social_security: 3_000 + tax_units: + tax_unit: + members: [head, grandchild] + adjusted_gross_income: 0 + households: + household: + members: [head, grandchild] + state_code: MN + output: + # Section 22(c)(3) counts what the individual receives, so only the + # head's $5,000 reduces the $9,600 base. + mn_elderly_disabled_subtraction: 4_600 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_marriage_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_marriage_credit.yaml index 0db4e5766fa..6047f1fd784 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_marriage_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_marriage_credit.yaml @@ -129,3 +129,67 @@ state_code: MN output: mn_marriage_credit: 1_851.44 + +# Minn. Stat. 290.0675, subd. 1(b) counts each spouse's earned income, +# pensions and IRC 86(d)(1) Social Security benefits "to the extent included in +# Minnesota taxable income". Railroad retirement benefits never are: the state +# subtracts them (45 U.S.C. 231m(a)). +- name: MN marriage credit 2025 counts a spouse's taxable title II benefits + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 66 + employment_income: 60_000 + spouse: + is_tax_unit_spouse: true + age: 66 + taxable_social_security: 35_000 + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + mn_taxable_income: 90_000 + households: + household: + members: [head, spouse] + state_code: MN + output: + # Lesser earner: 35,000. Joint tax on 90,000: 0.0535 x 47,620 + 0.068 x + # 42,380 = 5,429.51. Lesser earner less half the joint standard deduction: + # 35,000 - 14,950 = 20,050, taxed at single rates: 1,072.675. The rest, + # 69,950: 0.0535 x 32,570 + 0.068 x 37,380 = 4,284.335. + # 5,429.51 - 1,072.675 - 4,284.335 = 72.50. + mn_marriage_credit: 72.50 + +- name: MN marriage credit 2025 leaves out a spouse's taxable tier 1 railroad benefits + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 66 + employment_income: 60_000 + spouse: + is_tax_unit_spouse: true + age: 66 + railroad_social_security_equivalent_benefits: 40_000 + taxable_railroad_social_security_equivalent_benefits: 35_000 + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + mn_taxable_income: 90_000 + households: + household: + members: [head, spouse] + state_code: MN + output: + # The spouse's $35,000 of taxable tier 1 benefits are subtracted from + # Minnesota taxable income, so the spouse has no income under subd. 1(b) + # and the couple misses the $31,000 minimum. Schedule M1MA line 4 would + # carry Form 1040 line 6b, which includes them. + mn_marriage_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml index e90fead9244..8ca0511022c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml @@ -185,3 +185,160 @@ # step 9: 70,000; step 11: 70,000 - 62,090 = 7,910; step 12: 1,582; # step 14: 4,090 - 1,582 = 2,508; step 18: min(2,508, 8,500). mn_social_security_subtraction: 2_508 + +# Tier 1 railroad retirement benefits. Minn. Stat. 290.0132, subd. 26(i) gives +# "Social Security benefits" the meaning in IRC 86(d)(1), so Form 1040 lines 6a +# and 6b carry title II and tier 1 benefits together. The model subtracts the +# taxable tier 1 benefits with the rest of the railroad annuity +# (taxable_railroad_benefits, Schedule M1M line 17), so line 12 leaves them out +# as steps 26-29 of the 2025 worksheet for line 12 do. +- name: MN social security subtraction 2025 single below the threshold leaves the tier 1 benefits to line 17 + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + social_security: 20_000 + taxable_social_security: 8_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_social_security_equivalent_benefits: 4_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 50_000 + households: + household: + members: [person1] + state_code: MN + output: + # AGI is under $84,490, so line 12 takes all of Form 1040 line 6b, + # $12,000, and line 17 leaves out the tier 1 benefits. The model's line 12 + # is the $8,000 of taxable title II benefits, and the $4,000 of taxable + # tier 1 benefits come off once, through taxable_railroad_benefits. + mn_social_security_subtraction: 8_000 + taxable_railroad_benefits: 4_000 + mn_subtractions: 12_000 + +- name: MN social security subtraction 2025 single simplified method takes out the tier 1 benefits after the phase-out + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + social_security: 20_000 + taxable_social_security: 17_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_social_security_equivalent_benefits: 8_500 + irs_gross_income: 92_000 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 92_000 + households: + household: + members: [person1] + state_code: MN + output: + # Steps 1-5: (92,000 - 84,490) / 4,000 = 1.88, rounded up to 2, so 20%. + # Steps 6-8: 25,500 - 20% x 25,500 = 20,400. + # Steps 9-19: 92,000 - 25,500 + 50% x 30,000 = 81,500 of provisional + # income. Steps 20-24: 4,560 - 20% x (81,500 - 69,250) = 2,110. + # Steps 25-28: min(2,110, 25,500 - 8,500) = 2,110. + # Step 29: 20,400 - 8,500 = 11,900. Step 30: 11,900. + # With line 17's 8,500, the subtractions total 20,400. + mn_social_security_subtraction: 11_900 + mn_subtractions: 20_400 + +- name: MN social security subtraction 2025 joint provisional income counts tier 1 benefits + absolute_error_margin: 0.01 + period: 2025 + input: + filing_status: JOINT + social_security: 60_000 + taxable_social_security: 51_000 + railroad_social_security_equivalent_benefits: 40_000 + taxable_railroad_social_security_equivalent_benefits: 34_000 + irs_gross_income: 150_000 + adjusted_gross_income: 150_000 + state_code: MN + output: + # Steps 1-8: (150,000 - 108,320) / 4,000 rounds up to 11, capped at 10, + # so the simplified subtraction is $0. + # Steps 9-19: 150,000 - 85,000 + 50% x 100,000 = 115,000. + # Steps 20-24: 5,840 - 20% x (115,000 - 88,630) = 566. + # Steps 25-28: min(566, 85,000 - 34,000) = 566. + mn_social_security_subtraction: 566 + +- name: MN social security subtraction 2025 joint alternate method capped at taxable title II benefits + absolute_error_margin: 0.01 + period: 2025 + input: + filing_status: JOINT + social_security: 1_000 + taxable_social_security: 850 + railroad_social_security_equivalent_benefits: 99_000 + taxable_railroad_social_security_equivalent_benefits: 84_150 + irs_gross_income: 145_000 + adjusted_gross_income: 145_000 + state_code: MN + output: + # Steps 1-8: (145,000 - 108,320) / 4,000 rounds up to 10, so $0. + # Steps 9-19: 145,000 - 85,000 + 50% x 100,000 = 110,000. + # Steps 20-24: 5,840 - 20% x (110,000 - 88,630) = 1,566. + # Steps 25-28: min(1,566, 85,000 - 84,150) = 850. + mn_social_security_subtraction: 850 + +- name: MN social security subtraction 2022 provisional income counts tier 1 benefits + absolute_error_margin: 0.01 + period: 2022 + input: + filing_status: JOINT + social_security: 60_000 + taxable_social_security: 51_000 + railroad_social_security_equivalent_benefits: 40_000 + taxable_railroad_social_security_equivalent_benefits: 34_000 + irs_gross_income: 120_000 + adjusted_gross_income: 120_000 + state_code: MN + output: + # 2022 worksheet for line 12: steps 1-11, 120,000 - 85,000 + 50% x + # 100,000 = 85,000; steps 12-16, 5,450 - 20% x (85,000 - 82,770) = + # 5,004; steps 17-20, min(5,004, 85,000 - 34,000) = 5,004. + mn_social_security_subtraction: 5_004 + +- name: MN social security subtraction 2025 provisional income leaves out a dependent's benefits + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 70 + social_security: 100_000 + taxable_social_security: 85_000 + irs_gross_income: 150_000 + spouse: + is_tax_unit_spouse: true + age: 68 + child: + age: 15 + social_security: 12_000 + tax_units: + tax_unit: + members: [head, spouse, child] + filing_status: JOINT + adjusted_gross_income: 150_000 + households: + household: + members: [head, spouse, child] + state_code: MN + output: + # Step 12 takes Form 1040 line 6a, which holds the filers' benefits only; + # the child's $12,000 belongs on the child's return (IRS Pub. 915). + # Provisional income: 150,000 - 85,000 + 50% x 100,000 = 115,000. + # Steps 20-24: 5,840 - 20% x (115,000 - 88,630) = 566. + mn_social_security_subtraction: 566 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml index cfa6d3c978a..43b5b933b7a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml @@ -22,3 +22,31 @@ output: traditional_ira_deduction: 0 mn_homestead_credit_refund_household_income: 93_000 + +- name: Line 2 adds nontaxable tier 1 railroad retirement benefits + # 2025 M1PR instructions, line 2: box 5 of Form SSA-1099 or RRB-1099 less + # the taxable part on Form 1040 line 6b. Minn. Stat. 290A.03, subd. + # 3(a)(2)(v) counts railroad retirement benefits and Social Security Act + # payments. + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 70 + social_security: 10_000 + taxable_social_security: 1_000 + railroad_social_security_equivalent_benefits: 4_000 + taxable_railroad_social_security_equivalent_benefits: 400 + tax_units: + tax_unit: + members: [head] + adjusted_gross_income: 20_000 + households: + household: + members: [head] + state_code: MN + output: + # 20,000 + (10,000 - 1,000) + (4,000 - 400) - 5,200 (age 65 or older). + mn_homestead_credit_refund_household_income: 27_400 diff --git a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py index 0f2226338c4..50eb9e13683 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py @@ -7,9 +7,19 @@ class mn_elderly_disabled_subtraction(Variable): label = "Minnesota elderly/disabled subtraction" unit = USD definition_period = YEAR + documentation = ( + "Schedule M1R. Minn. Stat. 290.0802 reduces the base amount by the " + "nontaxable retirement and disability benefits of subd. 1(c), which " + "take in all of the head's and spouse's railroad retirement benefits, " + "and measures adjusted gross income (subd. 1(a)) without the railroad " + "benefits in federal gross income, which 45 U.S.C. 231m(a) keeps the " + "state from taxing." + ) reference = ( + "https://www.revisor.mn.gov/statutes/cite/290.0802", "https://www.revenue.state.mn.us/sites/default/files/2023-01/m1r_21.pdf", "https://www.revenue.state.mn.us/sites/default/files/2023-01/m1r_22.pdf", + "https://www.revenue.state.mn.us/sites/default/files/2026-07/m1r-25.pdf", ) defined_for = StateCode.MN @@ -41,17 +51,39 @@ def formula(tax_unit, period, parameters): dinc_person = person("total_disability_payments", period) disinc_person = dinc_person * ~is_dependent * ~is_elderly disability_income = tax_unit.sum(disinc_person) - # ... determine untaxed social security benefits - unit_socsec = add(tax_unit, period, ["social_security"]) + # ... determine nontaxable retirement and disability benefits, + # subd. 1(c): the amounts section 22(c)(3) counts, which include the + # title II and tier 1 benefits section 86 leaves out and the tax-free + # rest of the railroad annuity (as section_22_income reads it), plus + # the pension and annuity benefits in federal gross income that the + # state cannot tax, the taxable railroad benefits. Section 22(c)(3) + # counts what the individual, or either spouse on a joint return, + # receives, so a dependent's benefits stay out. + unit_socsec = tax_unit_non_dep_add(tax_unit, period, ["social_security"]) unit_taxed_socsec = add(tax_unit, period, ["taxable_social_security"]) - untaxed_social_security = unit_socsec - unit_taxed_socsec - # ... determine total disability and social security benefits - benefits = disability_income + untaxed_social_security + untaxed_social_security = max_(0, unit_socsec - unit_taxed_socsec) + railroad_retirement_benefits = tax_unit_non_dep_add( + tax_unit, + period, + [ + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ], + ) + # ... determine total disability and retirement benefits + benefits = ( + disability_income + untaxed_social_security + railroad_retirement_benefits + ) # ... subtract benefits from base amount amount = max_(0, base_amount - benefits) - # ... determine net agi + # ... determine net agi: subd. 1(a) adds lump-sum distributions to + # federal AGI and takes out the railroad benefits that federal gross + # income includes. fed_agi = tax_unit("adjusted_gross_income", period) - agi = fed_agi + tax_unit("form_4972_lumpsum_distributions", period) + lump_sum = tax_unit("form_4972_lumpsum_distributions", period) + exempt_railroad = add(tax_unit, period, ["taxable_railroad_benefits"]) + agi = fed_agi + lump_sum - exempt_railroad agi_offset_base = p.agi_offset_base[filing_status] joint = filing_status == filing_status.possible_values.JOINT head_eligible = tax_unit.any(elderly_head | disabled_head) diff --git a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py index 3772b3c0d8e..f69569cf608 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py @@ -7,12 +7,27 @@ class mn_social_security_subtraction(Variable): label = "Minnesota social security subtraction" unit = USD definition_period = YEAR + documentation = ( + "Schedule M1M line 12. Minn. Stat. 290.0132, subd. 26(i) gives " + "'Social Security benefits' the meaning in IRC 86(d)(1), so title II " + "benefits and tier 1 railroad retirement benefits (the social security " + "equivalent benefit) count together, as on Form 1040 lines 6a and 6b. " + "The model subtracts the taxable tier 1 benefits with the rest of the " + "railroad annuity (taxable_railroad_benefits, 45 U.S.C. 231m(a)), on " + "Schedule M1M line 17, so this subtraction leaves them out as steps 26 " + "to 29 of the line 12 worksheet direct." + ) reference = ( + "https://www.revisor.mn.gov/statutes/cite/290.0132#stat.290.0132.26", "https://www.taxformfinder.org/forms/2021/2021-minnesota-form-m1m.pdf", - "https://www.revenue.state.mn.us/sites/default/files/2024-01/m1m-22.pdf", + # 2022 Schedule M1M instructions, worksheet for line 12, steps 17-20. + "https://www.revenue.state.mn.us/sites/default/files/2024-01/m1m-22.pdf#page=4", # 2020 Schedule M1M instructions, line 39 worksheet, step 8: federal # Schedule 1 adjustments only, not Form 1040 line 10b. "https://www.revenue.state.mn.us/sites/default/files/2024-01/m1m_20.pdf#page=8", + # 2025 Schedule M1M instructions, line 12 and its worksheet, steps + # 1-30, and line 17. + "https://www.revenue.state.mn.us/sites/default/files/2026-07/m1m-25.pdf#page=4", ) defined_for = StateCode.MN @@ -22,47 +37,61 @@ def formula(tax_unit, period, parameters): agi = tax_unit("adjusted_gross_income", period) reduction_start = p.reduction.start[filing_status] reduction_applies = agi > reduction_start - us_taxable_oasdi = add(tax_unit, period, ["taxable_social_security"]) + # Form 1040 line 6b (worksheet steps 6, 10 and 25): taxable title II + # benefits and taxable tier 1 railroad retirement benefits. + taxable_title_ii = add(tax_unit, period, ["taxable_social_security"]) + taxable_tier_1 = add( + tax_unit, + period, + ["taxable_railroad_social_security_equivalent_benefits"], + ) + taxable_benefits = taxable_title_ii + taxable_tier_1 if p.reduction.applies: + # Simplified subtraction, subd. 26(b) to (d). Steps 1-8 reduce + # all taxable benefits; step 29 then takes out the tier 1 + # benefits that line 17 subtracts. reduction_increment = p.reduction.increment[filing_status] - reduction_rate = p.reduction.rate agi_excess = max_(0, agi - reduction_start) reduction_fraction = np.ceil(agi_excess / reduction_increment) - reduction_rate = min_(reduction_rate * reduction_fraction, 1) - reduction = reduction_rate * us_taxable_oasdi - simplified_reduction = max_(us_taxable_oasdi - reduction, 0) + reduction_rate = min_(p.reduction.rate * reduction_fraction, 1) + reduced_benefits = max_(0, taxable_benefits * (1 - reduction_rate)) + simplified_subtraction = max_(0, reduced_benefits - taxable_tier_1) else: - simplified_reduction = 0 - # specify parameters - filing_status = tax_unit("filing_status", period) - - total_benefit_fraction = p.total_benefit_fraction + simplified_subtraction = 0 + # Alternate subtraction, subd. 26(e) to (h), on provisional income + # (steps 9-19). Step 11 starts from total income less all taxable + # benefits, and step 12 adds half of Form 1040 line 6a: the head's and + # spouse's title II and tier 1 benefits. A dependent's benefits go on + # the dependent's own return. income_amount = p.income_amount[filing_status] alt_amount = p.alternative_amount[filing_status] - # calculate subtraction amount (following "Worksheet for line 12") - # ... US-taxable social security benefits - # ... alternative benefit subtraction amount us_gross_income = add(tax_unit, period, ["irs_gross_income"]) - adj_income = us_gross_income - us_taxable_oasdi - total_oasdi = add(tax_unit, period, ["social_security"]) - oasdi_amount = total_oasdi * total_benefit_fraction + adj_income = us_gross_income - taxable_benefits + section_86_benefits = tax_unit("tax_unit_section_86_benefits", period) + benefit_amount = section_86_benefits * p.total_benefit_fraction tax_exempt_int = add(tax_unit, period, ["tax_exempt_interest_income"]) - sum_income = adj_income + oasdi_amount + tax_exempt_int + sum_income = adj_income + benefit_amount + tax_exempt_int us_ald = tax_unit("above_the_line_deductions", period) student_loan_int = add(tax_unit, period, ["student_loan_interest"]) - # Step 8 counts the Schedule 1 adjustments, so not the 2020 - # charitable deduction for non-itemizers (Form 1040 line 10b). + # The 2020 worksheet's step 8 counts the Schedule 1 adjustments, so + # not the 2020 charitable deduction for non-itemizers (Form 1040 line + # 10b). charitable_ald = tax_unit("charitable_contributions_ald", period) mn_ald = max_(0, us_ald - student_loan_int - charitable_ald) income = max_(0, sum_income - mn_ald) net_income = max_(0, income - income_amount) alt_sub_amt = max_(0, alt_amount - (net_income * p.net_income_fraction)) - main_reduction = min_(us_taxable_oasdi, alt_sub_amt) + # Steps 25-28: the lesser of the maximum subtraction and the taxable + # benefits other than the tier 1 benefits on line 17. + alternate_subtraction = min_(taxable_title_ii, alt_sub_amt) if p.reduction.applies: + # At or below the phase-out threshold, line 12 takes all of line + # 6b, and line 17 then leaves out the tier 1 benefits; the model + # subtracts those with the rest of the railroad annuity. return where( reduction_applies, - max_(main_reduction, simplified_reduction), - us_taxable_oasdi, + max_(alternate_subtraction, simplified_subtraction), + taxable_title_ii, ) else: - return max_(main_reduction, simplified_reduction) + return max_(alternate_subtraction, simplified_subtraction) diff --git a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py index cdaa9b234fa..c965954def7 100644 --- a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py +++ b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py @@ -9,7 +9,8 @@ class mn_homestead_credit_refund_household_income(Variable): definition_period = YEAR documentation = ( "Approximates Form M1PR lines 1 through 13 using modeled adjusted gross " - "income, nontaxable Social Security, retirement add-backs and " + "income, nontaxable Social Security and tier 1 railroad retirement " + "benefits, retirement add-backs and " "contributions, and age/disability and dependent subtractions. " "Co-occupant income and other nontaxable additions or subtractions " "are not separately modeled." @@ -17,6 +18,7 @@ class mn_homestead_credit_refund_household_income(Variable): reference = ( "https://www.taxformfinder.org/forms/2024/2024-minnesota-form-m1pr-instructions.pdf#page=7", "https://www.taxformfinder.org/forms/2025/2025-minnesota-form-m1pr-instructions.pdf#page=7", + "https://www.revisor.mn.gov/statutes/cite/290A.03#stat.290A.03.3", ) defined_for = StateCode.MN @@ -35,10 +37,22 @@ def formula(tax_unit, period, parameters): is_dependent = people("is_tax_unit_dependent", period) | people( "claimable_as_dependent_on_another_return", period ) + # M1PR line 2: the head's and spouse's Social Security and tier 1 + # railroad retirement benefits (box 5 of Forms SSA-1099 and RRB-1099) + # less the taxable part on Form 1040 line 6b. Minn. Stat. 290A.03, + # subd. 3(a)(2)(v) counts both kinds of benefits. social_security = people("social_security", period) taxable_social_security = people("taxable_social_security", period) + tier_1 = people("railroad_social_security_equivalent_benefits", period) + taxable_tier_1 = people( + "taxable_railroad_social_security_equivalent_benefits", period + ) nontaxable_social_security = tax_unit.sum( - max_(0, social_security - taxable_social_security) * ~is_dependent + ( + max_(0, social_security - taxable_social_security) + + max_(0, tier_1 - taxable_tier_1) + ) + * ~is_dependent ) # Line 1 is the filer's federal AGI, which leaves out tax unit From b5d14455a50e419ec8d133aaeac2d0ec77fb2aa1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 8 Nov 2026 16:14:18 -0500 Subject: [PATCH 02/11] Count tier 1 railroad benefits in Connecticut's and Iowa's Social Security worksheets Connecticut's Social Security benefit adjustment (12-701(a)(20)(B)(x)) reads the federal worksheet, which counts tier 1 railroad retirement benefits under IRC 86(d)(1). Line A now takes the section 86 benefits, and line D comes out of the taxable title II benefits first, since clause (iv) subtracts every taxable tier 1 benefit that clause (x) does not. Iowa's 2021-2022 reportable Social Security worksheet now follows the form: line 7 adds half of the head's and spouse's title II and tier 1 benefits, line 8 leaves out the student loan interest deduction, and line 1 leaves out dependents' benefits. The old formula left half of the benefits out of line 7. Missouri, Utah and Rhode Island already read title II amounts as their statutes direct; new tests pin those readings, and source-conflict entries record where their forms take Form 1040 line 6b instead. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 2 +- ...ct_social_security_benefit_adjustment.yaml | 106 ++++- .../income/ia_reportable_social_security.yaml | 70 +++- ...ion_and_ss_or_ssd_deduction_section_a.yaml | 38 ++ ...ion_and_ss_or_ssd_deduction_section_b.yaml | 39 ++ .../ri_social_security_modification.yaml | 36 ++ .../ut_ss_benefits_credit_max.yaml | 37 ++ .../ct_social_security_benefit_adjustment.py | 51 ++- .../income/ia_reportable_social_security.py | 54 ++- source_conflicts/candidates.yaml | 380 ++++++++++++++++++ 10 files changed, 779 insertions(+), 34 deletions(-) diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md index 77babfeec93..c28b612ac73 100644 --- a/changelog.d/state-sseb-worksheets.fixed.md +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -1 +1 @@ -Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, elderly and disabled subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0802, 290.0675 and 290A.03 define them, without subtracting them twice. +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, elderly and disabled subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0802, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. diff --git a/policyengine_us/tests/policy/baseline/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.yaml index 437ab0a78a1..7f4261e29bb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.yaml @@ -5,7 +5,7 @@ tax_unit_taxable_social_security: 12_000 adjusted_gross_income: 74_999 tax_unit_ss_combined_income_excess: 5_000 - tax_unit_social_security: 20_000 + tax_unit_section_86_benefits: 20_000 filing_status: SINGLE output: ct_social_security_benefit_adjustment: 12_000 @@ -17,7 +17,7 @@ tax_unit_taxable_social_security: 12_000 adjusted_gross_income: 99_999 tax_unit_ss_combined_income_excess: 5_000 - tax_unit_social_security: 20_000 + tax_unit_section_86_benefits: 20_000 filing_status: JOINT output: ct_social_security_benefit_adjustment: 12_000 @@ -29,7 +29,7 @@ tax_unit_taxable_social_security: 12_000 adjusted_gross_income: 100_000 tax_unit_ss_combined_income_excess: 5_000 - tax_unit_social_security: 15_000 + tax_unit_section_86_benefits: 15_000 filing_status: JOINT output: # Part C: min(15000, 5000) = 5000 @@ -87,3 +87,103 @@ ct_social_security_benefit_adjustment: 38_802 ct_agi: 43_544 ct_income_tax: 332 + +# Tier 1 railroad retirement benefits. Under IRC 86(d)(1) the federal Social +# Security Benefits Worksheet, and so worksheet lines A, B and E, count title +# II and tier 1 benefits together. Conn. Gen. Stat. 12-701(a)(20)(B)(iv) +# subtracts the taxable tier 1 benefits that clause (x) does not, and the +# model subtracts them with the rest of the railroad annuity +# (taxable_railroad_benefits), so line F keeps the title II share and takes +# line D from it first. +- name: CT social security adjustment 2025 below the threshold keeps the taxable title II benefits + period: 2025 + input: + state_code: CT + filing_status: SINGLE + adjusted_gross_income: 70_000 + # 20,000 of title II benefits and 10,000 of tier 1 benefits. + tax_unit_section_86_benefits: 30_000 + tax_unit_ss_combined_income_excess: 20_000 + # The title II share of the 12,000 of taxable benefits. + tax_unit_taxable_social_security: 8_000 + output: + # Clause (x)(III): all taxable benefits come off, the 8,000 of title II + # benefits here and the 4,000 of tier 1 benefits through clause (iv). + ct_social_security_benefit_adjustment: 8_000 + +- name: CT social security adjustment 2025 line A counts tier 1 benefits + period: 2025 + input: + state_code: CT + filing_status: JOINT + adjusted_gross_income: 150_000 + # 30,000 of title II benefits and 20,000 of tier 1 benefits. + tax_unit_section_86_benefits: 50_000 + tax_unit_ss_combined_income_excess: 60_000 + # 85% of 50,000 is 42,500 of taxable benefits; title II's 60% share. + tax_unit_taxable_social_security: 25_500 + output: + # C: min(50,000, 60,000) = 50,000. D: 12,500. F: 25,500 - 12,500. + # With clause (iv)'s 17,000, Connecticut subtracts 30,000, line E less + # line D. + ct_social_security_benefit_adjustment: 13_000 + +- name: CT social security adjustment 2025 line D larger than the taxable title II benefits + period: 2025 + input: + state_code: CT + filing_status: JOINT + adjusted_gross_income: 150_000 + # 4,000 of title II benefits and 36,000 of tier 1 benefits. + tax_unit_section_86_benefits: 40_000 + tax_unit_ss_combined_income_excess: 60_000 + # 85% of 40,000 is 34,000 of taxable benefits; title II's 10% share. + tax_unit_taxable_social_security: 3_400 + output: + # C: 40,000. D: 10,000, more than the 3,400 of taxable title II + # benefits, so line F is 0. Clause (iv) still subtracts all 30,600 of + # taxable tier 1 benefits, and Connecticut taxes the 3,400 of title II + # benefits. + ct_social_security_benefit_adjustment: 0 + +- name: CT social security adjustment 2025 integration with tier 1 benefits and a dependent's benefits + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + is_tax_unit_head: true + age: 70 + social_security_retirement: 30_000 + railroad_social_security_equivalent_benefits: 20_000 + taxable_private_pension_income: 90_000 + spouse: + is_tax_unit_spouse: true + age: 70 + child: + is_tax_unit_dependent: true + age: 10 + social_security_survivors: 6_000 + tax_units: + tax_unit: + members: [head, spouse, child] + filing_status: JOINT + households: + household: + members: [head, spouse, child] + state_code: CT + output: + # Federal worksheet line 1 holds the head's 30,000 of title II and + # 20,000 of tier 1 benefits; the child's benefits go on the child's own + # worksheet. + tax_unit_section_86_benefits: 50_000 + # Line 9: 90,000 + 50% x 50,000 - 32,000. + tax_unit_ss_combined_income_excess: 83_000 + # Line 18: min(85% x 50,000, 85% x (115,000 - 44,000) + 6,000) = 42,500, + # 25,500 of it title II (60%) and 17,000 tier 1 (40%). + tax_unit_taxable_social_security: 25_500 + taxable_railroad_benefits: [17_000, 0, 0] + adjusted_gross_income: 132_500 + # AGI is at least 100,000. C: min(50,000, 83,000). D: 12,500. + # F: 25,500 - 12,500. + ct_social_security_benefit_adjustment: 13_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_reportable_social_security.yaml b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_reportable_social_security.yaml index d0b89d39608..f2d00b9a32d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_reportable_social_security.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ia/tax/income/ia_reportable_social_security.yaml @@ -23,7 +23,10 @@ output: adjusted_gross_income: 57_600 taxable_social_security: [11_760, 7_840] - ia_reportable_social_security: 4_000 # one-half of (income - deduction) + # Worksheet line 2: 50% x 40,000 = 20,000. Line 3: 38,000 of interest. + # Line 6: 2,000. Line 7: 60,000. Line 11: 60,000 - 32,000 = 28,000. + # Line 12: 14,000. Line 13: min(20,000, 14,000). + ia_reportable_social_security: 14_000 - name: IA reportable social security unit test 2 absolute_error_margin: 0.01 @@ -49,3 +52,68 @@ state_code: IA output: ia_reportable_social_security: 1_500 # one-half of gross social security + +# Tier 1 railroad retirement benefits. Worksheet line 1 leaves out Form +# RRB-1099, line 4 adds half of its benefits, and line 3 leaves out Form 1040 +# line 6b, which holds the taxable title II and tier 1 benefits. +- name: IA reportable social security 2022 counts half of the tier 1 benefits in income + absolute_error_margin: 0.01 + period: 2022 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + social_security: 10_000 + railroad_social_security_equivalent_benefits: 20_000 + taxable_interest_income: 30_000 + person2: + is_tax_unit_spouse: true + age: 70 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: IA + output: + # Federal: 30,000 + 50% x 30,000 = 45,000 of provisional income, so + # min(25,500, 85% x 1,000 + 6,000) = 6,850 of the benefits are taxable. + adjusted_gross_income: 36_850 + # Line 2: 5,000. Line 3: 30,000. Line 4: 10,000. Line 7: 45,000. + # Line 11: 13,000. Line 12: 6,500. Line 13: min(5,000, 6,500). + ia_reportable_social_security: 5_000 + +- name: IA reportable social security 2022 leaves out a dependent's benefits + absolute_error_margin: 0.01 + period: 2022 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + social_security: 30_000 + taxable_interest_income: 40_000 + person2: + is_tax_unit_spouse: true + age: 70 + person3: + is_tax_unit_dependent: true + age: 15 + social_security_survivors: 8_000 + tax_units: + tax_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: IA + output: + # Federal: 40,000 + 15,000 = 55,000 of provisional income, so + # min(25,500, 85% x 11,000 + 6,000) = 15,350 of the benefits are taxable. + adjusted_gross_income: 55_350 + # Line 1 holds the head's 30,000; the child's benefits go on the child's + # own return. Line 2: 15,000. Line 7: 55,000. Line 11: 23,000. + # Line 12: 11,500. Line 13: min(15,000, 11,500). + ia_reportable_social_security: 11_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_a.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_a.yaml index eb2b1ca4499..f9f73ab0377 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_a.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_a.yaml @@ -377,3 +377,41 @@ state_code: MO output: mo_pension_and_ss_or_ssd_deduction_section_a: 8_000 + +# Tier 1 railroad retirement benefits. RSMo 143.125 subtracts the Social +# Security benefits included in federal AGI under IRC 86, which counts tier 1 +# benefits, and Form MO-A, Part 1, line 11 subtracts the same taxable tier 1 +# benefits as exempt railroad retirement (taxable_railroad_benefits, 45 U.S.C. +# 231m(a)). The model takes them on Part 1, line 11, so Section C holds the +# title II benefits alone and RSMo 143.124.7 reduces the public pension +# subtraction by the title II benefits only. +- name: MO public pension and Social Security deductions 2024 take the taxable tier 1 benefits in Part 1 + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + is_tax_unit_head: true + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_public_pension_income: 30_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: MO + output: + # Federal: provisional income 30,000 + 50% x 20,000 = 40,000; taxable + # benefits min(85% x 20,000, 85% x (40,000 - 34,000) + 4,500) = 9,600, + # half title II and half tier 1. + taxable_social_security: 4_800 + taxable_railroad_benefits: 4_800 + mo_adjusted_gross_income: 34_800 + # Section C: the 4,800 of taxable title II benefits. + mo_pension_and_ss_or_ssd_deduction_section_c: 4_800 + # Section A: min(30,000, 46,381) - 4,800. + mo_pension_and_ss_or_ssd_deduction_section_a: 25_200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_b.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_b.yaml index c925475349c..85a7d5a63a6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_b.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_b.yaml @@ -267,3 +267,42 @@ # Line 9: 6,000 - 3,000 = 3,000. mo_pension_and_ss_or_ssd_deduction_section_b: [3_000, 0] mo_pension_and_ss_or_ssd_deduction: 3_000 + +# Tier 1 railroad retirement benefits. Form MO-A, Part 3, Section B, line 2 +# takes the taxable benefits on Form 1040 line 6b, which include the taxable +# tier 1 benefits. RSMo 143.124.8 subtracts only the Social Security benefits +# "otherwise included in Missouri adjusted gross income", and Form MO-A, Part +# 1, line 11 already removes the taxable tier 1 benefits from Missouri AGI +# (taxable_railroad_benefits, 45 U.S.C. 231m(a)), so line 2 leaves them out. +- name: MO private pension deduction 2024 leaves the taxable tier 1 benefits out of line 2 + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 64 + is_tax_unit_head: true + railroad_social_security_equivalent_benefits: 20_000 + taxable_private_pension_income: 6_000 + taxable_interest_income: 22_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: MO + output: + # Federal: provisional income 28,000 + 50% x 20,000 = 38,000; taxable + # benefits min(85% x 20,000, 85% x (38,000 - 34,000) + 4,500) = 7,900, + # all tier 1. + taxable_railroad_benefits: 7_900 + adjusted_gross_income: 35_900 + # Line 1: 35,900 - 7,900 railroad subtraction. + mo_adjusted_gross_income: 28_000 + # Line 2: no taxable title II benefits. Line 5: 28,000 - 25,000 = 3,000. + # Line 8: min(6,000, 6,000). Line 9: 6,000 - 3,000. With line 6b on + # line 2, line 5 would be 0 and line 9 6,000. + mo_pension_and_ss_or_ssd_deduction_section_b: 3_000 + mo_pension_and_ss_or_ssd_deduction_section_c: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml index 41d94462b36..671d0fecea1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml @@ -131,3 +131,39 @@ state_code: RI output: ri_social_security_modification: 10_000 + +# Tier 1 railroad retirement benefits. R.I. Gen. Laws 44-30-12(c)(8) subtracts +# the "social security benefits includible in federal adjusted gross income", +# an IRC 86 amount that counts tier 1 benefits, and Schedule M line 1d +# subtracts the same taxable tier 1 benefits as railroad retirement benefits +# exempt under federal law (taxable_railroad_benefits, 45 U.S.C. 231m(a)). +# Line 1d has no age or income test, so the model takes the tier 1 benefits +# there, and the worksheet's line 11 holds the taxable title II benefits. +- name: RI Social Security modification 2024 takes the taxable tier 1 benefits on line 1d + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + is_tax_unit_head: true + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_private_pension_income: 30_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: RI + output: + # Federal: taxable benefits 9,600, half title II and half tier 1. + adjusted_gross_income: 39_600 + taxable_railroad_benefits: 4_800 + ri_social_security_modification_eligible: true + # Line 12: 1.0000, since the filer was born before the 2024 cutoff. Line + # 13: the 4,800 of taxable title II benefits. With line 6b on line 11, + # line 13 would be 9,600 and line 1d would subtract the 4,800 again. + ri_social_security_modification: 4_800 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml index 9f8b8f14077..d86bb4da47e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml @@ -52,3 +52,40 @@ state_code: UT output: ut_ss_benefits_credit_max: 4_700 + +# Tier 1 railroad retirement benefits. Utah Code 59-10-1042(1)(f) limits +# "social security benefit" to monthly benefits under the Social Security Act, +# and subsection (2)(b) gives the credit on those included in state taxable +# income. Tier 1 benefits are paid under the Railroad Retirement Act, and the +# subtraction for railroad retirement income (code 78, +# taxable_railroad_benefits) removes them from state taxable income, so line 1 +# holds the taxable title II benefits alone, though it points to Form 1040 +# line 6b. +- name: UT Social Security benefits credit 2024 leaves the taxable tier 1 benefits out of line 1 + period: 2024 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + is_tax_unit_head: true + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_private_pension_income: 30_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: UT + output: + # Federal: taxable benefits 9,600, half title II and half tier 1. + taxable_social_security: 4_800 + taxable_railroad_benefits: 4_800 + # Lines 2 to 6: Utah total income is federal AGI, 30,000 + 9,600. + ut_total_income: 39_600 + # Line 7: 4,800 x 4.55%. Line 9: 39,600 is below 45,000. With line 6b on + # line 1, line 7 would be 9,600 x 4.55% = 436.80. + ut_ss_benefits_credit_max: 218.40 diff --git a/policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py b/policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py index f199a27ca9b..eba00232d23 100644 --- a/policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py +++ b/policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py @@ -6,8 +6,21 @@ class ct_social_security_benefit_adjustment(Variable): entity = TaxUnit unit = USD label = "Connecticut social security benefit adjustment" + documentation = ( + "Form CT-1040 line 41, Conn. Gen. Stat. 12-701(a)(20)(B)(x). " + "Following IRC 86(d)(1), the worksheet counts title II benefits and " + "tier 1 railroad retirement benefits (the social security equivalent " + "benefit) together. Clause (iv) subtracts the taxable tier 1 benefits " + "that clause (x) does not, and the model subtracts them with the rest " + "of the railroad annuity (taxable_railroad_benefits, 45 U.S.C. " + "231m(a)), so this adjustment covers the title II benefits alone." + ) reference = ( "https://www.cga.ct.gov/current/pub/chap_229.htm#sec_12-701", + # Line 43: tier 1 and tier 2 benefits not already subtracted on + # line 41. + "https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=9", + # Social Security Benefit Adjustment Worksheet, lines A to F. "https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=24", ) definition_period = YEAR @@ -17,38 +30,38 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) p = parameters(period).gov.states.ct.tax.income.subtractions.social_security - # Part A: Gross Social Security - gross_social_security = tax_unit("tax_unit_social_security", period) + # Line A: federal Social Security Benefits Worksheet line 1, the + # head's and spouse's title II and tier 1 benefits (box 5 of Forms + # SSA-1099 and RRB-1099). + section_86_benefits = tax_unit("tax_unit_section_86_benefits", period) - # Part B: Combined income excess + # Line B: federal worksheet line 9, the excess in IRC 86(b)(1). ss_combined_income_excess = tax_unit( "tax_unit_ss_combined_income_excess", period ) - # Part C: Lesser of gross SS and combined income excess - capped_social_security = min_(gross_social_security, ss_combined_income_excess) - - # Part D: Apply CT rate to capped amount - capped_social_security_portion = capped_social_security * p.rate + # Lines C and D: 25% of the lesser of lines A and B. + capped_benefits = min_(section_86_benefits, ss_combined_income_excess) + capped_benefits_portion = capped_benefits * p.rate - # Part E: Federal taxable SS (Line 18 from federal SS worksheet) - federal_taxable_social_security = tax_unit( - "tax_unit_taxable_social_security", period - ) + # Line E: federal worksheet line 18. The title II share stays here; + # the tier 1 share goes to clause (iv). + taxable_title_ii = tax_unit("tax_unit_taxable_social_security", period) - # Part F: Reduced taxable SS - reduced_taxable_social_security = max_( - federal_taxable_social_security - capped_social_security_portion, + # Line F, with line D taken from the title II benefits first, so + # that clause (iv) subtracts every taxable tier 1 dollar. + reduced_taxable_title_ii = max_( + taxable_title_ii - capped_benefits_portion, 0, ) - # Filers with AGI below the threshold can subtract the full amount - # of their taxable social security benefits + # Clause (x)(III): below the threshold, filers subtract all their + # taxable benefits. agi = tax_unit("adjusted_gross_income", period) full_adjustment_eligible = agi < p.reduction_threshold[filing_status] return where( full_adjustment_eligible, - federal_taxable_social_security, - reduced_taxable_social_security, + taxable_title_ii, + reduced_taxable_title_ii, ) diff --git a/policyengine_us/variables/gov/states/ia/tax/income/ia_reportable_social_security.py b/policyengine_us/variables/gov/states/ia/tax/income/ia_reportable_social_security.py index 8964b2b87f6..673a14b6e97 100644 --- a/policyengine_us/variables/gov/states/ia/tax/income/ia_reportable_social_security.py +++ b/policyengine_us/variables/gov/states/ia/tax/income/ia_reportable_social_security.py @@ -7,22 +7,56 @@ class ia_reportable_social_security(Variable): label = "Iowa reportable social security benefits" unit = USD definition_period = YEAR + documentation = ( + "IA 1040 step 4, from the reportable Social Security benefits " + "worksheet. Line 1 takes the head's and spouse's benefits from Forms " + "SSA-1099 only. Line 7 adds half of those benefits, half of the tier " + "1 railroad retirement benefits from Form RRB-1099 (line 4) and the " + "other income on Form 1040 without line 6b." + ) reference = ( + # 2021 IA 1040 instructions, step 4 worksheet, lines 1-13. "https://revenue.iowa.gov/media/2650/download?inline#page=11", + # 2022 IA 1040 instructions, step 4 worksheet, lines 1-13. "https://revenue.iowa.gov/media/2721/download?inline#page=10", ) defined_for = StateCode.IA def formula(tax_unit, period, parameters): - benefits = add(tax_unit, period, ["social_security"]) - income = ( - tax_unit("adjusted_gross_income", period) - - add(tax_unit, period, ["taxable_social_security"]) - + add(tax_unit, period, ["tax_exempt_interest_income"]) - ) - p = parameters(period).gov.states.ia.tax.income + p = parameters(period).gov.states.ia.tax.income.reportable_social_security filing_status = tax_unit("filing_status", period) - deduction = p.reportable_social_security.deduction[filing_status] - return p.reportable_social_security.fraction * min_( - benefits, max_(0, income - deduction) + # Line 1: box 5 of the head's and spouse's Forms SSA-1099. A + # dependent's benefits go on the dependent's own return. + title_ii = tax_unit("tax_unit_social_security_for_taxability", period) + # Line 4: box 5 of Form RRB-1099, the net social security equivalent + # benefit. + tier_1 = tax_unit( + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability", + period, + ) + # Lines 3 and 8: federal AGI without Form 1040 line 6b (the taxable + # title II and tier 1 benefits). Line 8 counts Schedule 1 lines 11 to + # 20, 23 and the other adjustments, so not the student loan interest + # deduction on line 21. + agi = tax_unit("adjusted_gross_income", period) + taxable_benefits = tax_unit("tax_unit_taxable_section_86_benefits", period) + student_loan_interest = tax_unit_non_dep_add( + tax_unit, period, ["student_loan_interest_ald"] + ) + # Line 6: tax-exempt interest, Form 1040 line 2a. + tax_exempt_interest = tax_unit_non_dep_add( + tax_unit, period, ["tax_exempt_interest_income"] + ) + # Lines 2, 4 and 7-9. + income = ( + agi + - taxable_benefits + + student_loan_interest + + tax_exempt_interest + + p.fraction * (title_ii + tier_1) ) + # Lines 10-12. + deduction = p.deduction[filing_status] + excess = max_(0, income - deduction) + # Line 13: the smaller of line 2 and line 12. + return min_(p.fraction * title_ii, p.fraction * excess) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index f4bf0f8500d..bd7e3a05a29 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -2114,6 +2114,75 @@ candidates: state: ruled note: 'Ruled 2026-11-02 by the PR author in the pre-filing review of this PR, item CT-S3: keep the State Supplement out of countable income. Neither document names it, and plan 1.9 is the specific list.' +- id: us-ct:social-security-adjustment-railroad-tier-1 + program: income-tax + years: + from: 2019 + type: tax + question: When line D of the Social Security benefit adjustment, 25% of the lesser of the benefits and the IRC 86(b)(1) + excess, exceeds the taxable title II benefits, does Connecticut tax part of the tier 1 railroad retirement benefits? + sources: + - kind: statute + citation: Conn. Gen. Stat. 12-701(a)(20)(B)(iv) and (x)(IV) + url: https://www.cga.ct.gov/current/pub/chap_229.htm#sec_12-701 + - kind: instructions + citation: 2024 Form CT-1040 instructions, line 43 + url: https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=9 + - kind: worksheet + citation: 2024 Form CT-1040 instructions, Social Security Benefit Adjustment Worksheet, lines A to F + url: https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=24 + readings: + statute: Clause (iv) subtracts every taxable tier 1 benefit that clause (x) did not, so clauses (x) and (iv) together + leave at most the taxable title II benefits in Connecticut AGI. + other: Line 43 lets filers deduct tier 1 and tier 2 benefits only to the extent line 41 did not subtract them, and has + them enter the railroad benefits reported on Form 1040 line 5b. The tier 1 benefits on Form RRB-1099 go on line 6b, + so line 43 never reaches them, and all of line D stays in Connecticut AGI even when it exceeds the taxable title II + benefits. + example: A joint filer with AGI of $150,000, $4,000 of title II and $36,000 of tier 1 benefits, $34,000 of them taxable, + and a line B of $60,000 has a line D of $10,000. Under the statute Connecticut subtracts the $30,600 of taxable tier 1 + benefits and taxes the $3,400 of taxable title II benefits; under the instructions line 41 is $24,000 and line 43 is + $0, so Connecticut taxes $10,000. PE-US taxes $3,400. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). Instructions before 2024 were not compared. +- id: us-ct:social-security-adjustment-railroad-tier-1-order + program: income-tax + years: + from: 2019 + type: tax + question: Clause (iv) subtracts the taxable tier 1 railroad retirement benefits that clause (x) does not. Which benefits + does the clause (x) amount, line E less line D, cover first? + sources: + - kind: statute + citation: Conn. Gen. Stat. 12-701(a)(20)(B)(iv) and (x)(IV); 45 U.S.C. 231m(a) + url: https://www.cga.ct.gov/current/pub/chap_229.htm#sec_12-701 + - kind: instructions + citation: 2024 Form CT-1040 instructions, line 43 + url: https://portal.ct.gov/-/media/drs/forms/2024/income/2024-ct-1040-instructions_1224.pdf#page=9 + readings: + statute: Clause (x) subtracts one amount figured on all the benefits and does not say which benefits it covers. Covering + tier 1 benefits first, Connecticut taxes the lesser of line D and the taxable title II benefits; covering them in + proportion, line D times the title II share; covering title II benefits first, line D less the taxable tier 1 + benefits, but not below zero. + other: The instructions put the tier 1 benefits from Form RRB-1099 on line 41 and none of them on line 43, which treats + line 41 as covering them first. + example: A joint filer with AGI of $150,000, $30,000 of title II and $20,000 of tier 1 benefits, $42,500 of them taxable, + and a line B of $60,000 has a line D of $12,500. Covering tier 1 benefits first, Connecticut taxes $12,500; in + proportion, $7,500; title II benefits first, $0. PE-US taxes $12,500. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/ct/tax/income/subtractions/ct_social_security_benefit_adjustment.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for covering tier 1 benefits first. The statute leaves the order open, the instructions treat line 41 as + covering tier 1 benefits, and the order matches us-mn:social-security-subtraction-railroad-tier-1-order. - id: us-dc:eitc-no-tin-child-schedule-before-2023 program: income-tax years: @@ -5409,6 +5478,42 @@ candidates: state: unreviewed note: PE-US follows the manual and surrounding report editions; obtain contemporaneous BEM 556 for October 2022-September 2023. +- id: us-mn:elderly-disabled-subtraction-railroad-benefits + program: income-tax + years: + from: 2025 + type: tax + question: How do railroad retirement benefits enter the elderly or disabled subtraction, which reduces the base amount + by nontaxable retirement and disability benefits and by half of adjusted gross income over a threshold? + sources: + - kind: statute + citation: Minn. Stat. 290.0802, subd. 1(a) and (c), and subd. 2(b)(2) + url: https://www.revisor.mn.gov/statutes/cite/290.0802 + - kind: form + citation: 2025 Schedule M1R, lines 5 to 9, and the instructions for lines 5b and 9 + url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1r-25.pdf#page=1 + readings: + statute: Subd. 1(c) counts the benefits section 22(c)(3) would count, which include the tier 1 benefits section 86 + leaves out and the tax-free part of the rest of the annuity, and "pension, annuity, or disability benefits included + in federal gross income but not subject to state taxation", which takes in the taxable tier 1 and tier 2 benefits + that 45 U.S.C. 231m(a) keeps the state from taxing. Subd. 1(a) takes those same benefits out of adjusted gross + income. + other: Line 5 subtracts all of Form 1040 line 6b, taxable tier 1 benefits included, from the Form SSA-1099 benefits; + line 6 adds the tier 1 benefits from Form RRB-1099 and the railroad benefits on Form 1040 lines 4b and 5b; line 9 + takes only the railroad benefits on line 6b out of federal AGI. The reduction so leaves out the taxable tier 1 + benefits and the tax-free part of the rest of the annuity, and AGI keeps the taxable rest of the annuity. + example: A single filer aged 70 with $6,000 of nontaxable tier 1 benefits, $2,000 of taxable tier 2 benefits, $500 + recovered tax free and $12,000 of federal AGI subtracts $1,100 under the statute ($9,600 - $8,500; AGI of $10,000 is + under the $14,500 threshold) and $1,600 under Schedule M1R ($9,600 - $8,000); PE-US gives $1,100. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_elderly_disabled_subtraction.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). The section 22(c)(3) amounts follow the reading in + us:elderly-credit-railroad-tier-2-return-of-cost. Schedules M1R before 2025 were not compared. - id: us-mn:k12-subtraction-grade-cap-default program: income-tax years: @@ -5471,6 +5576,67 @@ candidates: state: ruled note: 'Ruled 2026-10-06: follow the manual over the plan. Code not yet on main: mn_eap_countable_income, gov.states.mn.commerce.eap.income.sources.' +- id: us-mn:marriage-credit-railroad-tier-1 + program: income-tax + years: + from: 2025 + type: tax + question: Do a spouse's taxable tier 1 railroad retirement benefits count toward the marriage credit's earned income, + which Minn. Stat. 290.0675 limits to amounts "included in Minnesota taxable income"? + sources: + - kind: statute + citation: Minn. Stat. 290.0675, subd. 1(b) + url: https://www.revisor.mn.gov/statutes/cite/290.0675#stat.290.0675.1 + - kind: instructions + citation: 2025 Schedule M1MA instructions, lines 1 to 4 + url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1ma-25.pdf#page=2 + readings: + statute: Subd. 1(b) counts IRC 86(d)(1) Social Security benefits, tier 1 benefits included, "to the extent included + in Minnesota taxable income". Minnesota subtracts taxable railroad retirement benefits (45 U.S.C. 231m(a); Schedule + M1M line 17), so they never count. + other: Line 4 takes "the amount from line 6b of federal Form 1040", which includes taxable tier 1 benefits, while the + instructions for lines 1 and 3 leave out Railroad Retirement Board benefits. + example: A couple filing jointly in 2025 with $90,000 of Minnesota taxable income, where one spouse has $60,000 of + wages and the other only $35,000 of taxable tier 1 benefits, gets no credit under the statute (the lesser earner has + no earned income) and $72.50 under line 4; PE-US gives no credit. + pe_us: + follows: statute + code: + - policyengine_us/parameters/gov/states/mn/tax/income/credits/marriage/income_sources.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10696 + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). PR 10696 made taxable_social_security the title II share, which keeps the + tier 1 benefits out of the source list; PR 10722 adds the tests. Instructions before 2025 were not compared. +- id: us-mn:marriage-credit-subtracted-social-security + program: income-tax + years: + from: 2025 + type: tax + question: Does the marriage credit's earned income count a spouse's taxable title II benefits that the Social Security + subtraction takes out of Minnesota taxable income? + sources: + - kind: statute + citation: Minn. Stat. 290.0675, subd. 1(b); Minn. Stat. 290.0132, subd. 26 + url: https://www.revisor.mn.gov/statutes/cite/290.0675#stat.290.0675.1 + - kind: instructions + citation: 2025 Schedule M1MA instructions, line 4 + url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1ma-25.pdf#page=2 + readings: + statute: Subd. 1(b) counts Social Security benefits only "to the extent included in Minnesota taxable income", so + not the part the subd. 26 subtraction removes. + other: Line 4 takes all of Form 1040 line 6b, divided between the spouses by Form SSA-1099 box 5. + example: A couple filing jointly in 2025 with AGI under $108,320, where the lesser earner's only income is $35,000 of + taxable title II benefits, subtracts all $35,000 under subd. 26, so under the statute the lesser earner has no + earned income and the couple gets no credit; line 4 counts $35,000. PE-US counts $35,000. + pe_us: + follows: conflicting-source + code: + - policyengine_us/parameters/gov/states/mn/tax/income/credits/marriage/income_sources.yaml + prs: [] + state: unreviewed + note: Found while auditing the railroad benefit changes in PR 10722, which leaves it unchanged. - id: us-mn:msa-couple-arrangement-b-pathways program: msa years: @@ -5530,6 +5696,72 @@ candidates: prs: [] state: unreviewed note: Establish the guidance's statutory basis and reconcile M1SA with M1REF. +- id: us-mn:social-security-subtraction-provisional-income-benefits + program: income-tax + years: + from: 2022 + type: tax + question: Does provisional income for the alternate Social Security subtraction add half of the taxable benefits, as + Minn. Stat. 290.0132 subd. 26(i) provides, or half of all benefits received, as the line 12 worksheet does? + sources: + - kind: statute + citation: Minn. Stat. 290.0132, subd. 26(i); Laws 2019, 1st Spec. Sess., ch. 6, art. 2, sec. 12 + url: https://www.revisor.mn.gov/statutes/cite/290.0132#stat.290.0132.26 + - kind: worksheet + citation: 2025 Schedule M1M instructions, worksheet for line 12, steps 12 and 13 (2022, steps 4 and 5) + url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1m-25.pdf#page=4 + readings: + statute: Provisional income is section 86(b)(2) modified adjusted gross income "plus one-half of the taxable Social + Security benefits received during the taxable year". The 2019 act inserted "taxable", effective for taxable years + beginning after December 31, 2017. + other: Steps 12 and 13 add half of Form 1040 line 6a, all benefits received. + example: A couple filing jointly in 2025 with $150,000 of gross income and AGI and $100,000 of benefits, $85,000 of them + taxable, has provisional income of $107,500 under the statute and an alternate subtraction of $2,066, against + $115,000 and $566 under the worksheet. PE-US gives $566. + pe_us: + follows: conflicting-source + code: + - policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py + - policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml + prs: [] + state: unreviewed + note: Found while auditing the railroad benefit changes in PR 10722, which keeps the worksheet's base. Either reading + counts tier 1 benefits with title II benefits, since subd. 26(i) gives "Social Security benefits" the IRC 86(d)(1) + meaning. +- id: us-mn:social-security-subtraction-railroad-tier-1-order + program: income-tax + years: + from: 2022 + type: tax + question: The Social Security subtraction runs on taxable benefits that include tier 1 railroad retirement benefits, + which Minnesota also subtracts under 45 U.S.C. 231m(a). How do the two subtractions combine? + sources: + - kind: statute + citation: Minn. Stat. 290.0132, subd. 26; 45 U.S.C. 231m(a) + url: https://www.revisor.mn.gov/statutes/cite/290.0132#stat.290.0132.26 + - kind: worksheet + citation: 2025 Schedule M1M instructions, line 12, worksheet for line 12 steps 25-30, and line 17 (2022, steps 17-20) + url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1m-25.pdf#page=4 + readings: + statute: Subd. 26 does not coordinate the two. Taking both in full subtracts taxable tier 1 benefits twice; the + reduced simplified subtraction could cover the tier 1 benefits first, last or in proportion. + other: Below the phase-out threshold, line 12 takes all of Form 1040 line 6b and line 17 leaves out the tier 1 + benefits. Above it, steps 26-29 take the tier 1 benefits subtracted on line 17 out of the worksheet. Step 27 caps + the alternate subtraction at the other taxable benefits, and step 29 takes the tier 1 benefits from the reduced + simplified amount, so the reduction falls on the tier 1 benefits first. Line 17 never gives less than line 12 for + them. + example: A single filer in 2025 with AGI of $92,000, $17,000 of taxable title II benefits and $8,500 of taxable tier 1 + benefits subtracts $11,900 on line 12 and $8,500 on line 17, $20,400 in all (80% of $25,500). Reducing title II + benefits alone gives $13,600 + $8,500 = $22,100, and reducing tier 1 benefits last gives $25,500. PE-US gives $20,400. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the worksheet, since the statute leaves the order open and the department's worksheet settles it + without a double subtraction. - id: us-mo:claim-of-right-federal-tax-deduction program: income-tax years: @@ -5858,6 +6090,42 @@ candidates: state: unreviewed note: 'The open PR applies the regulation from 2024; main''s source choice is unspecified, and no statute is supplied. Code not yet on main: mo_federal_adjusted_gross_income, gov.states.mo.tax.income.federal_agi.negative_floor_applies.' +- id: us-mo:pension-income-test-railroad-tier-1 + program: income-tax + years: + from: 2021 + type: tax + question: Does the income test for Missouri's private pension subtraction, Missouri AGI less Social Security benefits, + take the taxable tier 1 railroad retirement benefits out a second time? + sources: + - kind: statute + citation: RSMo 143.124.3, 143.124.4 and 143.124.8 + url: https://revisor.mo.gov/main/OneSection.aspx?section=143.124 + - kind: form + citation: 2024 Form MO-A, Part 3, Section B, lines 1 to 9 + url: https://dor.mo.gov/forms/MO-A_2024.pdf#page=3 + - kind: instructions + citation: 2024 Form MO-1040 instructions, Form MO-A Part 1, line 11 (railroad retirement benefits) + url: https://dor.mo.gov/forms/MO-1040%20Instructions_2024.pdf#page=14 + readings: + statute: Subsection 8 subtracts the Social Security benefits "otherwise included in Missouri adjusted gross income". + Form MO-A, Part 1, line 11 has already removed the taxable tier 1 benefits from Missouri AGI, so the test subtracts + the taxable title II benefits alone. + other: Section B, line 2 takes the taxable benefits on Form 1040 line 6b, tier 1 included, and subtracts them from a + Missouri AGI that no longer holds the tier 1 benefits. + example: A single filer with $20,000 of tier 1 benefits ($7,900 of them taxable), a $6,000 private pension and $22,000 of + interest has Missouri AGI of $28,000. Under the statute the test income is $28,000, $3,000 above the $25,000 allowance, + and the subtraction is $3,000; under Form MO-A, line 3 is $20,100 and the subtraction is $6,000. PE-US subtracts + $3,000. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_b.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). Subsection 8 also sets the income for Section A's public pension test, + which applied through 2023, and PE-US reads it the same way there. - id: us-mo:property-tax-credit-rounding program: property-tax-credit years: @@ -5965,6 +6233,48 @@ candidates: note: PE-US applies Missouri's one-adult-per-household cap from 2025, a start date the parameter's comment calls a modeling choice, and keeps the uncapped federal reading in other states (child_care_claim_cap/per_household.yaml, meets_snap_parent_exception.py). Verify whether the manual permissibly fills the federal silence. +- id: us-mo:social-security-deduction-railroad-tier-1 + program: income-tax + years: + from: 2021 + type: tax + question: RSMo 143.125 and the railroad retirement subtraction both reach the taxable tier 1 railroad retirement + benefits. Which one takes them, given that RSMo 143.124.7 reduces the public pension subtraction by the 143.125 + exemption? + sources: + - kind: statute + citation: RSMo 143.125.1 and 143.125.2 + url: https://revisor.mo.gov/main/OneSection.aspx?section=143.125 + - kind: statute + citation: RSMo 143.124.7; 45 U.S.C. 231m(a) + url: https://revisor.mo.gov/main/OneSection.aspx?section=143.124 + - kind: form + citation: 2024 Form MO-A, Part 1, line 11 and Part 3, Sections A and C + url: https://dor.mo.gov/forms/MO-A_2024.pdf#page=3 + - kind: instructions + citation: 2024 Form MO-1040 instructions, Form MO-A Part 3, Section C, lines 1 and 2 and their worksheet + url: https://dor.mo.gov/forms/MO-1040%20Instructions_2024.pdf#page=17 + readings: + statute: Section 143.125 subtracts the benefits "included in federal adjusted gross income under Section 86", which + counts tier 1 benefits, from Missouri AGI, and Missouri AGI has already left out the same benefits as income + federal law exempts. Neither statute says which subtraction takes them. + other: Section C, lines 1 and 2 take the taxable benefits on Form 1040 line 6b, tier 1 included, while Part 1, line 11 + subtracts the taxable tier 1 benefits as well, so the forms subtract them twice and reduce the public pension + subtraction by them. + example: A single filer aged 70 with $10,000 of title II and $10,000 of tier 1 benefits ($9,600 of them taxable, half of + each) and a $30,000 public pension. Taking the tier 1 benefits in Part 1, Section C is $4,800, Section A is $25,200 + and Part 1, line 11 is $4,800; taking them in Section C, Section C is $9,600, Section A is $20,400 and line 11 is $0. + PE-US takes them in Part 1. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_c.py + - policyengine_us/variables/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction_section_a.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for Part 1 (taxpayer choices minimize tax). Missouri AGI drops the exempt benefits before 143.125 applies to + it, and a filer free to choose would take Part 1, which leaves the public pension subtraction whole. - id: us-mo:wftc-investment-income-boundary program: income-tax years: @@ -9221,6 +9531,42 @@ candidates: state: ruled note: The code already uses a strict comparison and the eligibility tests pin AGI equal to the threshold as ineligible. Verify the statute's and the worksheet's wording before registering. +- id: us-ri:social-security-modification-railroad-tier-1 + program: income-tax + years: + from: 2021 + type: tax + question: The Social Security modification and the railroad retirement modification both reach the taxable tier 1 + railroad retirement benefits. Which one takes them? + sources: + - kind: statute + citation: R.I. Gen. Laws 44-30-12(c)(1) and (c)(8); 45 U.S.C. 231m(a) + url: https://webserver.rilegislature.gov/Statutes/TITLE44/44-30/44-II/44-30-12.htm + - kind: worksheet + citation: 2024 Modification for Taxable Social Security Income Worksheet, lines 8 to 13 + url: https://tax.ri.gov/sites/g/files/xkgbur541/files/2024-09/Social%20Security%20Worksheet_bd.pdf#page=1 + - kind: instructions + citation: 2025 RI-1040 instructions, Schedule M lines 1d and 1s + url: https://tax.ri.gov/sites/g/files/xkgbur541/files/2025-12/2025%201040R%20Instructions%20122025.pdf#page=9 + readings: + statute: Subsection (c)(8) subtracts "the social security benefits includible in federal adjusted gross income", an + IRC 86 amount that counts tier 1 benefits, and subsection (c)(1) subtracts income federal law exempts, which takes + in the same benefits. Neither says which modification takes them. + other: Worksheet line 11 takes the taxable benefits on Form 1040 line 6b, tier 1 included, and line 1d subtracts the + railroad retirement benefits in federal gross income, so the forms subtract the taxable tier 1 benefits twice. + example: A single filer aged 70 with $10,000 of title II and $10,000 of tier 1 benefits ($9,600 of them taxable, half of + each) and AGI of $39,600. Taking the tier 1 benefits on line 1d, line 1d is $4,800 and line 1s is $4,800; taking them + on line 1s, line 1s is $9,600 and line 1d is $0. On the forms, line 1d is $4,800 and line 1s is $9,600. PE-US takes + them on line 1d. + pe_us: + follows: gap-fill + code: + - policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for line 1d (taxpayer choices minimize tax). Line 1d has no age or income test, so it takes the tier 1 + benefits whenever line 1s would, and also when the filer fails line 1s's tests. - id: us-ri:ssp-category-d-without-federal-ssi program: ssi-state-supplement years: @@ -10633,6 +10979,40 @@ candidates: Utah state supplement (ut_ssp, which adds ut_dws_ssp, a Department of Workforce Services payment, and ut_dhhs_ssp, another agency payment that section 405 excludes in the same item). R982-403 is a current rule with its next five-year review due June 17, 2027, so decide whether it governs over the manual and plan. +- id: us-ut:social-security-credit-railroad-tier-1 + program: income-tax + years: + from: 2021 + type: tax + question: Does Utah's Social Security benefits credit apply to the taxable tier 1 railroad retirement benefits on Form + 1040 line 6b? + sources: + - kind: statute + citation: Utah Code 59-10-1042(1)(f) and (2) + url: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1042.html + - kind: worksheet + citation: 2024 Form TC-40 instructions, Social Security Credit Worksheet, line 1 + url: https://tax.utah.gov/forms/2024/tc-40inst.pdf#page=23 + - kind: instructions + citation: 2024 Form TC-40 instructions, code 78 (railroad retirement income) + url: https://tax.utah.gov/forms/2024/tc-40inst.pdf#page=20 + readings: + statute: Subsection (1)(f) limits "social security benefit" to monthly benefits under the Social Security Act, and + subsection (2)(b) gives the credit on those included in state taxable income. Tier 1 benefits come under the + Railroad Retirement Act, and code 78 removes them from state taxable income, so the credit leaves them out. + other: Worksheet line 1 takes Form 1040 line 6b, which includes the taxable tier 1 benefits that code 78 subtracts. + example: A single filer with $10,000 of title II and $10,000 of tier 1 benefits ($9,600 of them taxable, half of each) + and MAGI of $39,600. Under the statute line 7 is $218.40 (4.55% of $4,800); under the worksheet $436.80 (4.55% of + $9,600). PE-US gives $218.40. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). Before 2025 subsection (2)(b) read "included in adjusted gross income on the + claimant's federal income tax return", which reaches the same benefits through subsection (1)(f). - id: us-va:ag-entry-month-spouse-at-home program: ssi-state-supplement years: From caedc60a7aff4092560e269c38605b9d95fd99e1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 04:35:19 -0500 Subject: [PATCH 03/11] Count railroad benefits in Montana, Massachusetts, Pennsylvania, Maine, Michigan and DC provisions Co-Authored-By: Claude Opus 5.5 --- .../gross_income_sources.yaml | 6 + .../ma_qualified_unemployment_deduction.yaml | 50 +++++++ .../me_pension_income_deduction.yaml | 31 ++++ ...y_and_railroad_retirement_subtraction.yaml | 134 ++++++++++++++++++ ..._renter_credit_gross_household_income.yaml | 58 ++++++++ ...pa_property_tax_or_rent_rebate_income.yaml | 33 ++++- .../vt_retirement_income_exemption.yaml | 34 +++++ .../subtractions/dc_disability_exclusion.py | 14 +- .../ma_qualified_unemployment_deduction.py | 10 +- .../me_pension_income_deduction.py | 14 +- ...erest_dividends_capital_gains_deduction.py | 5 +- ..._expanded_retirement_benefits_deduction.py | 21 ++- ...ment_benefits_deduction_tier_one_amount.py | 14 +- ...uction_tier_three_ss_exempt_not_retired.py | 18 +-- ..._deduction_tier_three_ss_exempt_retired.py | 18 +-- .../mi_standard_deduction_tier_three.py | 9 +- .../mi_standard_deduction_tier_two.py | 14 +- ...ary_and_railroad_retirement_subtraction.py | 32 +++++ ...or_renter_credit_gross_household_income.py | 20 +++ .../pa_property_tax_or_rent_rebate_income.py | 14 +- source_conflicts/candidates.yaml | 34 +++++ 21 files changed, 526 insertions(+), 57 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.yaml create mode 100644 policyengine_us/variables/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.py diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml index f28c95c8377..db441e618ab 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml @@ -49,6 +49,10 @@ values: # AGI already includes taxable pension income; only the tax-exempt # portion is added back here per § 15-30-2337(9)(a)(i). - tax_exempt_pension_income + # § 15-30-2337(9)(a)(i) – Railroad Retirement Act benefits; the part of + # the annuity outside federal AGI. The formula adds the untaxed social + # security equivalent benefit. + - tax_exempt_railroad_pension_benefits - veterans_benefits # § 15-30-2337(9)(a)(i) – veterans' disability benefits - child_support_received # § 15-30-2337(9)(a)(iv) – support money - strike_benefits # § 15-30-2337(9)(a)(v) – nontaxable strike benefits @@ -95,6 +99,7 @@ values: - tax_exempt_interest_income - tax_exempt_ira_distributions - tax_exempt_pension_income + - tax_exempt_railroad_pension_benefits - veterans_benefits - child_support_received - strike_benefits @@ -118,6 +123,7 @@ values: - tax_exempt_interest_income - tax_exempt_ira_distributions - tax_exempt_pension_income + - tax_exempt_railroad_pension_benefits - veterans_benefits - child_support_received - strike_benefits diff --git a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.yaml index 9c313f117bd..8fa98ceaf0a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.yaml @@ -108,3 +108,53 @@ state_code: MA output: ma_qualified_unemployment_deduction: 0 + +- name: Untaxed tier 1 railroad retirement benefits count in household income + period: 2021 + input: + people: + person: + age: 70 + unemployment_compensation: 8_000 + social_security_retirement: 6_000 + railroad_social_security_equivalent_benefits: 12_000 + tax_units: + tax_unit: + members: [person] + households: + household: + members: [person] + state_code: MA + output: + # Provisional income is 8,000 + 50% x 18,000 = 17,000, below the 25,000 + # base, so none of the benefits are taxable and federal AGI is 8,000. + adjusted_gross_income: 8_000 + # Worksheet line 7 takes Form 1040 line 6a less line 6b, which counts + # the tier 1 benefits with the title II benefits: household income is + # 8,000 + 18,000 = 26,000, above the 25,520 limit for one person. + ma_qualified_unemployment_deduction: 0 + +- name: A dependent's Social Security stays out of household income + period: 2021 + input: + people: + parent: + age: 40 + unemployment_compensation: 14_000 + child: + age: 12 + is_tax_unit_dependent: true + social_security_survivors: 21_000 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: MA + output: + # Line 7 takes the parent's Form 1040 lines 6a and 6b; the child's + # benefits go on the child's own return. Household income is 14,000, + # within the 34,480 limit for two (25,520 + 8,960); with the child's + # benefits it would be 35,000, above it. + ma_qualified_unemployment_deduction: 10_200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/me_pension_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/me_pension_income_deduction.yaml index 5c21663f012..6615d407801 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/me_pension_income_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/taxable_income/me_pension_income_deduction.yaml @@ -390,3 +390,34 @@ members: [person1] output: me_pension_income_deduction: 35_000 + +- name: Railroad retirement benefits reduce the pension deduction cap, taxable or not + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + age: 70 + taxable_private_pension_income: 40_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 6_000 + tax_exempt_railroad_pension_benefits: 1_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + households: + household: + members: [person1] + state_code: ME + spm_units: + spm_unit: + members: [person1] + output: + # Provisional income 40,000 + 6,000 + 5,000 = 51,000, so 85% of the + # tier 1 benefits is taxable federally. + taxable_railroad_social_security_equivalent_benefits: 8_500 + adjusted_gross_income: 54_500 + # Line P3 counts all $17,000 of railroad retirement benefits: + # min(40,000, 48,216 - 17,000) = 31,216. + me_pension_income_deduction: 31_216 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.yaml new file mode 100644 index 00000000000..d6be47f9120 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.yaml @@ -0,0 +1,134 @@ +# MCL 206.30(1)(e) deducts military retirement benefits and Railroad Retirement +# Act benefits alike, and the limits in subsections (1)(f)(iv), (1)(p) and (9) +# count both. The MI-1040 worksheets take both from Schedule 1, line 11. + +- name: Schedule 1 line 11 holds military retirement pay and taxable railroad retirement benefits + period: 2025 + input: + state_code: MI + military_retirement_pay: 4_000 + taxable_railroad_pension_benefits: 6_000 + output: + mi_military_retirement_subtraction: 4_000 + taxable_railroad_benefits: 6_000 + mi_military_and_railroad_retirement_subtraction: 10_000 + +- name: Taxable railroad retirement benefits reduce a senior's private pension cap + period: 2024 + input: + state_code: MI + age: 82 + taxable_private_pension_income: 70_000 + taxable_railroad_pension_benefits: 10_000 + output: + # Form 4884 line 10: 64,040 - 10,000 = 54,040. + mi_retirement_benefits_deduction_tier_one_amount: 54_040 + +- name: Taxable railroad retirement benefits reduce a senior's investment income deduction + period: 2024 + input: + state_code: MI + age: 82 + taxable_interest_income: 20_000 + taxable_railroad_pension_benefits: 10_000 + output: + # Worksheet line 2 takes Schedule 1, line 11: 14,274 - 10,000 = 4,274. + mi_interest_dividends_capital_gains_deduction: 4_274 + +- name: Taxable railroad retirement benefits reduce the 2025 expanded retirement cap + period: 2025 + input: + state_code: MI + age: 62 + taxable_private_pension_income: 50_000 + taxable_railroad_pension_benefits: 10_000 + output: + # Worksheet 3.3: (65,897 - 10,000) x 75% = 41,922.75. + mi_expanded_retirement_benefits_deduction: 41_922.75 + +- name: Taxable railroad retirement benefits reduce the tier 3 standard deduction + period: 2022 + input: + people: + person1: + military_retirement_pay: 1_000 + taxable_railroad_pension_benefits: 3_000 + military_service_income: 500 + taxable_social_security: 100 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + mi_standard_deduction_tier_three_eligible: true + mi_personal_exemptions: 2_000 + members: [person1] + filing_status: SINGLE + household: + members: [person1] + state_code: MI + output: # 20,000 - (1,000 + 3,000) - (500 > 100) - 2,000 + mi_standard_deduction_tier_three: 13_500 + +- name: Taxable railroad retirement benefits reduce the tier 2 standard deduction + period: 2022 + input: + people: + person1: + taxable_railroad_pension_benefits: 6_000 + military_service_income: 5_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + mi_standard_deduction_tier_two_eligible: true + mi_standard_deduction_tier_two_increase_eligible_people: 0 + members: [person1] + filing_status: SURVIVING_SPOUSE + household: + members: [person1] + state_code: MI + output: # 20,000 - 6,000 - 5,000 + mi_standard_deduction_tier_two: 9_000 + +- name: Taxable railroad retirement benefits bring in the tier 1 limit for retired SSA-exempt filers + period: 2022 + input: + people: + person1: + taxable_pension_income: 20_000 + taxable_railroad_pension_benefits: 1_000 + is_tax_unit_head_or_spouse: true + person2: + taxable_pension_income: 40_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + mi_retirement_benefits_deduction_tier_three_eligible: true + mi_retirement_benefits_deduction_tier_three_ss_exempt_retired_eligible_people: 2 + mi_retirement_benefits_deduction_tier_one_amount: 50_000 + members: [person1, person2] + filing_status: JOINT + household: + members: [person1, person2] + state_code: MI + output: # Worksheet 3.1: min(min(70,000, 50,000), 20,000 + 40,000) + mi_retirement_benefits_deduction_tier_three_ss_exempt_retired: 50_000 + +- name: Taxable railroad retirement benefits bring in the tier 1 limit for SSA-exempt filers not retired in 2013 + period: 2022 + input: + people: + person1: + taxable_pension_income: 40_000 + taxable_railroad_pension_benefits: 1_000 + is_tax_unit_head_or_spouse: true + tax_units: + tax_unit: + mi_retirement_benefits_deduction_tier_three_eligible: true + mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired_eligible_people: 1 + mi_retirement_benefits_deduction_tier_one_amount: 10_000 + members: [person1] + filing_status: SINGLE + household: + members: [person1] + state_code: MI + output: # Worksheet 3.2: min(min(15,000, 10,000), 40,000) + mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired: 10_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml index 63408d7c55c..5750714e050 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml @@ -812,3 +812,61 @@ # The parts show a 10_000 gain and a 4_000 loss; line 7 counts 10_000, # not the inconsistent 20_000 net: 30_000. mt_elderly_homeowner_or_renter_credit_gross_household_income: 30_000 + +- name: Railroad Retirement Act benefits count in full in gross household income + period: 2025 + input: + people: + person1: + age: 75 + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 6_000 + tax_exempt_railroad_pension_benefits: 1_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_name: MT + output: + # Provisional income is 6,000 + 50% x 20,000 = 16,000, below the 25,000 + # base, so none of the title II or tier 1 benefits are taxable and + # federal AGI is the 6,000 taxable railroad pension. + adjusted_gross_income: 6_000 + # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits" and + # (viii) all Social Security payments: AGI 6,000 + the 1,000 of the + # annuity outside AGI + 10,000 of title II + 10,000 of tier 1 social + # security equivalent benefits. + mt_elderly_homeowner_or_renter_credit_gross_household_income: 27_000 + +- name: A dependent's tier 1 railroad retirement benefits count once, in full + period: 2024 + input: + people: + person1: + age: 70 + # No SSI for the claimant, so the case isolates the dependent's income. + ssi: 0 + person2: + age: 17 + employment_income: 25_000 + railroad_social_security_equivalent_benefits: 10_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_name: MT + output: + # On the dependent's own return, provisional income is + # 25_000 + 10_000 / 2 = 30_000, so 50% x (30_000 - 25_000) = 2_500 of the + # tier 1 benefits is taxable (IRC 86(d)(1)(B)) and is part of + # dependent_gross_income. + dependent_taxable_railroad_social_security_equivalent_benefits: [0, 2_500] + # § 15-30-2337(9)(a)(i) counts all Railroad Retirement Act benefits: + # wages 25_000 + tier 1 benefits 10_000 = 35_000, not 27_500 or 37_500. + mt_elderly_homeowner_or_renter_credit_gross_household_income: [0, 35_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml index ec86404383c..284b7d83de9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.yaml @@ -36,7 +36,7 @@ output: pa_property_tax_or_rent_rebate_income: 20_000 -- name: Case 3, SSI and railroad benefits are half-counted. +- name: Case 3, SSI and tier 1 railroad benefits are half-counted. period: 2025 input: people: @@ -44,7 +44,9 @@ age: 65 ssi: 2_000 pa_ssp: 0 - railroad_benefits: 10_000 + # Line 5: Railroad Retirement Tier 1 benefits. None of the $10,000 + # is taxable federally (provisional income $5,000). + railroad_social_security_equivalent_benefits: 10_000 tax_units: tax_unit: members: [person1] @@ -229,3 +231,30 @@ # Federal AGI holds the taxable 6,000 and line 11 the exempt 4,000. adjusted_gross_income: 6_000 pa_property_tax_or_rent_rebate_income: 10_000 + +- name: Tier 1 railroad benefits count at half and Tier 2 benefits in full, once each. + period: 2025 + input: + people: + person1: + age: 70 + ssi: 0 + pa_ssp: 0 + railroad_social_security_equivalent_benefits: 20_000 + taxable_railroad_pension_benefits: 30_000 + tax_exempt_railroad_pension_benefits: 2_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: PA + output: + # Provisional income 30,000 + 50% x 20,000 = 40,000; taxable tier 1 + # benefits min(85% x 20,000, 85% x (40,000 - 34,000) + 4,500) = 9,600. + adjusted_gross_income: 39_600 + # Line 5: 50% x 20,000 of Tier 1 benefits. Line 6: the gross 32,000 of + # Tier 2 benefits. Federal AGI's 9,600 of taxable Tier 1 benefits comes + # out, since line 5 counts them. + pa_property_tax_or_rent_rebate_income: 42_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_retirement_income_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_retirement_income_exemption.yaml index 9ed82a14d15..52fd8c00bf1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_retirement_income_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_retirement_income_exemption.yaml @@ -339,3 +339,37 @@ # AGI 150,000: ratio (175,000 - 150,000) / 50,000 = 0.50 -> 30,000 x 0.50 = 15,000 vt_military_retirement_income_based_exemption: 15_000 vt_retirement_income_exemption: 15_000 + +# Tier 1 railroad retirement benefits. 32 V.S.A. 5830e(a) excludes "federally +# taxable benefits received under the federal Social Security Act". Tier 1 +# benefits are paid under the Railroad Retirement Act, and Schedule IN-112, +# Part I, line 13 (taxable_railroad_benefits) subtracts the taxable tier 1 and +# tier 2 benefits, so the exemption covers the taxable title II benefits alone, +# though worksheet line 9 points to Form 1040 line 6b. +- name: VT 2024 Social Security exemption leaves the taxable tier 1 benefits to line 13 + period: 2024 + input: + people: + head: + age: 70 + is_tax_unit_head: true + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_private_pension_income: 30_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: VT + output: + # Federal: taxable benefits 9,600, half title II and half tier 1. + tax_unit_taxable_social_security: 4_800 + taxable_railroad_benefits: 4_800 + adjusted_gross_income: 39_600 + # AGI is below the $50,000 full-exemption limit. With line 6b on worksheet + # line 9, the exemption would be 9,600 and line 13 would subtract the + # same 4,800 of tier 1 benefits again. + vt_retirement_income_exemption: 4_800 diff --git a/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py b/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py index 355a56af7c1..f4af47c50a2 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py @@ -9,7 +9,12 @@ class dc_disability_exclusion(Variable): definition_period = YEAR defined_for = StateCode.DC reference = ( - "https://code.dccouncil.gov/us/dc/council/code/sections/47-1803.02#(a)(2)(M)" + "https://code.dccouncil.gov/us/dc/council/code/sections/47-1803.02#(a)(2)(M)", + # Form D-2440, lines 5 to 10. + "https://otr.cfo.dc.gov/sites/default/files/dc/sites/otr/publication/attachments/2025_D-2440_Form_012626.pdf#page=1", + # Form D-40, line 10: "Taxable amount of social security and tier 1 + # railroad retirement". + "https://otr.cfo.dc.gov/sites/default/files/dc/sites/otr/publication/attachments/2025_D40_Form_0002_030526.pdf#page=2", ) def formula(person, period, parameters): @@ -21,8 +26,11 @@ def formula(person, period, parameters): total_capped_payments = tax_unit.sum(capped_disability_payments) federal_agi = tax_unit("adjusted_gross_income", period) - social_security_income = tax_unit("tax_unit_taxable_social_security", period) - reduced_income = federal_agi - social_security_income - p.amount + # Line 6 takes Form D-40 line 10, the taxable title II and tier 1 + # railroad retirement benefits (Form 1040 line 6b), which IRC 105(d) + # before the Social Security Amendments of 1983 left out of AGI. + section_86_benefits = tax_unit("tax_unit_taxable_section_86_benefits", period) + reduced_income = federal_agi - section_86_benefits - p.amount capped_reduced_income = max_(reduced_income, 0) return max_(total_capped_payments - capped_reduced_income, 0) diff --git a/policyengine_us/variables/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.py b/policyengine_us/variables/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.py index 1b8c7c871e8..d40de0a3368 100644 --- a/policyengine_us/variables/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.py +++ b/policyengine_us/variables/gov/states/ma/tax/income/adjusted_gross_income/ma_qualified_unemployment_deduction.py @@ -27,10 +27,14 @@ def formula(tax_unit, period, parameters): + tax_unit("tax_unit_dependents", period) ) limit = p.income_limit.base + (size - 1) * p.income_limit.additional_person - # Lines 5-9: federal AGI + tax-exempt interest + untaxed social security. + # Lines 5-9: federal AGI + tax-exempt interest + untaxed social + # security. Line 7 takes Form 1040 line 6a less line 6b: the head's + # and spouse's title II and tier 1 railroad retirement benefits (IRC + # 86(d)(1)) less the taxable part. A dependent's benefits go on the + # dependent's own return. untaxed_social_security = tax_unit( - "tax_unit_social_security", period - ) - tax_unit("tax_unit_taxable_social_security", period) + "tax_unit_section_86_benefits", period + ) - tax_unit("tax_unit_taxable_section_86_benefits", period) household_income = ( tax_unit("adjusted_gross_income", period) + add(tax_unit, period, ["tax_exempt_interest_income"]) diff --git a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py index 045b49f6391..7c674b9f168 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py +++ b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py @@ -8,7 +8,11 @@ class me_pension_income_deduction(Variable): unit = USD definition_period = YEAR defined_for = StateCode.ME - reference = "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_1s_fillable.pdf#page=2" + reference = ( + "https://legislature.maine.gov/statutes/36/title36sec5122.html", + # Worksheet for Pension Income Deduction, lines P1 to P5. + "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_1s_fillable.pdf#page=2", + ) def formula(tax_unit, period, parameters): person = tax_unit.members @@ -33,8 +37,12 @@ def formula(tax_unit, period, parameters): add(person, period, pension_sources) + add(person, period, distribution_sources) * age_eligible ) - gross_ss = person("social_security", period) - ss_reduced_cap = max_(p.cap - gross_ss, 0) + # Line P3 and § 5122(2)(M-2)(1)(a)(ii): "the total amount of the + # individual's social security benefits and railroad retirement + # benefits paid by the United States", taxable or not, so all tiers + # of a railroad annuity count. + federal_benefits = add(person, period, ["social_security", "railroad_benefits"]) + ss_reduced_cap = max_(p.cap - federal_benefits, 0) non_military_deduction = min_(pension_income, ss_reduced_cap) military_retirement = person("military_retirement_income", period) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py index b7ad93fae0d..7d8345ccbb5 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/interest_dividends_capital_gains/mi_interest_dividends_capital_gains_deduction.py @@ -28,14 +28,15 @@ def formula(tax_unit, period, parameters): # Senior citizens may subtract interest, dividends, and capital gains included in AGI. income = add(tax_unit, period, p.income_types) # The maximum amount of the deduction must be reduced by any deduction for - # Military (including Michigan National Guard) retirement benefits + # Military (including Michigan National Guard) retirement benefits and + # railroad retirement benefits from Schedule 1, line 11 # Public and private retirement and pension benefits # Amount used for the federal credit for the elderly and totally and permanently disabled reductions_pay = add( person, period, [ - "mi_military_retirement_subtraction", + "mi_military_and_railroad_retirement_subtraction", "taxable_pension_income", ], ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py index 761fee0a84a..8c14ff2d10f 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/expanded/mi_expanded_retirement_benefits_deduction.py @@ -49,21 +49,20 @@ def formula(tax_unit, period, parameters): # Modeled after Worksheet 3.3 Retirement and Pension Benefits Subtraction for Section D of Form 4884 - # If a filer is recieving military retirement pay, the calculation includes the smaller of - # a part of tier one or expanded deduction amount + # If a filer subtracts military retirement benefits or taxable + # railroad retirement benefits on Schedule 1, line 11, the calculation + # includes the smaller of a part of tier one or expanded deduction amount # Line 1 tier_one_cap = p.tier_one.amount[filing_status] - head_or_spouse_military_retirement_pay = ( - person("mi_military_retirement_subtraction", period) * is_head_or_spouse + head_or_spouse_line_11_benefits = ( + person("mi_military_and_railroad_retirement_subtraction", period) + * is_head_or_spouse ) # Line 2 - total_military_retirement_pay = tax_unit.sum( - head_or_spouse_military_retirement_pay - ) - received_military_retirement_pay = total_military_retirement_pay > 0 - # the cap is reduced by the amount of military retirement pay + total_line_11_benefits = tax_unit.sum(head_or_spouse_line_11_benefits) + received_line_11_benefits = total_line_11_benefits > 0 # Line 3 - reduced_tier_one_cap = max_(tier_one_cap - total_military_retirement_pay, 0) + reduced_tier_one_cap = max_(tier_one_cap - total_line_11_benefits, 0) # Line 4 applicable_tier_one_cap = reduced_tier_one_cap * p.expanded.rate # Line 6 @@ -78,7 +77,7 @@ def formula(tax_unit, period, parameters): # Line 19 return where( - received_military_retirement_pay, + received_line_11_benefits, capped_benefit_amount, capped_head_or_spouse_pension_income, ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py index 8e6aea6f8ee..15aeee642c4 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_one/mi_retirement_benefits_deduction_tier_one_amount.py @@ -29,16 +29,18 @@ def formula(tax_unit, period, parameters): # "Recipients born before 1946 may subtract all qualifying retirement and # pension benefits received from federal or Michigan public sources" # all public benefits can be deducted - # Line 10 + # Line 10: military retirement benefits and taxable railroad + # retirement benefits from Schedule 1, line 11 (MCL 206.30(1)(f)(iv) + # reduces the maximum by the subsection (1)(e) deduction). is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) - military_retirement_pay = ( - person("mi_military_retirement_subtraction", period) * is_head_or_spouse + line_11_benefits = ( + person("mi_military_and_railroad_retirement_subtraction", period) + * is_head_or_spouse ) - total_military_retirement_pay = tax_unit.sum(military_retirement_pay) - # the cap is reduced by the amount of military retirement pay + total_line_11_benefits = tax_unit.sum(line_11_benefits) # Line 11 - reduced_private_cap = max_(private_cap - total_military_retirement_pay, 0) + reduced_private_cap = max_(private_cap - total_line_11_benefits, 0) # Line 12 public_benefits = ( person("taxable_public_pension_income", period) * is_head_or_spouse diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py index bde1d0eff4b..6f34b97eeda 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/not_retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_not_retired.py @@ -59,14 +59,16 @@ def formula(tax_unit, period, parameters): uncapped_pension_income * is_head_or_spouse ) - # If a filer is recieving military retirement pay, the calculation includes the smaller of - # the tier one or tier three deduction amount - # Worksheet 3.2 counts only military retirement income on the filer's Schedule 1, line 11, - # which excludes a dependent's pay (not in the filer's AGI). - military_pay = ( - person("mi_military_retirement_subtraction", period) * is_head_or_spouse + # If a filer subtracts military retirement benefits or taxable + # railroad retirement benefits on Schedule 1, line 11, the calculation + # includes the smaller of the tier one or tier three deduction amount + # (Worksheet 3.2). Line 11 is the filer's, so a dependent's benefits + # (not in the filer's AGI) do not count. + line_11_benefits = ( + person("mi_military_and_railroad_retirement_subtraction", period) + * is_head_or_spouse ) - military_retirement_pay_received = tax_unit.sum(military_pay) > 0 + line_11_benefits_received = tax_unit.sum(line_11_benefits) > 0 # Line 8 tier_one_amount = tax_unit( "mi_retirement_benefits_deduction_tier_one_amount", @@ -76,7 +78,7 @@ def formula(tax_unit, period, parameters): smaller_of_cap_or_tier_one_amount = min_(cap, tier_one_amount) eligible_deduction = where( - military_retirement_pay_received, + line_11_benefits_received, smaller_of_cap_or_tier_one_amount, cap, ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py index 87d5cac3b9f..b08f15b5abd 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/retirement/tier_three/ss_exempt/retired/mi_retirement_benefits_deduction_tier_three_ss_exempt_retired.py @@ -74,14 +74,16 @@ def formula(tax_unit, period, parameters): # Qualifying Retirement and Pension Benefits from Service in the U.S. Armed Forces or # Michigan National Guard and Completing Section B of Form 4884 - # If a filer is recieving military retirement pay, the calculation includes the smaller of - # the tier one or tier three deduction amount - # Worksheet 3.1 counts only military retirement income on the filer's Schedule 1, line 11, - # which excludes a dependent's pay (not in the filer's AGI). - military_pay = ( - person("mi_military_retirement_subtraction", period) * is_head_or_spouse + # If a filer subtracts military retirement benefits or taxable + # railroad retirement benefits on Schedule 1, line 11, the calculation + # includes the smaller of the tier one or tier three deduction amount + # (Worksheet 3.1). Line 11 is the filer's, so a dependent's benefits + # (not in the filer's AGI) do not count. + line_11_benefits = ( + person("mi_military_and_railroad_retirement_subtraction", period) + * is_head_or_spouse ) - military_retirement_pay_eligible = tax_unit.sum(military_pay) > 0 + line_11_benefits_received = tax_unit.sum(line_11_benefits) > 0 # Line 8 tier_one_amount = tax_unit( "mi_retirement_benefits_deduction_tier_one_amount", @@ -91,7 +93,7 @@ def formula(tax_unit, period, parameters): smaller_of_cap_or_tier_one_amount = min_(cap, tier_one_amount) eligible_deduction = where( - military_retirement_pay_eligible, + line_11_benefits_received, smaller_of_cap_or_tier_one_amount, cap, ) diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py index cf03976a763..b2eb4827d1a 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_three/mi_standard_deduction_tier_three.py @@ -30,9 +30,10 @@ def formula(tax_unit, period, parameters): person = tax_unit.members is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) - # Line 2: enter military retirement pay and survivor benefits - military_retirement_income = person( - "mi_military_retirement_subtraction", period + # Line 2: military retirement benefits and taxable railroad + # retirement benefits from Schedule 1, line 11 + line_11_benefits = person( + "mi_military_and_railroad_retirement_subtraction", period ) # Line 3: enter military service income or taxable social security taxable_ss = person("taxable_social_security", period) @@ -45,7 +46,7 @@ def formula(tax_unit, period, parameters): mi_personal_exemptions = tax_unit("mi_personal_exemptions", period) # Line 5: add lines 2 through 4 total_person_reductions = tax_unit.sum( - is_head_or_spouse * (military_retirement_income + larger_ss_or_military_pay) + is_head_or_spouse * (line_11_benefits + larger_ss_or_military_pay) ) total_reductions = total_person_reductions + mi_personal_exemptions # Line 6: subtract line 5 from line 1 diff --git a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py index b4d341417a8..3726e648aff 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/deductions/standard/tier_two/mi_standard_deduction_tier_two.py @@ -26,21 +26,23 @@ def formula(tax_unit, period, parameters): ) increased_amount = p.standard.tier_two.amount.increase * eligible_people increased_base_amount = base_amount + increased_amount - # After that we reduce the amount by the amounts from line 11 and line 14 - # just applicable to head and spouse + # After that we reduce the amount by the amounts from line 11 + # (military retirement benefits and taxable railroad retirement + # benefits) and line 14 (military pay), just applicable to head and + # spouse (Worksheet 2, lines 4 to 7) person = tax_unit.members - military_pay = add( + reductions = add( person, period, [ - "mi_military_retirement_subtraction", + "mi_military_and_railroad_retirement_subtraction", "military_service_income", ], ) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) - head_or_spouse_military_pay = tax_unit.sum(military_pay * is_head_or_spouse) + head_or_spouse_reductions = tax_unit.sum(reductions * is_head_or_spouse) standard_deduction_tier_two = max_( - increased_base_amount - head_or_spouse_military_pay, 0 + increased_base_amount - head_or_spouse_reductions, 0 ) # Worksheet 3.3: Retirement and Pension Benefits Subtraction for Section D of Form 4884 if p.retirement_benefits.expanded.availability: diff --git a/policyengine_us/variables/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.py b/policyengine_us/variables/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.py new file mode 100644 index 00000000000..d4d421d1a61 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class mi_military_and_railroad_retirement_subtraction(Variable): + value_type = float + entity = Person + label = "Michigan military and railroad retirement subtraction" + unit = USD + definition_period = YEAR + documentation = ( + "Schedule 1 line 11: retirement benefits for service in the Armed " + "Forces or the Michigan National Guard and the Railroad Retirement Act " + "benefits in federal adjusted gross income, which MCL 206.30(1)(e) " + "deducts. The retirement and pension benefits subtraction " + "(subsection (1)(f)(iv)), the senior interest, dividends and capital " + "gains deduction (subsection (1)(p)) and the Michigan standard " + "deduction (subsection (9)) count this amount against their limits." + ) + reference = ( + "https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30", + # 2025 MI-1040 book: the dividend, interest and capital gains + # worksheet, line 2 (page 17); Worksheet 2, line 5 (page 18); Form + # 4884 line 10 (page 21); Worksheets 3.1 and 3.2, line 2 (page 24); + # Worksheet 3.3, line 2 (page 25). + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=17", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=18", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=21", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=24", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=25", + ) + defined_for = StateCode.MI + adds = ["mi_military_retirement_subtraction", "taxable_railroad_benefits"] diff --git a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py index 4f6ba36ed86..44470318a7c 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py @@ -67,6 +67,25 @@ def formula(person, period, parameters): person("taxable_social_security", period), ) untaxed_social_security = max_(social_security - counted_social_security, 0) + # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits", so + # the tier 1 social security equivalent benefit counts in full in the + # same way. The rest of the annuity outside federal AGI is in the + # sources (tax_exempt_railroad_pension_benefits). + railroad_benefits = person( + "railroad_social_security_equivalent_benefits", period + ) + counted_railroad_benefits = where( + is_dependent, + ~head_or_spouse + * person( + "dependent_taxable_railroad_social_security_equivalent_benefits", + period, + ), + person("taxable_railroad_social_security_equivalent_benefits", period), + ) + untaxed_railroad_benefits = max_( + railroad_benefits - counted_railroad_benefits, 0 + ) # Income is federal AGI "without regard to loss" (§ 15-30-2337(9)(a)), # so the business and capital losses deducted in computing federal # AGI are added back (2023 Elderly Homeowner/Renter Credit Schedule, @@ -127,6 +146,7 @@ def formula(person, period, parameters): sources + dependent_income + untaxed_social_security + + untaxed_railroad_benefits + losses + lost_capital_gains ) diff --git a/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py b/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py index 13dd8cab593..b1a20c5e468 100644 --- a/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py +++ b/policyengine_us/variables/gov/states/pa/tax/property/property_tax_or_rent_rebate/pa_property_tax_or_rent_rebate_income.py @@ -21,7 +21,12 @@ def formula(tax_unit, period, parameters): head_or_spouse = person("is_tax_unit_head_or_spouse", period) adjusted_gross_income = tax_unit("adjusted_gross_income", period) above_the_line_deductions = tax_unit("above_the_line_deductions", period) + # Federal AGI holds the taxable title II and tier 1 benefits; lines 4 + # and 5 count half of all of them instead. taxable_social_security = tax_unit("tax_unit_taxable_social_security", period) + taxable_tier_1 = tax_unit( + "tax_unit_taxable_railroad_social_security_equivalent_benefits", period + ) half_counted_benefits = tax_unit.sum( head_or_spouse * add( @@ -34,7 +39,10 @@ def formula(tax_unit, period, parameters): # Medicaid facility residents; the PA-1000 does not say # whether the Department of Revenue treats it as SSP. "pa_ssp", - "railroad_benefits", + # Line 5: Railroad Retirement Tier 1 benefits on Form + # RRB-1099. The rest of the annuity (Form RRB-1099-R) + # counts in full on line 6. + "railroad_social_security_equivalent_benefits", ], ) ) @@ -47,6 +55,9 @@ def formula(tax_unit, period, parameters): "tax_exempt_interest_income", "tax_exempt_pension_income", "tax_exempt_retirement_distributions", + # Line 6 takes the gross Tier 2 benefits; AGI holds the + # taxable part. + "tax_exempt_railroad_pension_benefits", ], ) ) @@ -81,6 +92,7 @@ def formula(tax_unit, period, parameters): adjusted_gross_income + above_the_line_deductions - taxable_social_security + - taxable_tier_1 + p.benefit_income_rate * half_counted_benefits + tax_exempt_income + pa_1000_line_11_income diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index bd7e3a05a29..a130241436a 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -11532,6 +11532,40 @@ candidates: state: ruled note: 'Ruled in October 2026 to follow the statute; verify the calculator editions and zero-exemption example. Code not yet on main: vt_renter_credit.' +- id: us-vt:retirement-income-exemption-railroad-tier-1 + program: income-tax + years: + from: 2021 + type: tax + question: Does Vermont's Social Security exemption cover the taxable tier 1 railroad retirement benefits on Form 1040 + line 6b? + sources: + - kind: statute + citation: 32 V.S.A. 5830e(a) + url: https://legislature.vermont.gov/statutes/section/32/151/05830e + - kind: worksheet + citation: 2024 Schedule IN-112 instructions, Retirement Income Exemption Worksheet, line 9 + url: https://tax.vermont.gov/sites/tax/files/documents/IN-112%20Instr-2024.pdf#page=2 + - kind: instructions + citation: 2024 Schedule IN-112 instructions, Part I, line 13 (railroad retirement) + url: https://tax.vermont.gov/sites/tax/files/documents/IN-112%20Instr-2024.pdf#page=3 + readings: + statute: Subsection (a) excludes "federally taxable benefits received under the federal Social Security Act". Tier 1 + benefits come under the Railroad Retirement Act, and line 13 subtracts the taxable tier 1 and tier 2 benefits, so + the exemption leaves them out. + other: Worksheet line 9 takes Form 1040 line 6b, which includes the taxable tier 1 benefits that line 13 also + subtracts. + example: A single filer with $10,000 of title II and $10,000 of tier 1 benefits ($9,600 of them taxable, half of each) + and AGI of $39,600 in 2024. Under the statute the exemption is $4,800 and line 13 subtracts the other $4,800; under the + worksheet the exemption is $9,600 and line 13 subtracts $4,800 again. PE-US exempts $4,800. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/retirement_income_exemption/vt_retirement_income_exemption.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). - id: us-vt:snap-child-support-deduction program: snap years: From 50008cc838980645859213162c13a25514efa91d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 05:08:43 -0500 Subject: [PATCH 04/11] Count railroad benefits in DC, Maryland, Wisconsin, New York, Oklahoma, Massachusetts, Idaho and North Dakota provisions Each change follows the state's statute or form: - DC disability exclusion, line 6: Form D-40 line 10 holds taxable title II and tier 1 benefits. - Maryland and Maine pension caps, and Idaho's retirement deduction cap: the statutes reduce each cap by Railroad Retirement Act benefits, taxable or not. - Wisconsin unemployment subtraction: worksheet line 4 takes Form 1040 line 6b. - New York IT-214 line 13, Oklahoma Forms 538-S and 538-H line 5, and Massachusetts c.62 s.6(k)(1) count railroad benefits by name or as retirement receipts. - North Dakota's renter's refund: N.D.C.C. 57-02-08.1(5)(c), logged as a source conflict. Maine, Maryland and New York read the railroad parts rather than railroad_benefits, so a total entered in that variable stays out of tax. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 2 +- .../income/disallowed_deductions.yaml | 5 +++ .../renters_refund/income_sources.yaml | 7 ++++ .../income/credits/gross_income_sources.yaml | 5 +++ .../tax/income/dc_disability_exclusion.yaml | 22 ++++++++++ .../id_retirement_benefits_deduction.yaml | 25 +++++++++++ .../ma_scb_total_income.yaml | 24 +++++++++++ .../agi/md_pension_subtraction_amount.yaml | 14 +++++++ .../nd_renters_refund_income.yaml | 12 ++++++ .../credits/ny_household_gross_income.yaml | 41 +++++++++++++++++++ .../states/ok/tax/income/ok_gross_income.yaml | 13 ++++++ ...unemployment_compensation_subtraction.yaml | 15 +++++++ .../subtractions/dc_disability_exclusion.py | 16 +++++--- .../id_retirement_benefits_deduction.py | 24 ++++++++--- .../md_pension_subtraction_amount.py | 24 +++++++++-- .../me_pension_income_deduction.py | 11 ++++- .../credits/ny_household_gross_income.py | 31 ++++++++++++-- ...i_unemployment_compensation_subtraction.py | 13 +++++- source_conflicts/candidates.yaml | 39 ++++++++++++++++++ 19 files changed, 323 insertions(+), 20 deletions(-) diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md index c28b612ac73..7b9fc8d5f58 100644 --- a/changelog.d/state-sseb-worksheets.fixed.md +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -1 +1 @@ -Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, elderly and disabled subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0802, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit and North Dakota's renter's refund. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. diff --git a/policyengine_us/parameters/gov/states/ma/tax/income/credits/senior_circuit_breaker/income/disallowed_deductions.yaml b/policyengine_us/parameters/gov/states/ma/tax/income/credits/senior_circuit_breaker/income/disallowed_deductions.yaml index 879ff14cb15..8f487ce410d 100644 --- a/policyengine_us/parameters/gov/states/ma/tax/income/credits/senior_circuit_breaker/income/disallowed_deductions.yaml +++ b/policyengine_us/parameters/gov/states/ma/tax/income/credits/senior_circuit_breaker/income/disallowed_deductions.yaml @@ -22,6 +22,11 @@ description: Income sources re-added to total income for the Massachusetts Senio values: 2001-01-01: - social_security + # "[T]he total amount of income and receipts from social security, + # retirement, pension, or annuities" reaches every railroad tier. + - railroad_social_security_equivalent_benefits + - taxable_railroad_pension_benefits + - tax_exempt_railroad_pension_benefits - tax_exempt_unemployment_compensation - tax_exempt_pension_income - taxable_public_pension_income diff --git a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml index 31642f83af3..5893a29044a 100644 --- a/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml @@ -3,6 +3,13 @@ values: 2025-01-01: - market_income - social_security + # "[S]ocial security, or other retirement benefits". IRC 86 is the federal + # exclusion that the statute's social security exception lifts, and IRC + # 86(d)(1)(B) treats tier 1 benefits as social security benefits. The + # exempt part of the rest of the annuity stays out as an amount excluded + # from income by federal law. + - railroad_social_security_equivalent_benefits + - taxable_railroad_pension_benefits - total_unemployment_compensation - general_assistance metadata: diff --git a/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml b/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml index 54eb8257ffc..69024f1644e 100644 --- a/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ok/tax/income/credits/gross_income_sources.yaml @@ -20,6 +20,11 @@ values: - total_unemployment_compensation - workers_compensation - social_security + # Forms 538-S and 538-H, line 5: Railroad Retirement benefits, every tier, + # whether or not federal AGI includes them. + - railroad_social_security_equivalent_benefits + - taxable_railroad_pension_benefits + - tax_exempt_railroad_pension_benefits - alimony_income - child_support_received - illicit_income diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_disability_exclusion.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_disability_exclusion.yaml index 911618083d2..f2c25bdd88d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_disability_exclusion.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_disability_exclusion.yaml @@ -66,3 +66,25 @@ state_code: DC output: dc_disability_exclusion: 7_000 + +- name: taxable tier 1 railroad benefits leave the income that limits the exclusion + period: 2025 + input: + people: + person1: + is_tax_unit_head: true + total_disability_payments: 5_200 + tax_units: + tax_unit: + members: [person1] + adjusted_gross_income: 25_000 + tax_unit_taxable_social_security: 2_000 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 4_000 + households: + household: + members: [person1] + state_code: DC + output: + # Form D-2440: line 6 takes Form D-40 line 10, $2,000 + $4,000. Line 7 is + # $19,000, line 9 is $4,000 and line 10 is $5,200 - $4,000. + dc_disability_exclusion: 1_200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml index 72f0567011f..bb06a71b362 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.yaml @@ -135,3 +135,28 @@ output: id_retirement_benefits_deduction_relevant_income: 10_000 id_retirement_benefits_deduction: 10_000 + +- name: Idaho reduces the cap by every railroad tier, taxable or not, and by Social Security. + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 67 + public_pension_income: 20_000 + social_security_retirement: 5_000 + railroad_social_security_equivalent_benefits: 25_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: ID + output: + # Form 39R: line 8a 48,216; line 8b 25,000 + 3,000 + 2,000 = 30,000; + # line 8c 5,000; line 8d 13,216; line 8e 20,000; line 8f 13,216. + id_retirement_benefits_deduction: 13_216 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/credits/senior_circuit_breaker/ma_scb_total_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/credits/senior_circuit_breaker/ma_scb_total_income.yaml index 5f536473a77..b01b5492e18 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/credits/senior_circuit_breaker/ma_scb_total_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ma/tax/income/credits/senior_circuit_breaker/ma_scb_total_income.yaml @@ -186,3 +186,27 @@ # reduced by 700 aged exemption = 29,300. ma_gross_income: 0 ma_scb_total_income: 29_300 + +- name: Total income re-adds every railroad tier once, whether or not federal AGI includes it. + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 70 + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + households: + household: + members: [head] + state_code: MA + output: + # Provisional income 30,000 + 3,000 + half of 10,000 = 38,000. Taxable + # tier 1 benefits are 4,500 + 85% of (38,000 - 34,000) = 7,900 and federal + # AGI 40,900, but Massachusetts gross income leaves out both railroad + # amounts in AGI: 30,000. Section 6(k)(1) adds 10,000 + 3,000 + 2,000, and + # the 700 aged exemption comes off: 44,300. + ma_gross_income: 30_000 + ma_scb_total_income: 44_300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/md_pension_subtraction_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/md_pension_subtraction_amount.yaml index 0f8234e781d..7cbb724d8e1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/md_pension_subtraction_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/tax/income/agi/md_pension_subtraction_amount.yaml @@ -101,3 +101,17 @@ md_military_retirement_subtraction_amount: 20_000 md_pension_subtraction_amount: 20_000 + +- name: Railroad retirement benefits of both tiers reduce the pension exclusion, taxable or not + period: 2025 + input: + is_tax_unit_dependent: false + age: 70 + taxable_pension_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 6_000 + tax_exempt_railroad_pension_benefits: 1_000 + state_code: MD + output: + # Worksheet 13A, line 3 counts all $17,000. Line 4: 41,200 - 17,000. + md_pension_subtraction_amount: 24_200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_income.yaml index 187e04fe8d3..a99a7a35704 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/tax/property/renters_refund/nd_renters_refund_income.yaml @@ -249,3 +249,15 @@ # The 5,000 from a plan the recipient paid for is excluded by IRC # 104(a)(3), and NDCC 57-02-08.1(5)(c) excludes it: 10,000 + 7,000. nd_renters_refund_income: 17_000 + +- name: North Dakota counts tier 1 benefits in full and the taxable rest of a railroad annuity. + period: 2025 + input: + state_code: ND + employment_income: 4_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # NDCC 57-02-08.1(5)(c): 4,000 + 10,000 + 3,000. IRC 72 excludes the 2,000. + nd_renters_refund_income: 17_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_gross_income.yaml index afe440fbc38..ee152da7f3c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/credits/ny_household_gross_income.yaml @@ -13,3 +13,44 @@ output: # AGI 5,000 (Social Security not taxable at this income) + nontaxable SS 12,000 ny_household_gross_income: 17_000 + +- name: Line 13 adds the untaxed part of every railroad tier and nothing already in AGI. + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 71 + employment_income: 6_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + households: + household: + members: [head] + state_code: NY + output: + # Provisional income 6,000 + 3,000 + half of 10,000 = 14,000, under the + # 25,000 base, so no tier 1 benefit reaches AGI: line 9 is 9,000. + # Line 13: 10,000 + 2,000 = 12,000. + ny_household_gross_income: 21_000 + +- name: Lines 11 and 13 split untaxed benefits between title II and tier 1 and count them once. + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 71 + employment_income: 30_000 + social_security_retirement: 5_000 + railroad_social_security_equivalent_benefits: 5_000 + households: + household: + members: [head] + state_code: NY + output: + # Provisional income 30,000 + half of 10,000 = 35,000. Taxable benefits: + # 4,500 + 85% of (35,000 - 34,000) = 5,350, half each from title II and + # tier 1. Line 9: 35,350. Line 11: 5,000 - 2,675 = 2,325. Line 13: 2,325. + ny_household_gross_income: 40_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml index ac934f30090..9feeaa1f1c4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ok/tax/income/ok_gross_income.yaml @@ -70,3 +70,16 @@ ok_ssp: 480 ok_gross_income: 12_408 ok_ptc: 0 + +- name: OK gross income counts every railroad tier, as line 5 of Forms 538-S and 538-H does. + period: 2024 + input: + age: 70 + state_code: OK + social_security_retirement: 4_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # Line 4: 4,000 of Social Security. Line 5: 10,000 + 3,000 + 2,000. + ok_gross_income: 19_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_unemployment_compensation_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_unemployment_compensation_subtraction.yaml index 133a96f3efa..16cf712662f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_unemployment_compensation_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_unemployment_compensation_subtraction.yaml @@ -96,3 +96,18 @@ # line 9 = min(2,000, 1,000) = 1,000; line 10 = 2,000 - 1,000 = 1,000. adjusted_gross_income: 14_000 wi_unemployment_compensation_subtraction: 1_000 + +- name: Taxable tier 1 railroad benefits raise the worksheet's income floor as Form 1040 line 6b does + absolute_error_margin: 0.01 + period: 2024 + input: + taxable_unemployment_compensation: 6_000 + adjusted_gross_income: 30_000 + tax_unit_taxable_social_security: 2_000 + tax_unit_taxable_railroad_social_security_equivalent_benefits: 8_000 + filing_status: SINGLE + state_code: WI + output: + # Line 4 is $10,000, line 6 is $22,000, line 7 is $8,000, line 8 is + # $4,000 and line 9 is $4,000. Line 10: $6,000 - $4,000. + wi_unemployment_compensation_subtraction: 2_000 diff --git a/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py b/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py index f4af47c50a2..00e63bfbf43 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/subtractions/dc_disability_exclusion.py @@ -26,11 +26,17 @@ def formula(person, period, parameters): total_capped_payments = tax_unit.sum(capped_disability_payments) federal_agi = tax_unit("adjusted_gross_income", period) - # Line 6 takes Form D-40 line 10, the taxable title II and tier 1 - # railroad retirement benefits (Form 1040 line 6b), which IRC 105(d) - # before the Social Security Amendments of 1983 left out of AGI. - section_86_benefits = tax_unit("tax_unit_taxable_section_86_benefits", period) - reduced_income = federal_agi - section_86_benefits - p.amount + # Line 6 takes Form D-40 line 10, "Taxable amount of social security + # and tier 1 railroad retirement". + taxable_benefits = add( + tax_unit, + period, + [ + "tax_unit_taxable_social_security", + "tax_unit_taxable_railroad_social_security_equivalent_benefits", + ], + ) + reduced_income = federal_agi - taxable_benefits - p.amount capped_reduced_income = max_(reduced_income, 0) return max_(total_capped_payments - capped_reduced_income, 0) diff --git a/policyengine_us/variables/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.py b/policyengine_us/variables/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.py index f0738ef6397..8e7957050f9 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.py +++ b/policyengine_us/variables/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.py @@ -19,15 +19,29 @@ def formula(tax_unit, period, parameters): # Line 8a # Max retirement benefits deduction amount cap = p.cap[filing_status] - # Line 8c - # Social Security retirement benefits received person = tax_unit.members head_or_spouse = person("is_tax_unit_head_or_spouse", period) + # Line 8b + # Railroad Retirement Act benefits received, taxable or not: + # Form RRB-1099 box 5 plus Form RRB-1099-R box 7 (63-3022A(3)). + railroad_benefits = add( + person, + period, + [ + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ], + ) + # Line 8c + # Social Security retirement benefits received ss_retirement = person("social_security_retirement", period) - total_head_spouse_ss = tax_unit.sum(ss_retirement * head_or_spouse) + total_federal_benefits = tax_unit.sum( + (railroad_benefits + ss_retirement) * head_or_spouse + ) # Line 8d - # Base amount minus social Security benefits received - ded_amt = max_(cap - total_head_spouse_ss, 0) + # Base amount minus lines 8b and 8c + ded_amt = max_(cap - total_federal_benefits, 0) # Line 8e # Qualified retirement benefits included in federal income # The head or spouse condition is included in the eligible person variable diff --git a/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_pension_subtraction_amount.py b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_pension_subtraction_amount.py index 07795b5966a..951be24a4bd 100644 --- a/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_pension_subtraction_amount.py +++ b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_pension_subtraction_amount.py @@ -7,7 +7,12 @@ class md_pension_subtraction_amount(Variable): label = "MD pension subtraction from AGI" unit = USD definition_period = YEAR - reference = "https://interactive.marylandtaxes.gov/Individuals/iFile_ChooseForm/PriorYearForms/Resident_Booklet_2021.pdf#page=13" + reference = ( + "https://interactive.marylandtaxes.gov/Individuals/iFile_ChooseForm/PriorYearForms/Resident_Booklet_2021.pdf#page=13", + "https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg§ion=10-209", + # Pension Exclusion Computation Worksheet (13A), line 3. + "https://www.marylandcomptroller.gov/content/dam/mdcomp/tax/instructions/2025/resident-booklet.pdf#page=14", + ) defined_for = StateCode.MD def formula(person, period, parameters): @@ -31,7 +36,20 @@ def formula(person, period, parameters): # calculate pension subtraction amount for each person peninc = add(person, period, p.pension.sources) + excess_military - socsec = person("social_security", period) - amount = min_(peninc, max_(0, p.pension.max_amount - socsec)) + # Worksheet 13A, line 3, and Tax-General 10-209(b)(2): "any payment + # received as old age, survivors, or disability benefits under the + # Social Security Act, the Railroad Retirement Act, or both", whether + # or not federal AGI includes them, so both railroad tiers count. + federal_benefits = add( + person, + period, + [ + "social_security", + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ], + ) + amount = min_(peninc, max_(0, p.pension.max_amount - federal_benefits)) # return pension subtraction amount for each eligible person return eligible * amount diff --git a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py index 7c674b9f168..48f6da44c25 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py +++ b/policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py @@ -41,7 +41,16 @@ def formula(tax_unit, period, parameters): # individual's social security benefits and railroad retirement # benefits paid by the United States", taxable or not, so all tiers # of a railroad annuity count. - federal_benefits = add(person, period, ["social_security", "railroad_benefits"]) + federal_benefits = add( + person, + period, + [ + "social_security", + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ], + ) ss_reduced_cap = max_(p.cap - federal_benefits, 0) non_military_deduction = min_(pension_income, ss_reduced_cap) diff --git a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_gross_income.py b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_gross_income.py index 6bae9a74f09..cc319b760f1 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_gross_income.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/credits/ny_household_gross_income.py @@ -15,14 +15,18 @@ class ny_household_gross_income(Variable): "statute (Tax Law 606(e)(1)(C)) household gross income is the income of " "ALL household members; this variable sums at the tax unit as a " "simplification. It also omits statutory line items PolicyEngine does " - "not yet add back here: pensions and annuities not in AGI (line 13), " + "not yet add back here: pensions and annuities not in AGI other than " + "railroad retirement benefits (line 13), " "cash public assistance and TANF (line 14), workers' compensation, " "child support / support money, and veterans' disability pensions " "(line 11)." ) # Year-specific 2024 instructions; the current_forms/ URL resolves to the # latest (2025) form, whose Step 3 uses federal AGI, not this measure. - reference = "https://www.tax.ny.gov/pdf/2024/inc/it214i_2024.pdf#page=2" # IT-214 Step 3, lines 9-16 + reference = ( + "https://www.tax.ny.gov/pdf/2024/inc/it214i_2024.pdf#page=2", # IT-214 Step 3, lines 9-16 + "https://www.tax.ny.gov/pdf/2024/inc/it214_2024_fill_in.pdf", # Line 13 + ) def formula(tax_unit, period, parameters): # Form IT-214, Step 3 "Determine household gross income" (line 16 = @@ -39,7 +43,28 @@ def formula(tax_unit, period, parameters): nontaxable_social_security = max_(social_security - taxable_social_security, 0) # Line 12: Supplemental Security Income (SSI). ssi = person("ssi", period) + # Line 13: "Pensions and annuities (including railroad retirement + # benefits) not included on lines 9 through 12". Line 11 covers only + # the Social Security Act, so the untaxed part of every railroad tier + # lands here. + railroad_benefits = add( + person, + period, + [ + "railroad_social_security_equivalent_benefits", + "taxable_railroad_pension_benefits", + "tax_exempt_railroad_pension_benefits", + ], + ) + untaxed_railroad_benefits = max_( + railroad_benefits - person("taxable_railroad_benefits", period), 0 + ) # Line 15: other income (tax-exempt interest). tax_exempt_interest = person("tax_exempt_interest_income", period) - additions = tax_unit.sum(nontaxable_social_security + ssi + tax_exempt_interest) + additions = tax_unit.sum( + nontaxable_social_security + + ssi + + untaxed_railroad_benefits + + tax_exempt_interest + ) return agi + additions diff --git a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_unemployment_compensation_subtraction.py b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_unemployment_compensation_subtraction.py index 643b0078853..ddf660d89b5 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_unemployment_compensation_subtraction.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_unemployment_compensation_subtraction.py @@ -28,8 +28,17 @@ def formula(tax_unit, period, parameters): wi = parameters(period).gov.states.wi.tax.income p = wi.subtractions.unemployment_compensation.income_phase_out base_income = p.base[tax_unit("filing_status", period)] - us_taxed_socsec = tax_unit("tax_unit_taxable_social_security", period) - ucsub_income = base_income + us_taxed_socsec + # Worksheet line 4 takes Form 1040 line 6b: taxable title II and tier 1 + # railroad retirement benefits. + section_86_benefits = add( + tax_unit, + period, + [ + "tax_unit_taxable_social_security", + "tax_unit_taxable_railroad_social_security_equivalent_benefits", + ], + ) + ucsub_income = base_income + section_86_benefits excess_inc = max_(0, us_agi - ucsub_income) tuc_limit = excess_inc * p.rate # WI Unemploy Compen Worksheet, Line 8 wi_tuc = min_(tuc_limit, us_tuc) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index a130241436a..1d548066dc6 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -7092,6 +7092,45 @@ candidates: - https://github.com/PolicyEngine/policyengine-us/pull/10663 state: ruled note: The statute's text settles the case, so it governs over the guideline's general list. +- id: us-nd:renters-refund-railroad-benefits + program: renter-credit + years: + from: 2025 + type: tax + question: Does Renter's Refund income count railroad retirement benefits that federal law leaves out of gross income or + bars states from taxing? + sources: + - kind: statute + citation: N.D.C.C. 57-02-08.1(5)(c) + url: https://ndlegis.gov/cencode/t57c02.pdf#page=16 + - kind: statute + citation: 26 U.S.C. 86(d)(1)(B); 45 U.S.C. 231m(a) + url: https://www.law.cornell.edu/uscode/text/26/86#d_1_B + - kind: form + citation: 2025 Form SFN 24777, instructions for lines 4 to 9 + url: https://www.tax.nd.gov/sites/www/files/documents/forms/homestead-disabled-renters/renters-refund-application.pdf + - kind: guidance + citation: Property Tax Credits for North Dakota Homeowners and Renters, item 10 + url: https://www.tax.nd.gov/sites/www/files/documents/guidelines/homestead-veterans-renters/credits-for-nd-homeowners-renters-guideline.pdf#page=2 + readings: + statute: Income includes "social security, or other retirement benefits" and excludes "any amount excluded from income + by federal or state law with the exception of income from social security benefits". The federal exclusion that + exception lifts comes from section 86, which treats tier 1 benefits as social security benefits, so tier 1 benefits + count in full, taxable tier 2 benefits count as other retirement benefits, and the part of tier 2 that section 72 + excludes stays out. + other: Railroad retirement benefits are not social security benefits under the exception, and 45 U.S.C. 231m(a) and + North Dakota's subtraction take them out of income, so none of them count. + example: A renter with $4,000 of wages, $10,000 of tier 1 benefits, $3,000 of taxable tier 2 benefits and $2,000 of exempt + tier 2 benefits has income of $17,000 on the first reading and $4,000 on the second. PE-US follows the first. + pe_us: + follows: statute + code: + - policyengine_us/parameters/gov/states/nd/tax/property/renters_refund/income_sources.yaml + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the first reading (statute first). The form's instructions and the Tax Commissioner's guideline list + pensions, the guideline naming military pensions as well, without limiting them to pensions taxed by the state. - id: us-nd:stillborn-subtraction-dependency-condition program: income-tax years: From 7b3398a1f27f62541580d0e975525cee55a5e62c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 05:45:00 -0500 Subject: [PATCH 05/11] Count railroad benefits in nine more state income lists Missouri's property tax credit, Rhode Island's RI-1040H, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior exemption and New Mexico's modified gross income name railroad retirement benefits. Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess credit count Social Security benefits with their federal meaning, so excluded tier 1 benefits count there too. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 2 +- .../sales_tax_refund/magi_sources.yaml | 5 ++++ .../credits/fairness/income_sources.yaml | 4 +++ .../nm/tax/income/modified_gross_income.yaml | 5 ++++ .../credits/property_tax/income_sources.yaml | 5 ++++ .../senior_disabled/income/sources.yaml | 2 ++ .../credits/homestead/income/sources.yaml | 5 ++++ .../heptc/gross_household_income/sources.yaml | 4 +++ .../sales_tax_refund/co_modified_agi.yaml | 15 +++++++++++ ...d_property_tax_fairness_credit_income.yaml | 14 ++++++++++ .../property_tax/mo_ptc_gross_income.yaml | 16 +++++++++++ .../tax/income/nm_modified_gross_income.yaml | 13 +++++++++ .../ri_property_tax_household_income.yaml | 14 ++++++++++ ...renter_credit_countable_tax_exempt_ss.yaml | 14 ++++++++++ .../wa_pte_combined_disposable_income.yaml | 27 +++++++++++++++++++ .../wi/tax/income/wi_homestead_income.yaml | 15 +++++++++++ .../heptc/wv_gross_household_income.yaml | 14 ++++++++++ .../property_tax/mo_ptc_gross_income.py | 7 +++++ ...t_renter_credit_countable_tax_exempt_ss.py | 14 +++++++--- 19 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/sales_tax_refund/co_modified_agi.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/fairness/me_sales_and_property_tax_fairness_credit_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_household_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/heptc/wv_gross_household_income.yaml diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md index 7b9fc8d5f58..c97ad326966 100644 --- a/changelog.d/state-sseb-worksheets.fixed.md +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -1 +1 @@ -Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit and North Dakota's renter's refund. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit, North Dakota's renter's refund, Missouri's and Rhode Island's property tax credits, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior property tax exemption and New Mexico's modified gross income. Count excluded tier 1 railroad benefits as excluded Social Security benefits in Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess property tax credit. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/sales_tax_refund/magi_sources.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/sales_tax_refund/magi_sources.yaml index 2e16e04c9dc..9311fc05ef3 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/sales_tax_refund/magi_sources.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/sales_tax_refund/magi_sources.yaml @@ -7,6 +7,11 @@ values: 1999-01-01: - adjusted_gross_income - tax_exempt_social_security + # C.R.S. 39-22-2003 adds back "social security benefits excluded from + # federal adjusted gross income", and 39-22-103(11) gives terms their + # federal meaning. IRC 86(d)(1)(B) makes tier 1 railroad benefits social + # security benefits. + - tax_exempt_railroad_social_security_equivalent_benefits # - non_taxable_interest_income_from_state_and_local_bonds metadata: period: year diff --git a/policyengine_us/parameters/gov/states/me/tax/income/credits/fairness/income_sources.yaml b/policyengine_us/parameters/gov/states/me/tax/income/credits/fairness/income_sources.yaml index 69511996e9f..93b422c1bb1 100644 --- a/policyengine_us/parameters/gov/states/me/tax/income/credits/fairness/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/me/tax/income/credits/fairness/income_sources.yaml @@ -3,6 +3,10 @@ values: 2021-01-01: - irs_gross_income - tax_exempt_social_security + # 36 M.R.S. 5213-A(1)(B) and 5219-KK(1)(D): "railroad retirement benefits + # to the extent not included in federal adjusted gross income". + - tax_exempt_railroad_social_security_equivalent_benefits + - tax_exempt_railroad_pension_benefits - tax_exempt_interest_income metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/nm/tax/income/modified_gross_income.yaml b/policyengine_us/parameters/gov/states/nm/tax/income/modified_gross_income.yaml index caac7071fad..b088f73bb24 100644 --- a/policyengine_us/parameters/gov/states/nm/tax/income/modified_gross_income.yaml +++ b/policyengine_us/parameters/gov/states/nm/tax/income/modified_gross_income.yaml @@ -2,6 +2,11 @@ description: New Mexico includes these income sources in its modified gross inco values: 2021-01-01: - tax_unit_social_security + # NMSA 1978 7-2-2(L) counts "(9) annuities", "(11) pensions" and "(16) + # social security benefits", taxable or not, so every railroad tier counts. + - railroad_social_security_equivalent_benefits + - taxable_railroad_pension_benefits + - tax_exempt_railroad_pension_benefits - capital_gains - tanf - ssi diff --git a/policyengine_us/parameters/gov/states/ri/tax/income/credits/property_tax/income_sources.yaml b/policyengine_us/parameters/gov/states/ri/tax/income/credits/property_tax/income_sources.yaml index 0078ec7e790..758b5c2f25b 100644 --- a/policyengine_us/parameters/gov/states/ri/tax/income/credits/property_tax/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ri/tax/income/credits/property_tax/income_sources.yaml @@ -6,6 +6,11 @@ values: - tax_exempt_pension_income - tax_exempt_interest_income - tax_exempt_social_security + # R.I. Gen. Laws 44-33-3: "the gross amount of any pension or annuity + # (including Railroad Retirement Act benefits ...)". Adjusted gross income + # holds the taxable part, so only the rest belongs here. + - tax_exempt_railroad_social_security_equivalent_benefits + - tax_exempt_railroad_pension_benefits - tax_exempt_unemployment_compensation - alimony_income - tanf diff --git a/policyengine_us/parameters/gov/states/wa/dor/property_tax_exemption/senior_disabled/income/sources.yaml b/policyengine_us/parameters/gov/states/wa/dor/property_tax_exemption/senior_disabled/income/sources.yaml index 11b1484dc78..e69921bf575 100644 --- a/policyengine_us/parameters/gov/states/wa/dor/property_tax_exemption/senior_disabled/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wa/dor/property_tax_exemption/senior_disabled/income/sources.yaml @@ -14,6 +14,8 @@ values: # and don't track the included subcomponents separately. # RCW 84.36.383(7)(g) — Social Security and Railroad Retirement not in AGI - tax_exempt_social_security + - tax_exempt_railroad_social_security_equivalent_benefits + - tax_exempt_railroad_pension_benefits # RCW 84.36.383(7)(i) — state/municipal bond interest not in AGI - tax_exempt_interest_income # RCW 84.36.383(7)(a) — capital gains add-back. AGI already includes diff --git a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml index 879c387a65f..34558443df8 100644 --- a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml @@ -13,6 +13,11 @@ values: # additional homestead income sources: - tax_exempt_interest_income - tax_exempt_social_security + # Wis. Stat. 71.52(6): "the gross amount of any pension or annuity + # (including railroad retirement benefits ...)". Adjusted gross income + # holds the taxable part, so only the rest belongs here. + - tax_exempt_railroad_social_security_equivalent_benefits + - tax_exempt_railroad_pension_benefits - tax_exempt_pension_income - tax_exempt_unemployment_compensation # cash transfer income sources: diff --git a/policyengine_us/parameters/gov/states/wv/tax/income/credits/heptc/gross_household_income/sources.yaml b/policyengine_us/parameters/gov/states/wv/tax/income/credits/heptc/gross_household_income/sources.yaml index 940917dea99..f2483003d0b 100644 --- a/policyengine_us/parameters/gov/states/wv/tax/income/credits/heptc/gross_household_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wv/tax/income/credits/heptc/gross_household_income/sources.yaml @@ -24,3 +24,7 @@ values: - tax_exempt_interest_income - workers_compensation - tax_exempt_social_security + # W. Va. Code 11-21-23(d)(1)(D) counts "nontaxable Social Security + # benefits", and 11-21-9(a) gives terms their federal meaning. IRC + # 86(d)(1)(B) makes tier 1 railroad benefits social security benefits. + - tax_exempt_railroad_social_security_equivalent_benefits diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/sales_tax_refund/co_modified_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/sales_tax_refund/co_modified_agi.yaml new file mode 100644 index 00000000000..c6fa81f4f73 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/sales_tax_refund/co_modified_agi.yaml @@ -0,0 +1,15 @@ +- name: Tier 1 railroad benefits excluded from AGI count as social security benefits. + absolute_error_margin: 0.01 + period: 2024 + input: + age: 70 + state_code: CO + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add the 2,100 of tier 1 outside AGI. C.R.S. 39-22-2003 does not name + # the exempt part of a pension. + co_modified_agi: 43_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/fairness/me_sales_and_property_tax_fairness_credit_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/fairness/me_sales_and_property_tax_fairness_credit_income.yaml new file mode 100644 index 00000000000..d40de13e8d4 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/fairness/me_sales_and_property_tax_fairness_credit_income.yaml @@ -0,0 +1,14 @@ +- name: Fairness credit income counts railroad benefits to the extent federal AGI leaves them out. + absolute_error_margin: 0.01 + period: 2024 + input: + age: 70 + state_code: ME + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # Gross income: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add 2,100 of tier 1 and 2,000 of tier 2 outside AGI. + me_sales_and_property_tax_fairness_credit_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.yaml index 228ab9c057c..b997c848827 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.yaml @@ -26,3 +26,19 @@ state_code: MO output: mo_ptc_gross_income: 29_000 # 29 = 13 + 9 + 4 + 3 + +- name: Every railroad tier counts, on lines 2 and 4 of Form MO-PTS, after Missouri AGI removes its taxable part. + absolute_error_margin: 0.01 + period: 2024 + input: + age: 70 + state_code: MO + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # Federal AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Missouri AGI subtracts 7,900 + 3,000 and leaves 30,000. + # Line 2: 10,000. Line 4: 3,000 + 2,000. + mo_ptc_gross_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/nm_modified_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/nm_modified_gross_income.yaml index 06c92d3744d..9e97bac9f25 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/nm_modified_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/nm_modified_gross_income.yaml @@ -110,3 +110,16 @@ output: # § 7-2-2(L): "undiminished by losses". max(-5,000, 0) = 0, so 20,000. nm_modified_gross_income: 20_000 + +- name: NM modified gross income counts every railroad tier, taxable or not. + period: 2024 + absolute_error_margin: 0.01 + input: + age: 70 + state_code: NM + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + nm_modified_gross_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_household_income.yaml new file mode 100644 index 00000000000..3116e1ae36d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_household_income.yaml @@ -0,0 +1,14 @@ +- name: Household income counts the railroad benefits federal AGI leaves out. + absolute_error_margin: 0.01 + period: 2022 + input: + age: 70 + state_code: RI + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add 2,100 of tier 1 and 2,000 of tier 2 outside AGI. + ri_property_tax_household_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit_countable_tax_exempt_ss.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit_countable_tax_exempt_ss.yaml index bfe89b13860..26ce04a227a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit_countable_tax_exempt_ss.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit_countable_tax_exempt_ss.yaml @@ -13,3 +13,17 @@ state_code: VT output: vt_renter_credit_countable_tax_exempt_ss: 0 + +- name: Tier 1 railroad benefits count as Social Security benefits under IRC 86(d). + absolute_error_margin: 0.01 + period: 2022 + input: + age: 70 + state_code: VT + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + output: + # Provisional income 35,000 exceeds the 34,000 single second threshold: + # 0.85 * 1,000 + 4,500 = 5,350 taxable, so 4,650 stays excluded. + # 75 percent of 4,650 = 3,487.50. + vt_renter_credit_countable_tax_exempt_ss: 3_487.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_combined_disposable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_combined_disposable_income.yaml index 8d5b9246b16..1182f70edac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_combined_disposable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/dor/property_tax_exemption/senior_disabled/wa_pte_combined_disposable_income.yaml @@ -217,3 +217,30 @@ # Cotenant filed a separate return; statute folds their disposable income # into the combined total. 30,000 + 25,000 = 55,000. wa_pte_combined_disposable_income: 55_000 + +- name: Railroad retirement benefits outside AGI count, under RCW 84.36.383(7)(g). + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + age: 70 + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + medicare_part_b_premium: 0 + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: WA + output: + # AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add 2,100 of tier 1 and 2,000 of tier 2 outside AGI. + wa_pte_combined_disposable_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 744d7253c8a..adaeb8581a0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -99,3 +99,18 @@ # loss_ald deducts each category's loss on its own, so AGI is 0 and the # add-back is the 5,000 non-SSTB loss. wi_homestead_income: 5_000 + +- name: Case 4, railroad benefits count in full, as Wis. Stat. 71.52(6) requires. + absolute_error_margin: 0.01 + period: 2024 + input: + age: 70 + state_code: WI + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add 2,100 of tier 1 and 2,000 of tier 2 outside AGI. + wi_homestead_income: 45_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/heptc/wv_gross_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/heptc/wv_gross_household_income.yaml new file mode 100644 index 00000000000..50e4f23be94 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/heptc/wv_gross_household_income.yaml @@ -0,0 +1,14 @@ +- name: Tier 1 railroad benefits outside AGI count as nontaxable social security benefits. + absolute_error_margin: 0.01 + period: 2024 + input: + age: 70 + state_code: WV + employment_income: 30_000 + railroad_social_security_equivalent_benefits: 10_000 + taxable_railroad_pension_benefits: 3_000 + tax_exempt_railroad_pension_benefits: 2_000 + output: + # AGI: 30,000 + 7,900 taxable of the 10,000 + 3,000 = 40,900. + # Add the 2,100 of tier 1 outside AGI. + wv_gross_household_income: 43_000 diff --git a/policyengine_us/variables/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.py b/policyengine_us/variables/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.py index 538e720def3..71d37f2a0fc 100644 --- a/policyengine_us/variables/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.py +++ b/policyengine_us/variables/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.py @@ -22,6 +22,13 @@ def formula(tax_unit, period, parameters): "mo_adjusted_gross_income", # line 1 "tax_exempt_pension_income", # line 3 "tax_exempt_interest_income", # line 3 + # Section 135.010 counts "railroad retirement" benefits. Missouri + # AGI subtracts their taxable part, so every tier enters in full: + # line 2 takes "the amount of social security equivalent railroad + # retirement benefits" and line 4 takes the rest of the annuity. + "railroad_social_security_equivalent_benefits", # line 2 + "taxable_railroad_pension_benefits", # line 4 + "tax_exempt_railroad_pension_benefits", # line 4 ] core_income = tax_unit.sum(add(person, period, sources)) # compute social security included in PTC gross income (line 2) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit_countable_tax_exempt_ss.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit_countable_tax_exempt_ss.py index 7a0d84b3b88..07a13c68642 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit_countable_tax_exempt_ss.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit_countable_tax_exempt_ss.py @@ -15,7 +15,15 @@ class vt_renter_credit_countable_tax_exempt_ss(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.vt.tax.income.credits.renter - return ( - tax_unit("tax_exempt_social_security", period) - * p.countable_tax_exempt_ss_fraction + # Section 6061(18)(C) counts "Social Security benefits as defined + # under 26 U.S.C. § 86(d)", and 86(d)(1)(B) includes tier 1 railroad + # benefits. + excluded_benefits = add( + tax_unit, + period, + [ + "tax_exempt_social_security", + "tax_exempt_railroad_social_security_equivalent_benefits", + ], ) + return excluded_benefits * p.countable_tax_exempt_ss_fraction From 56e24936d5736bf3aafe16d20e7aac633e080225 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 05:58:56 -0500 Subject: [PATCH 06/11] Add cross-state property tests for railroad benefits Swapping title II benefits for tier 1 benefits now has to leave federal AGI alone, never raise state tax outside Michigan, and leave every state income test and pension cap unchanged. A step-by-step 2025 Schedule M1M line 12 worksheet checks Minnesota's formula. Also pin Montana's pre-2024 worksheet with tier 1 benefits, and log the open 45 U.S.C. 231m(a) question. Co-Authored-By: Claude Opus 5.5 --- .../income/mt_taxable_social_security.yaml | 22 ++ .../test_state_railroad_benefit_provisions.py | 348 ++++++++++++++++++ source_conflicts/candidates.yaml | 41 +++ 3 files changed, 411 insertions(+) create mode 100644 policyengine_us/tests/test_state_railroad_benefit_provisions.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_social_security.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_social_security.yaml index f8622cf7218..87f21447c63 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_social_security.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/mt_taxable_social_security.yaml @@ -77,3 +77,25 @@ state_code: MT output: mt_taxable_social_security: 85 + +# 2023 Form 2, Taxable Social Security Benefits Worksheet, for a single filer +# with $30,000 of wages, $10,000 of title II benefits and $10,000 of tier 1 +# benefits. Federal taxable benefits (Form 1040 line 6b) are the smaller of +# 0.85 x 20,000 = 17,000 and 0.85 x (40,000 - 34,000) + 4,500 = 9,600. +# Line 1 takes Forms SSA-1099 only: 10,000. Line 2: 5,000. Line 3 subtracts all +# of line 6b from total income: 39,600 - 9,600 = 30,000. Line 6: 35,000. Line 8 +# (subtractions through line 35) leaves out the tier 1 benefits, which line 37 +# subtracts: 0. Line 9: 35,000. Line 11: 10,000. Line 12: 9,000. Line 13: 1,000. +# Line 14: 9,000. Line 15: 4,500. Line 16: 4,500. Line 17: 850. Line 18: 5,350. +# Line 19: 8,500. Line 20: 5,350. +- name: Tier 1 railroad benefits stay out of Montana's modified income and line 1. + period: 2023 + input: + age: 67 + employment_income: 30_000 + social_security_retirement: 10_000 + railroad_social_security_equivalent_benefits: 10_000 + filing_status: SINGLE + state_code: MT + output: + mt_taxable_social_security: 5_350 diff --git a/policyengine_us/tests/test_state_railroad_benefit_provisions.py b/policyengine_us/tests/test_state_railroad_benefit_provisions.py new file mode 100644 index 00000000000..8e15fdcd796 --- /dev/null +++ b/policyengine_us/tests/test_state_railroad_benefit_provisions.py @@ -0,0 +1,348 @@ +"""Property tests for state provisions that read railroad retirement benefits. + +IRC 86(d)(1)(B) makes the tier 1 social security equivalent benefit a social +security benefit for federal tax, and states write their provisions around +that amount, title II Social Security, or both. These properties mostly swap +one benefit for the other and compare the two households. Invariants, for +every input the strategies draw: + +S1. Moving all of a filer's benefits from title II to tier 1 leaves federal + AGI unchanged and never raises state income tax before refundable credits. + Michigan is an intended exception: Form 4884 and MCL 206.30 reduce its + retirement and standard deduction limits by the railroad benefits that + federal AGI includes, and not by Social Security. +S2. In the states that tax no filer's Social Security in 2025, the swap leaves + state income tax unchanged. Michigan is excepted as in S1. Idaho's grocery + credit is excepted after refundable credits: the model's SNAP income lists + no railroad benefits, so the swap can make a household SNAP eligible, and + Idaho denies the credit for SNAP months. Before refundable credits, Idaho + stays neutral. +S3. Each state income test in INCOME_TESTS counts tier 1 like title II: the + swap leaves the test's income unchanged, for any wages and pensions. +S4. Each pension deduction in PENSION_CAPS shrinks equally for title II and + tier 1, because its law reduces the cap by both: the swap leaves the + deduction unchanged. Benefits can only reduce the deduction, and do reduce + it when the pension exceeds the cap. +S5. Minnesota's Social Security subtraction (Minn. Stat. 290.0132, subd. 26) + stays between zero and the taxable title II benefits. Adding the taxable + tier 1 benefits that the model subtracts with the rest of the railroad + annuity, as Schedule M1M line 17 does, total relief reaches at least what + the same benefits would get as title II. +D1. Differential: walking the 2025 Schedule M1M line 12 worksheet step by step, + with its published amounts and Form 1040 lines taken from the model, gives + the model's subtraction for every filing status. + +The model estimates state income tax paid from withholding, and its +state_withholding_agi counts title II benefits but not railroad benefits. That +estimate feeds the deduction for state and local taxes, which has nothing to +do with how a state treats the benefits, so S1 and S2 set its input to zero +for both households. + +YAML tests pin worked examples; these properties quantify over amounts, filing +status and every state, which YAML cannot. +""" + +import math + +from hypothesis import given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.tests.test_railroad_retirement_taxation import ( + INCOME_TAX_STATES, + calc, + situation, + tolerance, +) + +YEAR = 2025 +TITLE_II = "social_security_retirement" +TIER_1 = "railroad_social_security_equivalent_benefits" + +# States whose tax in 2025 can reach title II benefits: their own Social +# Security worksheets and limits or, in Missouri, retirement benefits of +# filers under 62 (Form MO-A Section C). +TAXES_SOCIAL_SECURITY = {"CO", "CT", "MN", "MO", "MT", "NM", "RI", "UT", "VT", "WV"} + +# Form 4884 and MCL 206.30 reduce Michigan's limits by taxable railroad +# benefits, so tier 1 can cost a filer more than title II would. +INTENDED_INCREASE = {"MI"} + +NEUTRAL_STATES = { + state + for state in INCOME_TAX_STATES + if state not in TAXES_SOCIAL_SECURITY | INTENDED_INCREASE +} + +# Each of these counts tier 1 railroad benefits with Social Security benefits +# in full, or (Pennsylvania) both at the same fraction, whether or not federal +# AGI includes them. The YAML test beside each variable quotes its law. +INCOME_TESTS = [ + ("CO", "co_modified_agi"), + ("MA", "ma_scb_total_income"), + ("ME", "me_sales_and_property_tax_fairness_credit_income"), + ("MI", "mi_household_resources"), + ("MN", "mn_homestead_credit_refund_household_income"), + ("MO", "mo_ptc_gross_income"), + ("MT", "mt_elderly_homeowner_or_renter_credit_gross_household_income"), + ("ND", "nd_renters_refund_income"), + ("NM", "nm_modified_gross_income"), + ("NY", "ny_household_gross_income"), + ("OK", "ok_gross_income"), + ("PA", "pa_property_tax_or_rent_rebate_income"), + ("RI", "ri_property_tax_household_income"), + ("UT", "ut_homeowner_renter_relief_household_income"), + ("VT", "vt_renter_credit_countable_tax_exempt_ss"), + ("WA", "wa_pte_combined_disposable_income"), + ("WI", "wi_homestead_income"), + ("WV", "wv_gross_household_income"), +] + +# Idaho Code 63-3022A(3), 36 M.R.S. 5122(2)(M-2)(1)(a)(ii) and Md. Tax-General +# 10-209(b)(2). +PENSION_CAPS = [ + ("ID", "id_retirement_benefits_deduction"), + ("MD", "md_pension_subtraction_amount"), + ("ME", "me_pension_income_deduction"), +] + +# Exceeds every 2025 cap in PENSION_CAPS, joint filers' included. +BINDING_PENSION = 100_000 + +# Schedule M1M worksheet for line 12, 2025: the steps 2, 4, 20 and 23 amounts. +M1M_2025 = { + "SINGLE": (84_490, 4_000, 69_250, 4_560), + "HEAD_OF_HOUSEHOLD": (84_490, 4_000, 69_250, 4_560), + "JOINT": (108_320, 4_000, 88_630, 5_840), + "SURVIVING_SPOUSE": (108_320, 4_000, 88_630, 5_840), + "SEPARATE": (54_160, 2_000, 44_315, 2_920), +} + +ages = st.sampled_from((45, 58, 62, 67, 72)) +wages = st.integers(min_value=0, max_value=200_000) +pensions = st.integers(min_value=0, max_value=60_000) +benefits = st.integers(min_value=1, max_value=60_000) +amounts = st.integers(min_value=0, max_value=60_000) + + +def couple(head, joint, age): + """Return the head alone, or the head and a spouse with no income.""" + return [head, {"age": age, "role": "spouse"}] if joint else [head] + + +def swap_simulation(states, age, earnings, pension, amount, joint): + """Build one Simulation with a title II and a tier 1 household per state.""" + households = [ + ( + state, + couple( + { + "age": age, + "employment_income": earnings, + "taxable_private_pension_income": pension, + label: amount, + }, + joint, + age, + ), + ) + for state in states + for label in (TITLE_II, TIER_1) + ] + built = situation(households) + for unit in built["tax_units"].values(): + unit["state_and_local_sales_or_income_tax"] = {YEAR: 0} + return Simulation(situation=built) + + +def by_household(simulation, variable): + return simulation.calculate(variable, YEAR, map_to="household").astype(float) + + +def by_tax_unit(simulation, variable, year): + return simulation.calculate(variable, year, map_to="tax_unit").astype(float) + + +@settings(max_examples=8, deadline=None) +@given(age=ages, earnings=wages, pension=pensions, amount=benefits, joint=st.booleans()) +def test_s1_s2_swapping_title_ii_for_tier_1(age, earnings, pension, amount, joint): + simulation = swap_simulation( + INCOME_TAX_STATES, age, earnings, pension, amount, joint + ) + agi = calc(simulation, "adjusted_gross_income") + before_refundable = calc(simulation, "state_income_tax_before_refundable_credits") + after_refundable = calc(simulation, "state_income_tax") + allowed = tolerance(earnings + pension + amount) + for index, state in enumerate(INCOME_TAX_STATES): + title_ii, tier_1 = 2 * index, 2 * index + 1 + assert abs(agi[tier_1] - agi[title_ii]) <= allowed, state + change = before_refundable[tier_1] - before_refundable[title_ii] + if state not in INTENDED_INCREASE: + assert change <= allowed, (state, change) + if state in NEUTRAL_STATES: + assert abs(change) <= allowed, (state, change) + if state != "ID": + change = after_refundable[tier_1] - after_refundable[title_ii] + assert abs(change) <= allowed, (state, change) + + +@settings(max_examples=15, deadline=None) +@given(age=ages, earnings=wages, pension=pensions, amount=benefits, joint=st.booleans()) +def test_s3_income_tests_count_tier_1_like_title_ii( + age, earnings, pension, amount, joint +): + states = [state for state, _ in INCOME_TESTS] + simulation = swap_simulation(states, age, earnings, pension, amount, joint) + allowed = tolerance(earnings + pension + amount) + for index, (state, variable) in enumerate(INCOME_TESTS): + income = by_household(simulation, variable) + title_ii, tier_1 = income[2 * index], income[2 * index + 1] + assert abs(tier_1 - title_ii) <= allowed, (state, variable, title_ii, tier_1) + + +@settings(max_examples=15, deadline=None) +@given( + age=st.sampled_from((67, 72)), + pension=st.one_of( + st.integers(min_value=1_000, max_value=60_000), st.just(BINDING_PENSION) + ), + amount=benefits, + joint=st.booleans(), +) +def test_s4_pension_caps_shrink_alike(age, pension, amount, joint): + households = [ + ( + state, + couple( + {"age": age, "taxable_public_pension_income": pension} | benefit, + joint, + age, + ), + ) + for state, _ in PENSION_CAPS + for benefit in ({TITLE_II: amount}, {TIER_1: amount}, {}) + ] + simulation = Simulation(situation=situation(households)) + allowed = tolerance(pension + amount) + for index, (state, variable) in enumerate(PENSION_CAPS): + deduction = by_household(simulation, variable) + title_ii, tier_1, neither = deduction[3 * index : 3 * index + 3] + assert abs(tier_1 - title_ii) <= allowed, (state, title_ii, tier_1) + assert tier_1 <= neither + allowed, (state, tier_1, neither) + if pension == BINDING_PENSION: + assert tier_1 < neither - allowed, (state, tier_1, neither) + + +@settings(max_examples=25, deadline=None) +@given( + age=ages, + earnings=wages, + pension=pensions, + title_ii=amounts, + tier_1=amounts, + joint=st.booleans(), + year=st.sampled_from((2022, 2025)), +) +def test_s5_minnesota_subtraction_bounds( + age, earnings, pension, title_ii, tier_1, joint, year +): + mixed = { + "age": age, + "employment_income": earnings, + "taxable_private_pension_income": pension, + TITLE_II: title_ii, + TIER_1: tier_1, + } + all_title_ii = mixed | {TITLE_II: title_ii + tier_1, TIER_1: 0} + simulation = Simulation( + situation=situation( + [ + ("MN", couple(mixed, joint, age)), + ("MN", couple(all_title_ii, joint, age)), + ], + year=year, + ) + ) + subtraction = calc(simulation, "mn_social_security_subtraction", year) + taxable_title_ii = calc(simulation, "tax_unit_taxable_social_security", year) + taxable_tier_1 = by_tax_unit( + simulation, "taxable_railroad_social_security_equivalent_benefits", year + ) + allowed = tolerance(earnings + pension + title_ii + tier_1) + assert subtraction[0] >= -allowed + assert subtraction[0] <= taxable_title_ii[0] + allowed + assert subtraction[0] + taxable_tier_1[0] >= subtraction[1] - allowed + + +def schedule_m1m_line_12_2025( + status, agi, line_9, line_6a, line_6b, line_2a, schedule_1_line_26, tier_1 +): + """Walk the 2025 worksheet for Schedule M1M line 12, one filer at a time. + + Tier 1 benefits reach line 17 in the model, so this returns line 12 less + any tier 1 benefits. The worksheet's steps 26 to 29 do that above the + threshold. At or below it line 12 takes all of line 6b, and line 17 then + leaves those benefits out. Schedule 1 line 21 is zero for these filers. + """ + threshold, increment, income_amount, maximum = M1M_2025[status] + step_27 = max(line_6b - tier_1, 0) + if agi <= threshold: + return step_27 + step_4 = min(math.ceil((agi - threshold) / increment), 10) + step_7 = step_4 * 0.1 * line_6b + step_8 = max(line_6b - step_7, 0) + step_11 = line_9 - line_6b + step_15 = step_11 + line_6a * 0.5 + line_2a + step_19 = max(step_15 - max(schedule_1_line_26, 0), 0) + step_21 = max(step_19 - income_amount, 0) + step_24 = max(maximum - step_21 * 0.2, 0) + step_28 = min(step_24, step_27) + step_29 = max(step_8 - tier_1, 0) + return max(step_28, step_29) + + +@settings(max_examples=40, deadline=None) +@given( + status=st.sampled_from(sorted(M1M_2025)), + age=ages, + earnings=wages, + self_employment=st.integers(min_value=0, max_value=80_000), + pension=pensions, + exempt_interest=st.integers(min_value=0, max_value=20_000), + title_ii=amounts, + tier_1=amounts, +) +def test_d1_minnesota_subtraction_matches_the_2025_worksheet( + status, age, earnings, self_employment, pension, exempt_interest, title_ii, tier_1 +): + head = { + "age": age, + "employment_income": earnings, + "self_employment_income": self_employment, + "taxable_private_pension_income": pension, + "tax_exempt_interest_income": exempt_interest, + TITLE_II: title_ii, + TIER_1: tier_1, + } + built = situation([("MN", couple(head, status == "JOINT", age))]) + built["tax_units"]["h0"]["filing_status"] = {YEAR: status} + simulation = Simulation(situation=built) + + def form_line(variable): + return float(by_tax_unit(simulation, variable, YEAR)[0]) + + taxable_title_ii = form_line("taxable_social_security") + taxable_tier_1 = form_line("taxable_railroad_social_security_equivalent_benefits") + expected = schedule_m1m_line_12_2025( + status, + agi=form_line("adjusted_gross_income"), + line_9=form_line("irs_gross_income"), + line_6a=form_line("tax_unit_section_86_benefits"), + line_6b=taxable_title_ii + taxable_tier_1, + line_2a=exempt_interest, + schedule_1_line_26=form_line("above_the_line_deductions"), + tier_1=taxable_tier_1, + ) + actual = form_line("mn_social_security_subtraction") + allowed = tolerance(earnings + self_employment + pension + title_ii + tier_1) + assert abs(actual - expected) <= allowed, (actual, expected) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 1d548066dc6..106f71d4b0b 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -13163,6 +13163,47 @@ candidates: note: The same instructions say on page 1 that income excepted under sections 1402(a)(1) through (17) "is subject to NIIT", and the self-employment bullet in line 4b's list removes income taken into account in self-employment income. +- id: us:railroad-benefits-in-state-income-tests + program: income-tax + years: + from: 2021 + type: tax + question: Does 45 U.S.C. 231m(a), under which no railroad annuity shall "be subject to any tax", bar a state from + counting the annuity in an income test, phase-out or deduction limit that sizes the tax on other income? + sources: + - kind: statute + citation: 45 U.S.C. 231m(a) + url: https://www.law.cornell.edu/uscode/text/45/231m + - kind: statute + citation: 36 M.R.S. 5122(2)(M-2)(1)(a)(ii), reducing Maine's pension deduction by "railroad retirement benefits paid + by the United States" + url: https://legislature.maine.gov/statutes/36/title36sec5122.html + - kind: statute + citation: Md. Code, Tax-General 10-209(b)(2), reducing Maryland's pension exclusion by payments under the Railroad + Retirement Act + url: https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg§ion=10-209 + readings: + statute: The annuity itself stays untaxed, and a state may count it as a measure, as each state's statute or form + says. The tax clause lacks the "considered in computing a tax" words of 31 U.S.C. 3124(a). + other: The words "any tax ... under any circumstances whatsoever" also bar counting the annuity where that raises + the tax on other income, at least when a deduction shrinks because the filer holds the annuity. + example: In 2025, a Maine filer with a $60,000 pension and $20,000 of tier 1 benefits deducts $28,216 under the + statute ($48,216 less $20,000) and $48,216 under the other reading. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/me/tax/income/adjusted_gross_income/me_pension_income_deduction.py + - policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_pension_subtraction_amount.py + - policyengine_us/variables/gov/states/id/tax/income/deductions/retirement_benefits/id_retirement_benefits_deduction.py + - policyengine_us/variables/gov/states/mi/tax/income/mi_military_and_railroad_retirement_subtraction.py + - policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: open + note: PE-US applies each state's provision as that state's statute and forms read it, whether the provision measures + income with federal AGI, names railroad benefits in an income test, or shrinks a deduction limit because of the + benefits. No decision found applies 231m(a) to such a provision. The Oregon Tax Court's Deras v. Department of + Revenue (2026) reached AGI phase-outs under Oregon's constitution and 31 U.S.C. 3124, whose words differ. - id: us:recovery-rebate-joint-claimable-spouse program: recovery-rebate-credit years: From 98f3b025e3dcbaea188e8f3748e337822b584c8f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 12:57:04 -0500 Subject: [PATCH 07/11] Answer the tax review of the state railroad benefit changes Minnesota's Social Security subtraction adds half of the taxable benefits to provisional income, as Minn. Stat. 290.0132 subd. 26(i) defines it since the 2019 amendment, in the baseline and in the HF1938 repeal reform. The M1M worksheets add half of all benefits; the register entry records the reading. Minnesota's homestead credit refund income counts the tax-free rest of a railroad annuity, which 290A.03 subd. 3(a)(2)(v) reaches because railroad employers fund tier 2 too (26 U.S.C. 3221(b)). The property tests now hold Minnesota's S1 increase to its top rate times the elderly or disabled subtraction lost, treat Idaho's SNAP-driven S2 change as a model gap tied to #10551, add the rest of the annuity to S3 and S4, and add S6: railroad benefits never lower a state base, and each Social Security subtraction stays within taxable title II benefits. D1 follows subd. 26(i). Rhode Island's worksheet leaves railroad benefits out, so its register entry goes; Utah's entry ends in 2024, when the worksheet starts taking those benefits back out. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 2 +- .../total_benefit_fraction.yaml | 4 +- .../reforms/states/mn/walz/mn_walz_hf1938.py | 20 +- .../mn_social_security_subtraction.yaml | 66 ++-- ...estead_credit_refund_household_income.yaml | 29 ++ .../ri_social_security_modification.yaml | 15 +- .../ut_ss_benefits_credit_max.yaml | 5 +- .../states/mn/walz/mn_walz_hf1938.yaml | 23 ++ .../test_state_railroad_benefit_provisions.py | 282 +++++++++++++++--- .../mn_social_security_subtraction.py | 10 +- ...omestead_credit_refund_household_income.py | 17 +- .../person/retirement/railroad_benefits.py | 5 +- source_conflicts/candidates.yaml | 98 +++--- 13 files changed, 435 insertions(+), 141 deletions(-) diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md index c97ad326966..38e7d8a3802 100644 --- a/changelog.d/state-sseb-worksheets.fixed.md +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -1 +1 @@ -Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Count them in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit, North Dakota's renter's refund, Missouri's and Rhode Island's property tax credits, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior property tax exemption and New Mexico's modified gross income. Count excluded tier 1 railroad benefits as excluded Social Security benefits in Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess property tax credit. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Add half of the taxable benefits, not half of all benefits, to provisional income for Minnesota's alternate Social Security subtraction, as subd. 26(i) provides, in the baseline and in the HF1938 repeal reform. Count the part of a railroad annuity recovered tax free in Minnesota's homestead credit refund income, as 290A.03 subd. 3(a)(2)(v) counts annuities the claimant did not fund alone. Leave dependents' own Social Security benefits out of Minnesota's elderly and disabled subtraction, which reads the head's and spouse's benefits, as section 22(c)(3) does. Count tier 1 benefits in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit, North Dakota's renter's refund, Missouri's and Rhode Island's property tax credits, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior property tax exemption and New Mexico's modified gross income. Count excluded tier 1 railroad benefits as excluded Social Security benefits in Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess property tax credit. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. diff --git a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml index 0d8ffbf280e..d553fd94baa 100644 --- a/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml +++ b/policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml @@ -1,11 +1,11 @@ -description: Minnesota counts this fraction of the head's and spouse's social security benefits, title II and tier 1 railroad retirement benefits together (Form 1040 line 6a), in provisional income for the alternate social security subtraction. +description: Minnesota counts this fraction of taxable social security benefits, title II and tier 1 railroad retirement benefits together (Form 1040 line 6b), in provisional income for the alternate social security subtraction. values: 2021-01-01: 0.5 metadata: period: year unit: /1 - label: Minnesota social security subtraction total benefit fraction + label: Minnesota social security subtraction taxable benefit fraction reference: - title: Minnesota Statutes 290.0132 Subd. 26.Social Security benefits. (i) href: https://www.revisor.mn.gov/statutes/cite/290.0132#stat.290.0132.26 diff --git a/policyengine_us/reforms/states/mn/walz/mn_walz_hf1938.py b/policyengine_us/reforms/states/mn/walz/mn_walz_hf1938.py index 0a2f4903d04..3f6158ad5ff 100644 --- a/policyengine_us/reforms/states/mn/walz/mn_walz_hf1938.py +++ b/policyengine_us/reforms/states/mn/walz/mn_walz_hf1938.py @@ -44,13 +44,21 @@ def formula(tax_unit, period, parameters): net_income_fraction = p.net_income_fraction alt_amount = p.alternative_amount[filing_status] # calculate subtraction amount (following "Worksheet for line 12") - # ... US-taxable social security benefits + # ... US-taxable social security benefits: Form 1040 line 6b holds + # the taxable title II and tier 1 railroad retirement benefits, + # which subd. 26(e) (2022), now (i), defines through IRC 86(d)(1) us_taxable_oasdi = add(tax_unit, period, ["taxable_social_security"]) + us_taxable_tier_1 = add( + tax_unit, + period, + ["taxable_railroad_social_security_equivalent_benefits"], + ) + us_taxable_benefits = us_taxable_oasdi + us_taxable_tier_1 # ... alternative benefit subtraction amount us_gross_income = add(tax_unit, period, ["irs_gross_income"]) - adj_income = us_gross_income - us_taxable_oasdi - total_oasdi = add(tax_unit, period, ["social_security"]) - oasdi_amount = total_oasdi * total_benefit_fraction + adj_income = us_gross_income - us_taxable_benefits + # Provisional income adds one-half of the taxable benefits. + oasdi_amount = us_taxable_benefits * total_benefit_fraction tax_exempt_int = add(tax_unit, period, ["tax_exempt_interest_income"]) sum_income = adj_income + oasdi_amount + tax_exempt_int us_ald = tax_unit("above_the_line_deductions", period) @@ -59,6 +67,10 @@ def formula(tax_unit, period, parameters): income = max_(0, sum_income - mn_ald) net_income = max_(0, income - income_amount) alt_sub_amt = max_(0, alt_amount - (net_income * net_income_fraction)) + # Line 17 subtracts the taxable tier 1 benefits with the rest of + # the railroad annuity (taxable_railroad_benefits), so line 12 + # takes the taxable title II benefits, as in the 2022 worksheet's + # steps 17-20. return min_(us_taxable_oasdi, alt_sub_amt) class mn_standard_deduction(Variable): diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml index 8ca0511022c..340c8311e71 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/mn_social_security_subtraction.yaml @@ -135,9 +135,10 @@ state_code: MN output: # Simplified: AGI is $39,220 over $110,780, so 10 x 10% = 100% reduction, $0. - # Provisional income = (150,000 - 85,000) + 50% x 100,000 = 115,000. - # Alternate = 5,840 - 20% x (115,000 - 88,630) = 5,840 - 5,274 = 566. - mn_social_security_subtraction: 566 + # Provisional income, subd. 26(i): (150,000 - 85,000) + 50% x 85,000 of + # taxable benefits = 107,500. + # Alternate = 5,840 - 20% x (107,500 - 88,630) = 5,840 - 3,774 = 2,066. + mn_social_security_subtraction: 2_066 - name: MN social security subtraction 2026 single at the simplified phase-out threshold absolute_error_margin: 0.01 @@ -180,18 +181,21 @@ # Federal: provisional income 65,000 - 300 + 5,000 = 69,700, so 85% of # benefits, 8,500, are taxable. tax_unit_taxable_social_security: 8_500 - # Steps 1-3: 73,500 - 8,500 = 65,000; step 5: 5,000; step 7: 70,000; - # step 8: Schedule 1 lines 10-19 and 22 only, 0 (not line 10b); - # step 9: 70,000; step 11: 70,000 - 62,090 = 7,910; step 12: 1,582; - # step 14: 4,090 - 1,582 = 2,508; step 18: min(2,508, 8,500). - mn_social_security_subtraction: 2_508 + # Steps 1-3: 73,500 - 8,500 = 65,000. Steps 4-5 take half of Form 1040 + # line 6a; subd. 26(e) (2020), now (i), adds half of the taxable benefits, + # 4,250. Step 7: 69,250; step 8: Schedule 1 lines 10-19 and 22 only, 0 + # (not line 10b); step 9: 69,250; step 11: 69,250 - 62,090 = 7,160; step + # 12: 1,432; step 14: 4,090 - 1,432 = 2,658; step 18: min(2,658, 8,500). + mn_social_security_subtraction: 2_658 # Tier 1 railroad retirement benefits. Minn. Stat. 290.0132, subd. 26(i) gives # "Social Security benefits" the meaning in IRC 86(d)(1), so Form 1040 lines 6a # and 6b carry title II and tier 1 benefits together. The model subtracts the # taxable tier 1 benefits with the rest of the railroad annuity # (taxable_railroad_benefits, Schedule M1M line 17), so line 12 leaves them out -# as steps 26-29 of the 2025 worksheet for line 12 do. +# as steps 26-29 of the 2025 worksheet for line 12 do. Provisional income adds +# "one-half of the taxable Social Security benefits", half of line 6b, as subd. +# 26(i) reads, where steps 12 and 13 of the worksheet take half of line 6a. - name: MN social security subtraction 2025 single below the threshold leaves the tier 1 benefits to line 17 absolute_error_margin: 0.01 period: 2025 @@ -245,9 +249,9 @@ output: # Steps 1-5: (92,000 - 84,490) / 4,000 = 1.88, rounded up to 2, so 20%. # Steps 6-8: 25,500 - 20% x 25,500 = 20,400. - # Steps 9-19: 92,000 - 25,500 + 50% x 30,000 = 81,500 of provisional - # income. Steps 20-24: 4,560 - 20% x (81,500 - 69,250) = 2,110. - # Steps 25-28: min(2,110, 25,500 - 8,500) = 2,110. + # Steps 9-19: 92,000 - 25,500 + 50% x 25,500 = 79,250 of provisional + # income. Steps 20-24: 4,560 - 20% x (79,250 - 69,250) = 2,560. + # Steps 25-28: min(2,560, 25,500 - 8,500) = 2,560. # Step 29: 20,400 - 8,500 = 11,900. Step 30: 11,900. # With line 17's 8,500, the subtractions total 20,400. mn_social_security_subtraction: 11_900 @@ -268,10 +272,10 @@ output: # Steps 1-8: (150,000 - 108,320) / 4,000 rounds up to 11, capped at 10, # so the simplified subtraction is $0. - # Steps 9-19: 150,000 - 85,000 + 50% x 100,000 = 115,000. - # Steps 20-24: 5,840 - 20% x (115,000 - 88,630) = 566. - # Steps 25-28: min(566, 85,000 - 34,000) = 566. - mn_social_security_subtraction: 566 + # Steps 9-19: 150,000 - 85,000 + 50% x 85,000 = 107,500. + # Steps 20-24: 5,840 - 20% x (107,500 - 88,630) = 2,066. + # Steps 25-28: min(2,066, 85,000 - 34,000) = 2,066. + mn_social_security_subtraction: 2_066 - name: MN social security subtraction 2025 joint alternate method capped at taxable title II benefits absolute_error_margin: 0.01 @@ -287,9 +291,9 @@ state_code: MN output: # Steps 1-8: (145,000 - 108,320) / 4,000 rounds up to 10, so $0. - # Steps 9-19: 145,000 - 85,000 + 50% x 100,000 = 110,000. - # Steps 20-24: 5,840 - 20% x (110,000 - 88,630) = 1,566. - # Steps 25-28: min(1,566, 85,000 - 84,150) = 850. + # Steps 9-19: 145,000 - 85,000 + 50% x 85,000 = 102,500. + # Steps 20-24: 5,840 - 20% x (102,500 - 88,630) = 3,066. + # Steps 25-28: min(3,066, 85,000 - 84,150) = 850. mn_social_security_subtraction: 850 - name: MN social security subtraction 2022 provisional income counts tier 1 benefits @@ -301,14 +305,15 @@ taxable_social_security: 51_000 railroad_social_security_equivalent_benefits: 40_000 taxable_railroad_social_security_equivalent_benefits: 34_000 - irs_gross_income: 120_000 - adjusted_gross_income: 120_000 + irs_gross_income: 130_000 + adjusted_gross_income: 130_000 state_code: MN output: - # 2022 worksheet for line 12: steps 1-11, 120,000 - 85,000 + 50% x - # 100,000 = 85,000; steps 12-16, 5,450 - 20% x (85,000 - 82,770) = - # 5,004; steps 17-20, min(5,004, 85,000 - 34,000) = 5,004. - mn_social_security_subtraction: 5_004 + # 2022 worksheet for line 12, with subd. 26(e), now (i), adding half of + # line 6b: steps 1-11, 130,000 - 85,000 + 50% x 85,000 = 87,500; steps + # 12-16, 5,450 - 20% x (87,500 - 82,770) = 4,504; steps 17-20, + # min(4,504, 85,000 - 34,000) = 4,504. + mn_social_security_subtraction: 4_504 - name: MN social security subtraction 2025 provisional income leaves out a dependent's benefits absolute_error_margin: 0.01 @@ -337,8 +342,9 @@ members: [head, spouse, child] state_code: MN output: - # Step 12 takes Form 1040 line 6a, which holds the filers' benefits only; - # the child's $12,000 belongs on the child's return (IRS Pub. 915). - # Provisional income: 150,000 - 85,000 + 50% x 100,000 = 115,000. - # Steps 20-24: 5,840 - 20% x (115,000 - 88,630) = 566. - mn_social_security_subtraction: 566 + # Form 1040 line 6b holds the filers' taxable benefits only; the child's + # $12,000 belongs on the child's return (IRS Pub. 915). + taxable_social_security: [85_000, 0, 0] + # Provisional income: 150,000 - 85,000 + 50% x 85,000 = 107,500. + # Steps 20-24: 5,840 - 20% x (107,500 - 88,630) = 2,066. + mn_social_security_subtraction: 2_066 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml index 43b5b933b7a..2cd408e11ef 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.yaml @@ -50,3 +50,32 @@ output: # 20,000 + (10,000 - 1,000) + (4,000 - 400) - 5,200 (age 65 or older). mn_homestead_credit_refund_household_income: 27_400 + +- name: Line 6 adds the part of a railroad annuity recovered tax free + # Minn. Stat. 290A.03, subd. 3(a)(2)(v) counts a pension or annuity, + # railroad retirement benefits included, "which was not exclusively funded + # by the claimant or spouse". Railroad employers pay tier 2 tax as employees + # do (26 U.S.C. 3201(b), 3221(b)), so the annuity counts in full, the part + # that returns the employee's tier 2 tax tax free included, and subd. + # 3(b)(2), for annuities the claimant or spouse funded alone, doesn't reach + # it. + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + is_tax_unit_head: true + age: 70 + taxable_railroad_pension_benefits: 20_000 + tax_exempt_railroad_pension_benefits: 5_000 + tax_units: + tax_unit: + members: [head] + adjusted_gross_income: 20_000 + households: + household: + members: [head] + state_code: MN + output: + # 20,000 + 5,000 - 5,200 (age 65 or older). + mn_homestead_credit_refund_household_income: 19_800 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml index 671d0fecea1..c7368f48490 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.yaml @@ -132,13 +132,11 @@ output: ri_social_security_modification: 10_000 -# Tier 1 railroad retirement benefits. R.I. Gen. Laws 44-30-12(c)(8) subtracts -# the "social security benefits includible in federal adjusted gross income", -# an IRC 86 amount that counts tier 1 benefits, and Schedule M line 1d -# subtracts the same taxable tier 1 benefits as railroad retirement benefits -# exempt under federal law (taxable_railroad_benefits, 45 U.S.C. 231m(a)). -# Line 1d has no age or income test, so the model takes the tier 1 benefits -# there, and the worksheet's line 11 holds the taxable title II benefits. +# Tier 1 railroad retirement benefits. Schedule M line 1d subtracts the taxable +# tier 1 benefits as railroad retirement benefits exempt under federal law +# (taxable_railroad_benefits, 45 U.S.C. 231m(a)). The header of the 2024 +# worksheet reads "Do not include amounts from Railroad Retirement Benefits on +# this worksheet", so line 13 holds the taxable title II benefits alone. - name: RI Social Security modification 2024 takes the taxable tier 1 benefits on line 1d period: 2024 absolute_error_margin: 0.01 @@ -164,6 +162,5 @@ taxable_railroad_benefits: 4_800 ri_social_security_modification_eligible: true # Line 12: 1.0000, since the filer was born before the 2024 cutoff. Line - # 13: the 4,800 of taxable title II benefits. With line 6b on line 11, - # line 13 would be 9,600 and line 1d would subtract the 4,800 again. + # 13: the 4,800 of taxable title II benefits. ri_social_security_modification: 4_800 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml index d86bb4da47e..717657fa243 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ss_benefits_credit/ut_ss_benefits_credit_max.yaml @@ -59,8 +59,9 @@ # income. Tier 1 benefits are paid under the Railroad Retirement Act, and the # subtraction for railroad retirement income (code 78, # taxable_railroad_benefits) removes them from state taxable income, so line 1 -# holds the taxable title II benefits alone, though it points to Form 1040 -# line 6b. +# holds the taxable title II benefits alone, though the 2024 worksheet points +# to Form 1040 line 6b. From 2025 the worksheet's line 2 takes those benefits +# back out of line 1. - name: UT Social Security benefits credit 2024 leaves the taxable tier 1 benefits out of line 1 period: 2024 absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/contrib/states/mn/walz/mn_walz_hf1938.yaml b/policyengine_us/tests/policy/contrib/states/mn/walz/mn_walz_hf1938.yaml index aacb657d111..d2a3a375c2b 100644 --- a/policyengine_us/tests/policy/contrib/states/mn/walz/mn_walz_hf1938.yaml +++ b/policyengine_us/tests/policy/contrib/states/mn/walz/mn_walz_hf1938.yaml @@ -128,3 +128,26 @@ # (0.8 x 10,000 = 8,000) and 3% of AGI over $220,650 (53,380.50). # 10,000 + 49,900 - 8,000 mn_itemized_deductions: 51_900 + +- name: Social security subtraction with reform counts tier 1 railroad retirement benefits in provisional income + absolute_error_margin: 0.01 + period: 2023 + reforms: policyengine_us.reforms.states.mn.walz.mn_walz_hf1938.mn_walz_hf1938 + input: + gov.contrib.states.mn.walz.hf1938.repeal: true + filing_status: JOINT + social_security: 60_000 + taxable_social_security: 51_000 + railroad_social_security_equivalent_benefits: 40_000 + taxable_railroad_social_security_equivalent_benefits: 34_000 + irs_gross_income: 150_000 + adjusted_gross_income: 150_000 + state_code: MN + output: + # Minn. Stat. 290.0132, subd. 26(e) (2022), now (i): provisional income is + # 150,000 - 85,000 + 50% x 85,000 of taxable title II and tier 1 benefits + # = 107,500. Maximum: 5,840 - 20% x (107,500 - 88,630) = 2,066. Line 12 + # takes min(2,066, 51,000), and line 17 subtracts the 34,000 of taxable + # tier 1 benefits. + mn_social_security_subtraction: 2_066 + taxable_railroad_benefits: 34_000 diff --git a/policyengine_us/tests/test_state_railroad_benefit_provisions.py b/policyengine_us/tests/test_state_railroad_benefit_provisions.py index 8e15fdcd796..1045e00f027 100644 --- a/policyengine_us/tests/test_state_railroad_benefit_provisions.py +++ b/policyengine_us/tests/test_state_railroad_benefit_provisions.py @@ -6,31 +6,62 @@ one benefit for the other and compare the two households. Invariants, for every input the strategies draw: -S1. Moving all of a filer's benefits from title II to tier 1 leaves federal - AGI unchanged and never raises state income tax before refundable credits. - Michigan is an intended exception: Form 4884 and MCL 206.30 reduce its - retirement and standard deduction limits by the railroad benefits that - federal AGI includes, and not by Social Security. +S1. Moving all of a filer's benefits from title II to tier 1, with or without + the rest of a railroad annuity, leaves federal AGI unchanged and never + raises state income tax before refundable credits, with two intended + exceptions. Form 4884 and MCL 206.30 reduce Michigan's retirement and + standard deduction limits by the railroad benefits that federal AGI + includes, and not by Social Security. Minnesota's elderly or disabled + subtraction (Minn. Stat. 290.0802) reduces its base by "pension, annuity, + or disability benefits included in federal gross income but not subject to + state taxation" in full (subd. 1(c)) and takes them out of the adjusted + gross income it halves (subd. 1(a)), so taxable tier 1 benefits shrink the + subtraction where taxable title II benefits would not; S1 lets Minnesota's + tax rise by its top rate times the subtraction lost. S2. In the states that tax no filer's Social Security in 2025, the swap leaves - state income tax unchanged. Michigan is excepted as in S1. Idaho's grocery - credit is excepted after refundable credits: the model's SNAP income lists - no railroad benefits, so the swap can make a household SNAP eligible, and - Idaho denies the credit for SNAP months. Before refundable credits, Idaho - stays neutral. + state income tax unchanged. Michigan is excepted as in S1. After + refundable credits, Idaho is a model gap, not an exception: 7 CFR + 273.9(b)(2)(ii) counts railroad retirement benefits as SNAP unearned + income, the model's SNAP income list leaves them out + (PolicyEngine/policyengine-us#10551), so the swap can make a household + SNAP eligible, and Idaho denies its grocery credit for SNAP months. The + test skips that check until the SNAP list counts railroad benefits. Before + refundable credits, Idaho stays neutral. S3. Each state income test in INCOME_TESTS counts tier 1 like title II: the - swap leaves the test's income unchanged, for any wages and pensions. + swap leaves the test's income unchanged, for any wages, pensions and rest + of a railroad annuity. S4. Each pension deduction in PENSION_CAPS shrinks equally for title II and tier 1, because its law reduces the cap by both: the swap leaves the - deduction unchanged. Benefits can only reduce the deduction, and do reduce - it when the pension exceeds the cap. + deduction unchanged, with or without the rest of a railroad annuity. + Benefits can only reduce the deduction, and do reduce it when the pension + exceeds the cap and no other railroad benefits use the cap up. S5. Minnesota's Social Security subtraction (Minn. Stat. 290.0132, subd. 26) stays between zero and the taxable title II benefits. Adding the taxable tier 1 benefits that the model subtracts with the rest of the railroad annuity, as Schedule M1M line 17 does, total relief reaches at least what the same benefits would get as title II. +S6. Adding railroad benefits (tier 1, and the taxable and tax-free rest of the + annuity) never lowers a state's base: state AGI, or state taxable income + where the reported AGI includes the benefits. A state that subtracted the + benefits twice would fail. The households have no title II benefits: the + model splits the section 86 amount between title II and tier 1 in + proportion to the benefits, so as tier 1 grows, part of the taxable + amount moves off title II, which some states tax. They have no Part B + premium either: the benefits can end a Medicare Savings Program's payment + of it, and Idaho and Ohio subtract premiums the filer pays. Oregon + subtracts federal income tax inside its AGI (ORS 316.680(1)(b)), which + the benefits raise, so it is skipped, and Colorado and South Carolina are + skipped while the standard deduction absorbs part of the benefits, as in + I4 of test_railroad_retirement_taxation.py. Each Social Security + subtraction in SOCIAL_SECURITY_SUBTRACTIONS stays at or below the taxable + title II benefits, so with the taxable railroad benefits subtracted once, + the two never exceed what federal AGI includes from both. D1. Differential: walking the 2025 Schedule M1M line 12 worksheet step by step, with its published amounts and Form 1040 lines taken from the model, gives - the model's subtraction for every filing status. + the model's subtraction for every filing status. At steps 12 and 13 the + walk adds half of line 6b, the taxable benefits, as Minn. Stat. 290.0132, + subd. 26(i) defines provisional income, where the worksheet takes half of + line 6a. The model estimates state income tax paid from withholding, and its state_withholding_agi counts title II benefits but not railroad benefits. That @@ -44,12 +75,16 @@ import math -from hypothesis import given, settings +from hypothesis import example, given, settings from hypothesis import strategies as st from policyengine_us import Simulation +from policyengine_us.system import system from policyengine_us.tests.test_railroad_retirement_taxation import ( + FEDERAL_AGI_STATES, + FEDERAL_TAXABLE_INCOME_STATES, INCOME_TAX_STATES, + STATE_BASE_EXCEPTIONS, calc, situation, tolerance, @@ -58,6 +93,20 @@ YEAR = 2025 TITLE_II = "social_security_retirement" TIER_1 = "railroad_social_security_equivalent_benefits" +TIER_2 = "taxable_railroad_pension_benefits" +TAX_FREE = "tax_exempt_railroad_pension_benefits" +PARAMETERS = system.parameters(f"{YEAR}-01-01") + +# The Idaho skip in S2 ends once the SNAP income list counts railroad benefits. +SNAP_COUNTS_RAILROAD = "railroad_benefits" in ( + PARAMETERS.gov.usda.snap.income.sources.unearned +) + +MN_RATES = PARAMETERS.gov.states.mn.tax.income.rates +MN_TOP_RATE = max( + getattr(MN_RATES, status).rates[-1] + for status in ("single", "joint", "head_of_household", "separate") +) # States whose tax in 2025 can reach title II benefits: their own Social # Security worksheets and limits or, in Missouri, retirement benefits of @@ -109,6 +158,23 @@ # Exceeds every 2025 cap in PENSION_CAPS, joint filers' included. BINDING_PENSION = 100_000 +# Each subtracts Social Security benefits that federal AGI includes. The model +# subtracts the taxable tier 1 benefits with the rest of the railroad annuity +# (taxable_railroad_benefits, 45 U.S.C. 231m(a)), so none of these may reach +# them. +SOCIAL_SECURITY_SUBTRACTIONS = [ + ("CO", "co_social_security_subtraction_indv"), + ("CT", "ct_social_security_benefit_adjustment"), + ("MD", "md_socsec_subtraction_amount"), + ("MN", "mn_social_security_subtraction"), + ("NE", "ne_social_security_subtraction"), + ("NM", "nm_social_security_income_exemption"), + ("RI", "ri_social_security_modification"), + ("WV", "wv_social_security_benefits_subtraction"), +] + +S6_STATES = [state for state in INCOME_TAX_STATES if state != "OR"] + # Schedule M1M worksheet for line 12, 2025: the steps 2, 4, 20 and 23 amounts. M1M_2025 = { "SINGLE": (84_490, 4_000, 69_250, 4_560), @@ -123,6 +189,9 @@ pensions = st.integers(min_value=0, max_value=60_000) benefits = st.integers(min_value=1, max_value=60_000) amounts = st.integers(min_value=0, max_value=60_000) +# The rest of a railroad annuity, often absent. +tier_2s = st.one_of(st.just(0), st.integers(min_value=1, max_value=40_000)) +tax_frees = st.one_of(st.just(0), st.integers(min_value=1, max_value=5_000)) def couple(head, joint, age): @@ -130,7 +199,7 @@ def couple(head, joint, age): return [head, {"age": age, "role": "spouse"}] if joint else [head] -def swap_simulation(states, age, earnings, pension, amount, joint): +def swap_simulation(states, age, earnings, pension, amount, joint, tier_2, tax_free): """Build one Simulation with a title II and a tier 1 household per state.""" households = [ ( @@ -140,6 +209,8 @@ def swap_simulation(states, age, earnings, pension, amount, joint): "age": age, "employment_income": earnings, "taxable_private_pension_income": pension, + TIER_2: tier_2, + TAX_FREE: tax_free, label: amount, }, joint, @@ -164,36 +235,66 @@ def by_tax_unit(simulation, variable, year): @settings(max_examples=8, deadline=None) -@given(age=ages, earnings=wages, pension=pensions, amount=benefits, joint=st.booleans()) -def test_s1_s2_swapping_title_ii_for_tier_1(age, earnings, pension, amount, joint): +@given( + age=ages, + earnings=wages, + pension=pensions, + amount=benefits, + joint=st.booleans(), + tier_2=tier_2s, + tax_free=tax_frees, +) +# Minnesota: $1,000 of the $2,000 is taxable, so the tier 1 household's +# Schedule M1R reduction is $9,750 (1,000 + 1,000 + half of 30,000 - 14,500) +# against $9,250, and its $9,600 base leaves no subtraction against $350. +@example( + age=72, earnings=0, pension=30_000, amount=2_000, joint=False, tier_2=0, tax_free=0 +) +def test_s1_s2_swapping_title_ii_for_tier_1( + age, earnings, pension, amount, joint, tier_2, tax_free +): simulation = swap_simulation( - INCOME_TAX_STATES, age, earnings, pension, amount, joint + INCOME_TAX_STATES, age, earnings, pension, amount, joint, tier_2, tax_free ) agi = calc(simulation, "adjusted_gross_income") before_refundable = calc(simulation, "state_income_tax_before_refundable_credits") after_refundable = calc(simulation, "state_income_tax") - allowed = tolerance(earnings + pension + amount) + elderly_subtraction = calc(simulation, "mn_elderly_disabled_subtraction") + allowed = tolerance(earnings + pension + amount + tier_2 + tax_free) for index, state in enumerate(INCOME_TAX_STATES): title_ii, tier_1 = 2 * index, 2 * index + 1 assert abs(agi[tier_1] - agi[title_ii]) <= allowed, state change = before_refundable[tier_1] - before_refundable[title_ii] - if state not in INTENDED_INCREASE: + if state == "MN": + lost = elderly_subtraction[title_ii] - elderly_subtraction[tier_1] + assert change <= allowed + MN_TOP_RATE * max(0, lost), (state, change) + elif state not in INTENDED_INCREASE: assert change <= allowed, (state, change) if state in NEUTRAL_STATES: assert abs(change) <= allowed, (state, change) - if state != "ID": + if state != "ID" or SNAP_COUNTS_RAILROAD: change = after_refundable[tier_1] - after_refundable[title_ii] assert abs(change) <= allowed, (state, change) @settings(max_examples=15, deadline=None) -@given(age=ages, earnings=wages, pension=pensions, amount=benefits, joint=st.booleans()) +@given( + age=ages, + earnings=wages, + pension=pensions, + amount=benefits, + joint=st.booleans(), + tier_2=tier_2s, + tax_free=tax_frees, +) def test_s3_income_tests_count_tier_1_like_title_ii( - age, earnings, pension, amount, joint + age, earnings, pension, amount, joint, tier_2, tax_free ): states = [state for state, _ in INCOME_TESTS] - simulation = swap_simulation(states, age, earnings, pension, amount, joint) - allowed = tolerance(earnings + pension + amount) + simulation = swap_simulation( + states, age, earnings, pension, amount, joint, tier_2, tax_free + ) + allowed = tolerance(earnings + pension + amount + tier_2 + tax_free) for index, (state, variable) in enumerate(INCOME_TESTS): income = by_household(simulation, variable) title_ii, tier_1 = income[2 * index], income[2 * index + 1] @@ -208,13 +309,18 @@ def test_s3_income_tests_count_tier_1_like_title_ii( ), amount=benefits, joint=st.booleans(), + tier_2=tier_2s, + tax_free=tax_frees, ) -def test_s4_pension_caps_shrink_alike(age, pension, amount, joint): +def test_s4_pension_caps_shrink_alike(age, pension, amount, joint, tier_2, tax_free): + annuity = {TIER_2: tier_2, TAX_FREE: tax_free} households = [ ( state, couple( - {"age": age, "taxable_public_pension_income": pension} | benefit, + {"age": age, "taxable_public_pension_income": pension} + | annuity + | benefit, joint, age, ), @@ -223,13 +329,13 @@ def test_s4_pension_caps_shrink_alike(age, pension, amount, joint): for benefit in ({TITLE_II: amount}, {TIER_1: amount}, {}) ] simulation = Simulation(situation=situation(households)) - allowed = tolerance(pension + amount) + allowed = tolerance(pension + amount + tier_2 + tax_free) for index, (state, variable) in enumerate(PENSION_CAPS): deduction = by_household(simulation, variable) title_ii, tier_1, neither = deduction[3 * index : 3 * index + 3] assert abs(tier_1 - title_ii) <= allowed, (state, title_ii, tier_1) assert tier_1 <= neither + allowed, (state, tier_1, neither) - if pension == BINDING_PENSION: + if pension == BINDING_PENSION and tier_2 + tax_free == 0: assert tier_1 < neither - allowed, (state, tier_1, neither) @@ -274,15 +380,118 @@ def test_s5_minnesota_subtraction_bounds( assert subtraction[0] + taxable_tier_1[0] >= subtraction[1] - allowed +@settings(max_examples=8, deadline=None) +@given( + age=ages, + earnings=wages, + pension=pensions, + tier_1=amounts, + tier_2=tier_2s, + tax_free=tax_frees, + joint=st.booleans(), +) +# $22,350 of the tier 1 benefits are taxable; a Connecticut or Rhode Island +# Social Security worksheet that took them from Form 1040 line 6b would +# subtract them a second time. +@example( + age=70, earnings=0, pension=40_000, tier_1=30_000, tier_2=0, tax_free=0, joint=False +) +def test_s6_railroad_benefits_never_lower_state_bases( + age, earnings, pension, tier_1, tier_2, tax_free, joint +): + head = { + "age": age, + "employment_income": earnings, + "taxable_private_pension_income": pension, + "medicare_part_b_enrolled": False, + } + railroad = {TIER_1: tier_1, TIER_2: tier_2, TAX_FREE: tax_free} + spouse = {"age": age, "role": "spouse", "medicare_part_b_enrolled": False} + households = [ + (state, [member] + ([spouse] if joint else [])) + for state in S6_STATES + for member in (head, head | railroad) + ] + built = situation(households) + for unit in built["tax_units"].values(): + unit["state_and_local_sales_or_income_tax"] = {YEAR: 0} + simulation = Simulation(situation=built) + agi = calc(simulation, "taxsim_state_agi") + taxable_income = calc(simulation, "taxsim_state_taxable_income") + federal_taxable_income = calc(simulation, "taxable_income") + allowed = tolerance(earnings + pension + tier_1 + tier_2 + tax_free) + for index, state in enumerate(S6_STATES): + without, with_railroad = 2 * index, 2 * index + 1 + floored = federal_taxable_income[without] <= 0 + if state in FEDERAL_TAXABLE_INCOME_STATES and floored: + continue + base = taxable_income if state in FEDERAL_AGI_STATES else agi + before, after = base[without], base[with_railroad] + assert after >= before - allowed, (state, before, after) + + +@settings(max_examples=25, deadline=None) +@given( + age=ages, + earnings=wages, + pension=pensions, + title_ii=benefits, + tier_1=benefits, + tier_2=tier_2s, + tax_free=tax_frees, + joint=st.booleans(), +) +@example( + age=70, + earnings=0, + pension=40_000, + title_ii=10_000, + tier_1=30_000, + tier_2=0, + tax_free=0, + joint=False, +) +def test_s6_social_security_subtractions_stay_within_taxable_title_ii( + age, earnings, pension, title_ii, tier_1, tier_2, tax_free, joint +): + head = { + "age": age, + "employment_income": earnings, + "taxable_private_pension_income": pension, + TITLE_II: title_ii, + TIER_1: tier_1, + TIER_2: tier_2, + TAX_FREE: tax_free, + } + households = [ + (state, couple(head, joint, age)) for state, _ in SOCIAL_SECURITY_SUBTRACTIONS + ] + simulation = Simulation(situation=situation(households)) + taxable_title_ii = calc(simulation, "tax_unit_taxable_social_security") + allowed = tolerance(earnings + pension + title_ii + tier_1 + tier_2 + tax_free) + for index, (state, variable) in enumerate(SOCIAL_SECURITY_SUBTRACTIONS): + subtraction = by_tax_unit(simulation, variable, YEAR)[index] + assert subtraction >= -allowed, (state, variable, subtraction) + assert subtraction <= taxable_title_ii[index] + allowed, ( + state, + variable, + subtraction, + taxable_title_ii[index], + ) + + def schedule_m1m_line_12_2025( - status, agi, line_9, line_6a, line_6b, line_2a, schedule_1_line_26, tier_1 + status, agi, line_9, line_6b, line_2a, schedule_1_line_26, tier_1 ): """Walk the 2025 worksheet for Schedule M1M line 12, one filer at a time. - Tier 1 benefits reach line 17 in the model, so this returns line 12 less - any tier 1 benefits. The worksheet's steps 26 to 29 do that above the - threshold. At or below it line 12 takes all of line 6b, and line 17 then - leaves those benefits out. Schedule 1 line 21 is zero for these filers. + Steps 12 and 13 add half of Form 1040 line 6a, all benefits received; + Minn. Stat. 290.0132, subd. 26(i) adds "one-half of the taxable Social + Security benefits", so the walk takes half of line 6b there. Tier 1 + benefits reach line 17 in the model, so this returns line 12 less any tier + 1 benefits. The worksheet's steps 26 to 29 do that above the threshold. At + or below it line 12 takes all of line 6b, and line 17 then leaves those + benefits out. Schedule 1 line 21 is zero for these filers. """ threshold, increment, income_amount, maximum = M1M_2025[status] step_27 = max(line_6b - tier_1, 0) @@ -292,7 +501,7 @@ def schedule_m1m_line_12_2025( step_7 = step_4 * 0.1 * line_6b step_8 = max(line_6b - step_7, 0) step_11 = line_9 - line_6b - step_15 = step_11 + line_6a * 0.5 + line_2a + step_15 = step_11 + line_6b * 0.5 + line_2a step_19 = max(step_15 - max(schedule_1_line_26, 0), 0) step_21 = max(step_19 - income_amount, 0) step_24 = max(maximum - step_21 * 0.2, 0) @@ -337,7 +546,6 @@ def form_line(variable): status, agi=form_line("adjusted_gross_income"), line_9=form_line("irs_gross_income"), - line_6a=form_line("tax_unit_section_86_benefits"), line_6b=taxable_title_ii + taxable_tier_1, line_2a=exempt_interest, schedule_1_line_26=form_line("above_the_line_deductions"), diff --git a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py index f69569cf608..7e27e5748aa 100644 --- a/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py +++ b/policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py @@ -60,15 +60,15 @@ def formula(tax_unit, period, parameters): simplified_subtraction = 0 # Alternate subtraction, subd. 26(e) to (h), on provisional income # (steps 9-19). Step 11 starts from total income less all taxable - # benefits, and step 12 adds half of Form 1040 line 6a: the head's and - # spouse's title II and tier 1 benefits. A dependent's benefits go on - # the dependent's own return. + # benefits. Subd. 26(i) defines provisional income with "one-half of + # the taxable Social Security benefits", title II and tier 1 alike, so + # the model adds half of Form 1040 line 6b in place of steps 12 and + # 13, which take half of line 6a. income_amount = p.income_amount[filing_status] alt_amount = p.alternative_amount[filing_status] us_gross_income = add(tax_unit, period, ["irs_gross_income"]) adj_income = us_gross_income - taxable_benefits - section_86_benefits = tax_unit("tax_unit_section_86_benefits", period) - benefit_amount = section_86_benefits * p.total_benefit_fraction + benefit_amount = taxable_benefits * p.total_benefit_fraction tax_exempt_int = add(tax_unit, period, ["tax_exempt_interest_income"]) sum_income = adj_income + benefit_amount + tax_exempt_int us_ald = tax_unit("above_the_line_deductions", period) diff --git a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py index c965954def7..6fd80c2b978 100644 --- a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py +++ b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py @@ -9,7 +9,7 @@ class mn_homestead_credit_refund_household_income(Variable): definition_period = YEAR documentation = ( "Approximates Form M1PR lines 1 through 13 using modeled adjusted gross " - "income, nontaxable Social Security and tier 1 railroad retirement " + "income, nontaxable Social Security and railroad retirement " "benefits, retirement add-backs and " "contributions, and age/disability and dependent subtractions. " "Co-occupant income and other nontaxable additions or subtractions " @@ -54,6 +54,15 @@ def formula(tax_unit, period, parameters): ) * ~is_dependent ) + # Subd. 3(a)(2)(v) counts a pension or annuity, railroad retirement + # benefits included, "which was not exclusively funded by the claimant + # or spouse". Railroad employers and employees both pay tier 2 tax (26 + # U.S.C. 3201(b), 3221(b)), so the tax-free rest of the annuity counts + # and subd. 3(b)(2), for annuities the claimant alone funded, doesn't + # reach it. + tax_free_railroad_annuity = tax_unit.sum( + people("tax_exempt_railroad_pension_benefits", period) * ~is_dependent + ) # Line 1 is the filer's federal AGI, which leaves out tax unit # dependents, so the retirement add-backs, contributions and @@ -114,7 +123,11 @@ def formula(tax_unit, period, parameters): income_p.exemptions.amount * dependent_subtraction_multiplier ) - additions = nontaxable_social_security + retirement_additions + additions = ( + nontaxable_social_security + + tax_free_railroad_annuity + + retirement_additions + ) subtractions = ( age_or_disability_subtraction + dependent_subtraction diff --git a/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py b/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py index becc13dfb01..4f22e77a17f 100644 --- a/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py +++ b/policyengine_us/variables/household/income/person/retirement/railroad_benefits.py @@ -19,8 +19,9 @@ class railroad_benefits(Variable): "government transfer, and household and SPM benefits include these " "benefits. Enter the parts, not this total, so that taxes see them. " "A total entered here replaces the sum of the parts and reaches " - "income totals and benefit programs but no tax, and it applies only " - "to the year it is entered for, while the parts carry into later " + "income totals, benefit programs and the few state tax provisions " + "that read this total, but no federal tax, and it applies only to " + "the year it is entered for, while the parts carry into later " "years. Do not also enter them as social_security, pension_income, " "disability_benefits or survivor_benefits." ) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 106f71d4b0b..bc9924813c5 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -5514,6 +5514,39 @@ candidates: state: ruled note: Ruled for the statute (statute first). The section 22(c)(3) amounts follow the reading in us:elderly-credit-railroad-tier-2-return-of-cost. Schedules M1R before 2025 were not compared. +- id: us-mn:homestead-credit-refund-railroad-annuity-tax-free-part + program: property-tax-credit + years: + from: 2025 + type: tax + question: Does homestead credit refund household income count the part of a railroad retirement annuity that returns the + employee's tier 2 tax tax free? + sources: + - kind: statute + citation: Minn. Stat. 290A.03, subd. 3(a)(2)(v) and (b)(2); 26 U.S.C. 3201(b) and 3221(b) + url: https://www.revisor.mn.gov/statutes/cite/290A.03#stat.290A.03.3 + - kind: instructions + citation: 2025 Form M1PR instructions, line 6 (additional nontaxable income), "Do not include" + url: https://www.taxformfinder.org/forms/2025/2025-minnesota-form-m1pr-instructions.pdf#page=8 + readings: + statute: Subd. 3(a)(2)(v) counts "any pension or annuity (including railroad retirement benefits, ...), which was not + exclusively funded by the claimant or spouse". Railroad employers pay tier 2 tax under 26 U.S.C. 3221(b) as + employees do under 3201(b), so the annuity counts in full, and subd. 3(b)(2) excludes only annuities the claimant or + spouse funded alone. + other: The line 6 instructions list "Annuity payments that are a return of after-tax contributions" under "Do not + include", which leaves out the part of the annuity recovered tax free. + example: A claimant aged 70 with $20,000 of federal AGI, all of it taxable tier 2 benefits, and $5,000 of tier 2 benefits + recovered tax free has household income of $19,800 under the statute ($20,000 + $5,000 - $5,200) and $14,800 under + the instructions. PE-US gives $19,800. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). The 2024 instructions were not compared. The model adds no other nontaxable + pension income to household income. - id: us-mn:k12-subtraction-grade-cap-default program: income-tax years: @@ -5699,7 +5732,7 @@ candidates: - id: us-mn:social-security-subtraction-provisional-income-benefits program: income-tax years: - from: 2022 + from: 2018 type: tax question: Does provisional income for the alternate Social Security subtraction add half of the taxable benefits, as Minn. Stat. 290.0132 subd. 26(i) provides, or half of all benefits received, as the line 12 worksheet does? @@ -5708,26 +5741,30 @@ candidates: citation: Minn. Stat. 290.0132, subd. 26(i); Laws 2019, 1st Spec. Sess., ch. 6, art. 2, sec. 12 url: https://www.revisor.mn.gov/statutes/cite/290.0132#stat.290.0132.26 - kind: worksheet - citation: 2025 Schedule M1M instructions, worksheet for line 12, steps 12 and 13 (2022, steps 4 and 5) + citation: 2025 Schedule M1M instructions, worksheet for line 12, steps 12 and 13 (2023 and 2024, the same steps; 2021 + and 2022, steps 4 and 5; 2019 and 2020, worksheet for line 39, steps 4 and 5; 2018, worksheet for line 37, steps 8 + and 9) url: https://www.revenue.state.mn.us/sites/default/files/2026-07/m1m-25.pdf#page=4 readings: statute: Provisional income is section 86(b)(2) modified adjusted gross income "plus one-half of the taxable Social Security benefits received during the taxable year". The 2019 act inserted "taxable", effective for taxable years beginning after December 31, 2017. - other: Steps 12 and 13 add half of Form 1040 line 6a, all benefits received. + other: Steps 12 and 13 add half of Form 1040 line 6a, all benefits received (line 5a before 2020). example: A couple filing jointly in 2025 with $150,000 of gross income and AGI and $100,000 of benefits, $85,000 of them taxable, has provisional income of $107,500 under the statute and an alternate subtraction of $2,066, against - $115,000 and $566 under the worksheet. PE-US gives $566. + $115,000 and $566 under the worksheet. PE-US gives $2,066. pe_us: - follows: conflicting-source + follows: statute code: - policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_social_security_subtraction.py - policyengine_us/parameters/gov/states/mn/tax/income/subtractions/social_security/total_benefit_fraction.yaml - prs: [] - state: unreviewed - note: Found while auditing the railroad benefit changes in PR 10722, which keeps the worksheet's base. Either reading - counts tier 1 benefits with title II benefits, since subd. 26(i) gives "Social Security benefits" the IRC 86(d)(1) - meaning. + - policyengine_us/reforms/states/mn/walz/mn_walz_hf1938.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10722 + state: ruled + note: Ruled for the statute (statute first). The 2019 act inserted "taxable" in both the subtraction and the definition + of provisional income, and the worksheets for 2018 to 2025 kept half of all benefits. Either reading counts tier 1 + benefits with title II benefits, since subd. 26(i) gives "Social Security benefits" the IRC 86(d)(1) meaning. - id: us-mn:social-security-subtraction-railroad-tier-1-order program: income-tax years: @@ -9570,42 +9607,6 @@ candidates: state: ruled note: The code already uses a strict comparison and the eligibility tests pin AGI equal to the threshold as ineligible. Verify the statute's and the worksheet's wording before registering. -- id: us-ri:social-security-modification-railroad-tier-1 - program: income-tax - years: - from: 2021 - type: tax - question: The Social Security modification and the railroad retirement modification both reach the taxable tier 1 - railroad retirement benefits. Which one takes them? - sources: - - kind: statute - citation: R.I. Gen. Laws 44-30-12(c)(1) and (c)(8); 45 U.S.C. 231m(a) - url: https://webserver.rilegislature.gov/Statutes/TITLE44/44-30/44-II/44-30-12.htm - - kind: worksheet - citation: 2024 Modification for Taxable Social Security Income Worksheet, lines 8 to 13 - url: https://tax.ri.gov/sites/g/files/xkgbur541/files/2024-09/Social%20Security%20Worksheet_bd.pdf#page=1 - - kind: instructions - citation: 2025 RI-1040 instructions, Schedule M lines 1d and 1s - url: https://tax.ri.gov/sites/g/files/xkgbur541/files/2025-12/2025%201040R%20Instructions%20122025.pdf#page=9 - readings: - statute: Subsection (c)(8) subtracts "the social security benefits includible in federal adjusted gross income", an - IRC 86 amount that counts tier 1 benefits, and subsection (c)(1) subtracts income federal law exempts, which takes - in the same benefits. Neither says which modification takes them. - other: Worksheet line 11 takes the taxable benefits on Form 1040 line 6b, tier 1 included, and line 1d subtracts the - railroad retirement benefits in federal gross income, so the forms subtract the taxable tier 1 benefits twice. - example: A single filer aged 70 with $10,000 of title II and $10,000 of tier 1 benefits ($9,600 of them taxable, half of - each) and AGI of $39,600. Taking the tier 1 benefits on line 1d, line 1d is $4,800 and line 1s is $4,800; taking them - on line 1s, line 1s is $9,600 and line 1d is $0. On the forms, line 1d is $4,800 and line 1s is $9,600. PE-US takes - them on line 1d. - pe_us: - follows: gap-fill - code: - - policyengine_us/variables/gov/states/ri/tax/income/agi/subtractions/social_security/ri_social_security_modification.py - prs: - - https://github.com/PolicyEngine/policyengine-us/pull/10722 - state: ruled - note: Ruled for line 1d (taxpayer choices minimize tax). Line 1d has no age or income test, so it takes the tier 1 - benefits whenever line 1s would, and also when the filer fails line 1s's tests. - id: us-ri:ssp-category-d-without-federal-ssi program: ssi-state-supplement years: @@ -11022,6 +11023,7 @@ candidates: program: income-tax years: from: 2021 + to: 2024 type: tax question: Does Utah's Social Security benefits credit apply to the taxable tier 1 railroad retirement benefits on Form 1040 line 6b? @@ -11051,7 +11053,9 @@ candidates: - https://github.com/PolicyEngine/policyengine-us/pull/10722 state: ruled note: Ruled for the statute (statute first). Before 2025 subsection (2)(b) read "included in adjusted gross income on the - claimant's federal income tax return", which reaches the same benefits through subsection (1)(f). + claimant's federal income tax return", which reaches the same benefits through subsection (1)(f). From 2025 the + worksheet's line 2 takes back out "any Social Security benefit included in" code 77 or code 78 income, so the 2025 + worksheet agrees with the statute. - id: us-va:ag-entry-month-spouse-at-home program: ssi-state-supplement years: From 8746a743c757be4471eab13c05869790714eac2f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 9 Nov 2026 14:37:12 -0500 Subject: [PATCH 08/11] Hold SSI out of the railroad swap households The first property hunt found one counterexample in S3: Oklahoma's supplement rounds "the monthly amount of each income source" to the nearest dollar (OAC 317:35-5-42(i)), and the swap turns two payments into one, so ok_gross_income moved by $12. The swap households now take up no SSI, and S3 pins the case as an explicit example. Co-Authored-By: Claude Opus 5.5 --- .../test_state_railroad_benefit_provisions.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/policyengine_us/tests/test_state_railroad_benefit_provisions.py b/policyengine_us/tests/test_state_railroad_benefit_provisions.py index 1045e00f027..64b678e824c 100644 --- a/policyengine_us/tests/test_state_railroad_benefit_provisions.py +++ b/policyengine_us/tests/test_state_railroad_benefit_provisions.py @@ -69,6 +69,14 @@ do with how a state treats the benefits, so S1 and S2 set its input to zero for both households. +The swap households take up no SSI. Oklahoma's supplement rounds "the monthly +amount of each income source" to the nearest dollar (OAC 317:35-5-42(i)), and +the swap turns two payments, title II and the rest of a railroad annuity, into +one railroad payment. The supplement, which Oklahoma's gross income and sales +tax credit count, can then move by a dollar a month: $26.50 and $416.50 round to +$444, and $443 stays $443. That rounding has nothing to do with how a state +treats the benefits. + YAML tests pin worked examples; these properties quantify over amounts, filing status and every state, which YAML cannot. """ @@ -223,6 +231,8 @@ def swap_simulation(states, age, earnings, pension, amount, joint, tier_2, tax_f built = situation(households) for unit in built["tax_units"].values(): unit["state_and_local_sales_or_income_tax"] = {YEAR: 0} + for person in built["people"].values(): + person["takes_up_ssi_if_eligible"] = {YEAR: False} return Simulation(situation=built) @@ -287,6 +297,11 @@ def test_s1_s2_swapping_title_ii_for_tier_1( tier_2=tier_2s, tax_free=tax_frees, ) +# Oklahoma: with SSI taken up, the supplement's rounding of each monthly source +# moved ok_gross_income by $12 (title II $12,312, tier 1 $12,324). +@example( + age=67, earnings=0, pension=0, amount=318, joint=False, tier_2=0, tax_free=4_998 +) def test_s3_income_tests_count_tier_1_like_title_ii( age, earnings, pension, amount, joint, tier_2, tax_free ): From b86e464fd0f106f49ea961b4f7287ff9cda5a21c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 01:20:10 -0500 Subject: [PATCH 09/11] Drop the marriage credit from the changelog and widen the SNAP skip check The changelog named Minnesota's marriage credit and Minn. Stat. 290.0675, but this branch changes no marriage credit code: Minnesota subtracts taxable tier 1 under 45 U.S.C. 231m(a), so the credit's earned income measure, which counts benefits "to the extent included in Minnesota taxable income", leaves them out. The Idaho skip in S2 now ends when the SNAP unearned income list counts all three railroad parts, not only when it counts the railroad_benefits total. Co-Authored-By: Claude Opus 5.5 --- changelog.d/state-sseb-worksheets.fixed.md | 2 +- .../tests/test_state_railroad_benefit_provisions.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/changelog.d/state-sseb-worksheets.fixed.md b/changelog.d/state-sseb-worksheets.fixed.md index 38e7d8a3802..fa37e037777 100644 --- a/changelog.d/state-sseb-worksheets.fixed.md +++ b/changelog.d/state-sseb-worksheets.fixed.md @@ -1 +1 @@ -Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction, marriage credit and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26, 290.0675 and 290A.03 define them, without subtracting them twice. Add half of the taxable benefits, not half of all benefits, to provisional income for Minnesota's alternate Social Security subtraction, as subd. 26(i) provides, in the baseline and in the HF1938 repeal reform. Count the part of a railroad annuity recovered tax free in Minnesota's homestead credit refund income, as 290A.03 subd. 3(a)(2)(v) counts annuities the claimant did not fund alone. Leave dependents' own Social Security benefits out of Minnesota's elderly and disabled subtraction, which reads the head's and spouse's benefits, as section 22(c)(3) does. Count tier 1 benefits in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit, North Dakota's renter's refund, Missouri's and Rhode Island's property tax credits, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior property tax exemption and New Mexico's modified gross income. Count excluded tier 1 railroad benefits as excluded Social Security benefits in Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess property tax credit. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. +Count tier 1 railroad retirement benefits as Social Security benefits in Minnesota's Social Security subtraction and homestead credit refund income, as Minn. Stat. 290.0132 subd. 26 and 290A.03 define them, without subtracting them twice. Add half of the taxable benefits, not half of all benefits, to provisional income for Minnesota's alternate Social Security subtraction, as subd. 26(i) provides, in the baseline and in the HF1938 repeal reform. Count the part of a railroad annuity recovered tax free in Minnesota's homestead credit refund income, as 290A.03 subd. 3(a)(2)(v) counts annuities the claimant did not fund alone. Leave dependents' own Social Security benefits out of Minnesota's elderly and disabled subtraction, which reads the head's and spouse's benefits, as section 22(c)(3) does. Count tier 1 benefits in lines A and B of Connecticut's Social Security benefit adjustment worksheet, as Conn. Gen. Stat. 12-701(a)(20)(B)(x) does through IRC 86(d)(1), and take line D from the taxable title II benefits first. On Iowa's 2021 and 2022 reportable Social Security worksheet, add half of the head's and spouse's Social Security and tier 1 benefits to line 7, keep the student loan interest deduction out of line 8 and leave dependents' benefits out of line 1. Reduce the pension caps in Maine, Maryland and Idaho by railroad retirement benefits, and Michigan's seven retirement and standard deduction limits by taxable railroad retirement benefits, as their statutes and forms do. Count railroad retirement benefits in Montana's elderly homeowner or renter credit, New York's real property tax credit (Form IT-214 line 13), Oklahoma's sales tax and property tax credits, Massachusetts' senior circuit breaker credit, North Dakota's renter's refund, Missouri's and Rhode Island's property tax credits, Maine's fairness credits, Wisconsin's homestead credit, Washington's senior property tax exemption and New Mexico's modified gross income. Count excluded tier 1 railroad benefits as excluded Social Security benefits in Vermont's renter credit, Colorado's sales tax refund and West Virginia's homestead excess property tax credit. Take taxable and total benefits from both title II and tier 1 in the District of Columbia's disability exclusion, Massachusetts' 2021 unemployment compensation deduction and Wisconsin's unemployment compensation subtraction. diff --git a/policyengine_us/tests/test_state_railroad_benefit_provisions.py b/policyengine_us/tests/test_state_railroad_benefit_provisions.py index 64b678e824c..96af58f89b9 100644 --- a/policyengine_us/tests/test_state_railroad_benefit_provisions.py +++ b/policyengine_us/tests/test_state_railroad_benefit_provisions.py @@ -105,9 +105,11 @@ TAX_FREE = "tax_exempt_railroad_pension_benefits" PARAMETERS = system.parameters(f"{YEAR}-01-01") -# The Idaho skip in S2 ends once the SNAP income list counts railroad benefits. -SNAP_COUNTS_RAILROAD = "railroad_benefits" in ( - PARAMETERS.gov.usda.snap.income.sources.unearned +# The Idaho skip in S2 ends once the SNAP income list counts railroad benefits, +# as the total or as all three parts. +SNAP_UNEARNED = set(PARAMETERS.gov.usda.snap.income.sources.unearned) +SNAP_COUNTS_RAILROAD = ( + "railroad_benefits" in SNAP_UNEARNED or {TIER_1, TIER_2, TAX_FREE} <= SNAP_UNEARNED ) MN_RATES = PARAMETERS.gov.states.mn.tax.income.rates From 1bc11678f7821639fc6de9881c9fa2535a13b807 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 11:44:56 -0500 Subject: [PATCH 10/11] Leave the tax-free railroad annuity part out of Montana's household income MCA 15-30-2337(9)(b) reduces income "by the taxpayer's basis", and the 2025 Schedule 2EC line 5 tells filers to leave "your cost or net investment" out of pensions and annuities. The tax-free part of a railroad annuity recovers the employee's tier 2 taxes, which IRC 72(r)(2) treats as employee contributions, so it is basis. Montana still counts the untaxed tier 1 benefits in full under (9)(a)(i). The 2025 case falls from 27,000 to 26,000. Co-Authored-By: Claude Opus 5.5 --- .../gross_income_sources.yaml | 6 ------ ...eowner_or_renter_credit_gross_household_income.yaml | 9 +++++---- ...omeowner_or_renter_credit_gross_household_income.py | 10 ++++++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml b/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml index db441e618ab..f28c95c8377 100644 --- a/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/gross_income_sources.yaml @@ -49,10 +49,6 @@ values: # AGI already includes taxable pension income; only the tax-exempt # portion is added back here per § 15-30-2337(9)(a)(i). - tax_exempt_pension_income - # § 15-30-2337(9)(a)(i) – Railroad Retirement Act benefits; the part of - # the annuity outside federal AGI. The formula adds the untaxed social - # security equivalent benefit. - - tax_exempt_railroad_pension_benefits - veterans_benefits # § 15-30-2337(9)(a)(i) – veterans' disability benefits - child_support_received # § 15-30-2337(9)(a)(iv) – support money - strike_benefits # § 15-30-2337(9)(a)(v) – nontaxable strike benefits @@ -99,7 +95,6 @@ values: - tax_exempt_interest_income - tax_exempt_ira_distributions - tax_exempt_pension_income - - tax_exempt_railroad_pension_benefits - veterans_benefits - child_support_received - strike_benefits @@ -123,7 +118,6 @@ values: - tax_exempt_interest_income - tax_exempt_ira_distributions - tax_exempt_pension_income - - tax_exempt_railroad_pension_benefits - veterans_benefits - child_support_received - strike_benefits diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml index 5750714e050..216fa48ffac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml @@ -837,10 +837,11 @@ # federal AGI is the 6,000 taxable railroad pension. adjusted_gross_income: 6_000 # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits" and - # (viii) all Social Security payments: AGI 6,000 + the 1,000 of the - # annuity outside AGI + 10,000 of title II + 10,000 of tier 1 social - # security equivalent benefits. - mt_elderly_homeowner_or_renter_credit_gross_household_income: 27_000 + # (viii) all Social Security payments: AGI 6,000 + 10,000 of title II + + # 10,000 of tier 1 social security equivalent benefits. The 1,000 of the + # annuity outside AGI recovers the employee's tier 2 taxes, the + # taxpayer's basis, which (9)(b) takes out of income. + mt_elderly_homeowner_or_renter_credit_gross_household_income: 26_000 - name: A dependent's tier 1 railroad retirement benefits count once, in full period: 2024 diff --git a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py index 44470318a7c..7dd59746a88 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py @@ -23,6 +23,9 @@ class mt_elderly_homeowner_or_renter_credit_gross_household_income(Variable): # The statutory federal-AGI definition remains after the 2024 form # began displaying separate income categories: HB191 § 1(9), p.2. "https://archive.legmt.gov/bills/2021/HB0199/HB0191_X.pdf#page=2", + # 2025 Schedule 2EC, line 5: pensions and annuities without "your + # cost or net investment" + "https://revenue.mt.gov/files/forms/Montana-Individual-Income-Tax-Return-Form-2-Instructions/2025_Montana_Individual_Income_Tax_Return_Form_2_Instructions.pdf#page=45", ) def formula(person, period, parameters): @@ -69,8 +72,11 @@ def formula(person, period, parameters): untaxed_social_security = max_(social_security - counted_social_security, 0) # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits", so # the tier 1 social security equivalent benefit counts in full in the - # same way. The rest of the annuity outside federal AGI is in the - # sources (tax_exempt_railroad_pension_benefits). + # same way. The tax-free rest of the annuity stays out: it recovers + # the employee's tier 2 taxes, which IRC 72(r)(2) treats as employee + # contributions, and § 15-30-2337(9)(b) reduces income "by the + # taxpayer's basis" (2025 Schedule 2EC line 5: "do not include ... + # your cost or net investment"). railroad_benefits = person( "railroad_social_security_equivalent_benefits", period ) From 8916627848753dd9fe414f7691cbc4332c22cf3d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 12:58:22 -0500 Subject: [PATCH 11/11] Answer the 1bc11678f7 review's wording items Rename the Montana case to say the employee's basis stays out, and say in the code comment that the tax-free part returns the employee's own contributions, which under IRC 72(r)(2) include tier 2 taxes. The Idaho skip in S2 ends once the SNAP list counts tier 1 benefits, alone or in the railroad_benefits total: the swap gives both households the same rest of the annuity, so only tier 1 separates them. Co-Authored-By: Claude Fable 5.2 --- ...omeowner_or_renter_credit_gross_household_income.yaml | 4 ++-- .../tests/test_state_railroad_benefit_provisions.py | 9 ++++----- ..._homeowner_or_renter_credit_gross_household_income.py | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml index 216fa48ffac..c458655e2b6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml @@ -813,7 +813,7 @@ # not the inconsistent 20_000 net: 30_000. mt_elderly_homeowner_or_renter_credit_gross_household_income: 30_000 -- name: Railroad Retirement Act benefits count in full in gross household income +- name: Railroad Retirement Act benefits count in gross household income, less the employee's basis period: 2025 input: people: @@ -839,7 +839,7 @@ # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits" and # (viii) all Social Security payments: AGI 6,000 + 10,000 of title II + # 10,000 of tier 1 social security equivalent benefits. The 1,000 of the - # annuity outside AGI recovers the employee's tier 2 taxes, the + # annuity outside AGI returns the employee's own contributions, the # taxpayer's basis, which (9)(b) takes out of income. mt_elderly_homeowner_or_renter_credit_gross_household_income: 26_000 diff --git a/policyengine_us/tests/test_state_railroad_benefit_provisions.py b/policyengine_us/tests/test_state_railroad_benefit_provisions.py index 96af58f89b9..3d5ac75a640 100644 --- a/policyengine_us/tests/test_state_railroad_benefit_provisions.py +++ b/policyengine_us/tests/test_state_railroad_benefit_provisions.py @@ -105,12 +105,11 @@ TAX_FREE = "tax_exempt_railroad_pension_benefits" PARAMETERS = system.parameters(f"{YEAR}-01-01") -# The Idaho skip in S2 ends once the SNAP income list counts railroad benefits, -# as the total or as all three parts. +# The Idaho skip in S2 ends once the SNAP income list counts tier 1 benefits, +# alone or in the railroad_benefits total. The swap gives both households the +# same rest of the annuity, so only tier 1 separates them. SNAP_UNEARNED = set(PARAMETERS.gov.usda.snap.income.sources.unearned) -SNAP_COUNTS_RAILROAD = ( - "railroad_benefits" in SNAP_UNEARNED or {TIER_1, TIER_2, TAX_FREE} <= SNAP_UNEARNED -) +SNAP_COUNTS_RAILROAD = bool({TIER_1, "railroad_benefits"} & SNAP_UNEARNED) MN_RATES = PARAMETERS.gov.states.mn.tax.income.rates MN_TOP_RATE = max( diff --git a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py index 7dd59746a88..7d850b7d1b9 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py @@ -72,9 +72,9 @@ def formula(person, period, parameters): untaxed_social_security = max_(social_security - counted_social_security, 0) # § 15-30-2337(9)(a)(i) counts "Railroad Retirement Act benefits", so # the tier 1 social security equivalent benefit counts in full in the - # same way. The tax-free rest of the annuity stays out: it recovers - # the employee's tier 2 taxes, which IRC 72(r)(2) treats as employee - # contributions, and § 15-30-2337(9)(b) reduces income "by the + # same way. The tax-free rest of the annuity stays out: it returns + # the employee's own contributions, which under IRC 72(r)(2) include + # tier 2 taxes, and § 15-30-2337(9)(b) reduces income "by the # taxpayer's basis" (2025 Schedule 2EC line 5: "do not include ... # your cost or net investment"). railroad_benefits = person(