Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/statute-bugs-savers-filing-2026.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Keep ABLE contributions in the saver's credit for 2026, reduce qualified contributions by relevant ABLE distributions through a new input, and from 2027 count only ABLE contributions, reduced only by those distributions, under a $2,100 cap (Pub. L. 119-21). From 2026, apply the 26 U.S.C. 6012(a)(1)(A) and (B) filing threshold to every filer, including a married individual filing separately with no gross income and a filer who can be claimed as a dependent. Under section 6012(a)(1), require a joint return when either spouse can be claimed as a dependent and the other spouse's own gross income is at least the exemption amount ((A)(iv)), and withdraw the (A) exception when the standard deduction is zero under section 63(c)(6) ((C)(ii)).
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
description: The IRS caps the qualifying contributions at the following amount under the saver's credit.
values:
2018-01-01: 2_000
2027-01-01: 2_100
metadata:
unit: currency-USD
period: year
label: Saver's Credit contributions cap
reference:
- title: 26 U.S. Code § 25B(a)
href: https://www.law.cornell.edu/uscode/text/26/25B
- title: Public Law 119-21, saver's credit contribution cap from 2027
href: https://www.govinfo.gov/content/pkg/PLAW-119publ21/pdf/PLAW-119publ21.pdf#page=97
- title: IRS | Retirement Savings Contributions Credit (Saver's Credit)
href: https://www.irs.gov/retirement-plans/plan-participant-employee/retirement-savings-contributions-savers-credit
- title: Publication 590-A
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
description: The IRS subtracts these distributions from qualified contributions under the saver's credit.
values:
# 25B(d)(2)(A) reaches any entity of a type that can take contributions
# under 25B(d)(1), so ABLE distributions count for as long as ABLE
# contributions do (Form 8880, line 4).
2018-01-01:
- retirement_distributions
- savers_credit_relevant_able_distributions
# From 2027 only ABLE contributions count, so only ABLE distributions
# reduce them.
2027-01-01:
- savers_credit_relevant_able_distributions
metadata:
unit: list
period: year
label: Saver's Credit distributions that reduce qualified contributions
reference:
- title: 26 U.S. Code § 25B(d)(2)
href: https://www.law.cornell.edu/uscode/text/26/25B#d_2
- title: Public Law 119-21, ABLE contributions in the saver's credit
href: https://www.govinfo.gov/content/pkg/PLAW-119publ21/pdf/PLAW-119publ21.pdf#page=97
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ values:
- self_employed_pension_contributions
- able_contributions_person

# Pub. L. 119-21 keeps ABLE contributions by the designated beneficiary
# after 2025.
2026-01-01:
- traditional_ira_contributions
- roth_ira_contributions
Expand All @@ -18,6 +20,10 @@ values:
- roth_401k_contributions
- roth_403b_contributions
- self_employed_pension_contributions
- able_contributions_person
# From 2027 the section 25B credit covers ABLE contributions only.
2027-01-01:
- able_contributions_person

metadata:
unit: list
Expand All @@ -26,6 +32,8 @@ metadata:
reference:
- title: 26 U.S. Code § 25B - (d)
href: https://www.law.cornell.edu/uscode/text/26/25B#d_1
- title: Public Law 119-21, ABLE contributions in the saver's credit
href: https://www.govinfo.gov/content/pkg/PLAW-119publ21/pdf/PLAW-119publ21.pdf#page=97
- title: 2023 Publication 590-A
href: https://www.irs.gov/pub/irs-pdf/p590a.pdf#page=45
- title: 2023 Form 8880
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
- 2026 amounts: Rev. Proc. 2025-32, section 4.14:
https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=18
- IRC 6012(f) for 2024-2025, and 6012(a)(1) for 2026. Claimable filers
retain the dependent worksheet in every year (decision d1395):
keep the dependent worksheet in every year, and from 2026 also meet the
(A) and (B) threshold with the worksheet's limited basic deduction:
https://www.law.cornell.edu/uscode/text/26/6012

The federal deduction worksheet and Medicaid Table 2 reference use W-2 box 1
Expand Down Expand Up @@ -223,8 +224,13 @@ def reference(person, year):
# A claimed dependent keeps the section 63(c)(5) worksheet and Table 2's
# unearned-income test. Otherwise 6012(f) includes blindness through 2025,
# and 6012(a)(1) in 2026 permits the age addition only, with >= at equality.
# From 2026 that (A) and (B) threshold binds a claimed dependent too, with
# the limited basic deduction; (C) adds to it.
aged_threshold = basic + addition * int(person["age"] >= 65)
if claimed:
required = gross > standard or unearned > floor + extra
if year >= 2026:
required = required or gross >= aged_threshold
elif year <= 2025:
required = gross > standard
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
spouse's income other than earned income against the section 63(c)(5)(A)
amount alone, while Table 2 adds the additional standard deduction for age or
blindness; the model follows the statute, and property 10 states it. Where
the statute and the IRS charts differ otherwise (a dependent filing
separately, and in 2026 blindness and the exact dollar), the tests below
assert nothing that depends on the difference.
the statute and the IRS charts differ otherwise for 2018-2025 (a dependent
filing separately), the tests below assert nothing that depends on the
difference. From 2026 the model follows section 6012(a)(1): its (A) and (B)
threshold, with the limited basic standard deduction and the addition for
age only, binds a filer who can be claimed too, and (C)(i) adds that filer's
unearned income and standard deduction tests.

`test_table_2_boundary_grid` runs one simulation of single filers on a grid of
earned and unearned income at every Table 2 threshold, a dollar under and a
Expand All @@ -27,8 +30,8 @@
2. Differential: in 2025 it also equals `medicaid_person_is_required_to_file`,
the other Table 2 implementation, which serves Medicaid and ACA household
income for dependents inside a tax unit. In 2026 the rule is section
6012(a)(1), whose treatment of blindness and the exact dollar is open, so
the two are not compared there.
6012(a)(1), which leaves out blindness and requires a return at the exact
dollar, unlike Table 2, so the two are not compared there.
3. For a filer nobody can claim, the result depends on gross income alone: it
is the same however that income is split between wages and interest.
4. On a 2025 joint return with no other income, where the spouse who can be
Expand All @@ -52,8 +55,8 @@
6. Monotonicity: more wages, interest or self-employment profit never turns
"required" into "not required", for any tax unit.
7. Claimability never removes the requirement: marking the head as claimable
never turns "required" into "not required" (filers who are not blind and
do not file separately; see below).
never turns "required" into "not required" (every filer from 2026; for
2025, filers who are not blind and do not file separately; see below).
8. A tax unit dependent's wages and interest never change the result: that
income belongs on the dependent's own return.
9. Table 2 and the Medicaid implementation, as in 1 and 2, on the drawn
Expand All @@ -77,22 +80,14 @@
instructions, Chart A) but it does reduce the earned income that sets the
limited standard deduction, so a smaller loss raises the threshold and not
the income. `test_smaller_loss_can_end_the_requirement` pins the case.
- Property 7 leaves out blind filers. Chart A does not count blindness (only
age), while Table 2 does, so on the charts a blind filer under 65 with
wages between the two amounts must file only if nobody can claim them. From
2026 the model follows Chart A here: section 6012(a)(1)(B) raises the
threshold of a filer nobody can claim for age only, while a filer who can
be claimed keeps the standard deduction test (decision d1395).
- Property 7 also leaves out separate filers: Table 2 lets a claimable
married dependent filing separately skip a return unless the spouse
itemizes, while sections 6012(f) and (a)(1) require one with any gross
income (decision d1395).
- From 2026, property 7 also fails at one dollar: gross income equal to the
threshold of a sighted filer nobody can claim. Section 6012(a)(1)(A)
excuses that filer only if gross income is "less than" the threshold, while
a filer who can be claimed is tested with "in excess of" (d1395). The
property skips those units, and
`test_claimability_can_end_the_requirement_at_the_threshold` pins the case.
- For 2025, property 7 leaves out blind filers and separate filers. Table 2
adds blindness to a claimable filer's threshold, while Chart A doesn't for
a filer nobody can claim, and Table 2 lets a claimable married dependent
filing separately skip a return unless the spouse itemizes. From 2026,
property 7 checks every unit: the (A) threshold, which a claimable filer
also meets, counts age only, requires a return at equality, and reaches
any separate filer. `test_claimability_does_not_end_the_requirement_at_the_threshold`
pins the equality case.
- Properties 1, 2 and 9 use wages as the only earned income. Earned income for
the limited deduction is net of the deductible part of self-employment tax
on the IRS worksheet, which Table 2 does not spell out.
Expand Down Expand Up @@ -507,18 +502,13 @@ def test_filing_requirement_properties(anchors, drawn):
f"{year}: more income ended the requirement"
)
assert required["base"].any() and not required["base"].all()
# Property 7, except at the 2026 threshold (see the docstring).
sighted = ~anyone_blind[:, 0] & ~separate[:, 0]
ended = required["base"] & ~required["claimed"]
# Property 7; for 2025, sighted filers who don't file separately
# (see the docstring).
checked = ~anyone_blind[:, 0] & ~separate[:, 0]
if year >= 2026:
gross_income = unit_totals(
simulation, units, "irs_gross_income", year
).reshape(-1, len(VARIANTS))[:, 0]
standard_deduction = np.asarray(
simulation.calculate("standard_deduction", year)
).reshape(-1, len(VARIANTS))[:, 0]
ended &= ~(nobody_claimable & (gross_income == standard_deduction))
assert not ended[sighted].any(), (
checked = np.ones_like(checked)
ended = required["base"] & ~required["claimed"]
assert not ended[checked].any(), (
f"{year}: being claimable ended the requirement"
)
# Property 8.
Expand Down Expand Up @@ -561,12 +551,12 @@ def test_smaller_loss_can_end_the_requirement():
assert required.tolist() == [True, False]


def test_claimability_can_end_the_requirement_at_the_threshold():
# Intended exception to property 7 from 2026. Nobody can claim the first
# filer, whose $16,100 of wages equals the threshold of section
# 6012(a)(1)(A)(i), so a return is due. The second filer can be claimed;
# $16,100 does not exceed their standard deduction of $16,100
# ((a)(1)(C)(i)(II) and Chart B), so no return is due (decision d1395).
def test_claimability_does_not_end_the_requirement_at_the_threshold():
# From 2026, $16,100 of wages equals the section 6012(a)(1)(A)(i)
# threshold, so a return is due whether or not the filer can be claimed:
# (a)(1)(C) adds to (A) for a claimable filer, whose limited basic
# standard deduction is also $16,100 here; it doesn't replace (A) with
# the "in excess of" standard deduction test.
units = [single_filer(40, False, claimed, 16_100, 0) for claimed in (0, 1)]
required = simulate(units).calculate("tax_unit_is_required_to_file", 2026)
assert required.tolist() == [True, False]
assert required.tolist() == [True, True]
Loading
Loading