Skip to content

Limit self-employed plan contributions to the Pub. 560 maximum deduction - #10121

Draft
MaxGhenis wants to merge 11 commits into
mainfrom
fix-se-pension-earned-income-limit
Draft

MaxGhenis wants to merge 11 commits into
mainfrom
fix-se-pension-earned-income-limit

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Part of #10529.

What and why

On main, the self-employed SEP, SIMPLE and qualified plan deduction (Schedule 1 line 16) is min(desired contributions, the section 415(c) dollar limit, Schedule C net profit). Contributions can reach 100% of profit, so this deduction alone can make AGI negative: $1,000 of 2026 profit with $1,380 desired gives a $1,000 deduction and AGI of −$70.65.

This PR caps the deduction at the most the Pub. 560 Deduction Worksheet for Self-Employed allows:

  • New self_employed_pension_earned_income (worksheet step 3): Schedule C net profit (total_self_employment_income, floored at 0) less the part of Schedule 1 line 15 attributable to it. With farm or partnership earnings too, line 15 is split by each business's share of the positive net profits of Schedule C, Schedule F and K-1 box 14 code A, as on Form 7206 lines 5 to 8. The Schedule C business stays the plan's business, as on main.

  • New parameter gov.irs.gross_income.retirement_contributions.limit.profit_sharing_deduction_rate: 25% from 2002.

  • self_employed_pension_contribution_limit is the worksheet's maximum in closed form, with L = step 3:

    min(L, catch-up room + min(dollar limit, 0.2 L + regular deferral room, (L + regular deferral room) / 2))

    The deferral room is the section 402(g) limit plus the person's catch-up amount, less their deferrals to other employers' 401(k) and 403(b) plans, which use the regular limit first.

  • self_employed_pension_contribution_ald_person = min(contributions, limit), so directly supplied contributions are capped too.

With $1,000 of profit, step 3 is 1,000 − 70.65 = $929.35, so the deduction is $929.35 and AGI is 0.

Sources

  • 26 U.S.C. 404(a)(8)(C) allows contributions for a self-employed individual "to the extent that such contributions do not exceed the earned income of such individual (determined without regard to the deductions allowed by this section)" from the plan's business. 404(a)(8)(D) makes compensation that earned income.
  • 401(c)(2)(A) determines earned income "with regard to the deductions allowed by section 404" and "with regard to the deduction allowed to the taxpayer by section 164(f)".
  • 404(a)(3)(A)(i)(I) and 404(h)(1)(C) cap employer contributions to a profit-sharing plan or SEP at 25% of compensation. Compensation is net of the contribution itself, so the rate becomes 25/125 = 20% of step 3: the rate table's "0.200000" for a 25% plan (Pub. 560 (2025), page 40). EGTRRA § 616 raised the rate from 15% for years beginning after 2001.
  • 404(n) keeps elective deferrals outside that limit. 415(c)(1) caps annual additions at the dollar limit and 100% of compensation, which 415(c)(3)(B) and (D) make earned income including elective deferrals. 414(v)(3)(A) puts catch-up contributions outside 415(c), and 414(v)(2)(A)(ii) limits them to compensation less other deferrals.
  • Pub. 560 (2025), page 7: with more than one business but one plan, "only the earned income from that business is considered for that plan."
  • The worksheet is the same in the 2022 edition (page 34). Its steps 6 and 7 cap the employer contribution at the plan rate times the 401(a)(17) compensation limit, which never binds the result: 25% of that limit is 1.25 times the step 8 dollar limit in every year from 2002 (for example $350,000 and $70,000 for 2025), and step 13 can't exceed step 8.

Which plan the input represents

self_employed_pension_contributions_desired doesn't say whether the plan is a SEP, a SIMPLE IRA or a profit-sharing plan with a 401(k) arrangement, and their limits differ. The limit is the most any defined contribution plan allows: a 401(k) arrangement into which the person defers, before tax, all the 402(g) room they have left. With no room left, it is the SEP and profit-sharing limit, min(dollar limit, 20% of step 3).

  • It never cuts a deduction the law allows for some defined contribution plan the person could have. A SEP-only rule would cut solo 401(k) deductions, which reach 100% of step 3 at low profit. Under 401(b)(2) a plan adopted by the return's due date can be treated as adopted at year-end, and a sole owner's first-year deferrals can be made by then, so the plan is in effect the filer's choice. The tax-minimizing choice has a closed form here, the worksheet maximum.
  • It enforces every hard ceiling: 404(a)(8)(C) (never above step 3), 415(c), 402(g) and 414(v).
  • Where it overstates: a person who really has only a SEP or a SIMPLE IRA. Self-employed pension deduction: compensation from Schedule C profit, no section 404 percentage limit, catch-ups under 415(c) #10529's Case 1 (2026, Schedule C $10,000, $5,000 to a SEP): step 3 is 10,000 − 706.48 = $9,293.52, and a SEP allows 20% of it, $1,858.70. This PR allows the whole $5,000, as main does, because a 401(k) arrangement's deferrals could cover all of step 3.
  • Where it understates: defined benefit plans, which aren't modeled. Their deductions are actuarial and capped only by earned income, but contributions to them above this limit are cut. That is lower than main's 415(c) cap for moderate earners: in 2025 at age 55 with $150,000 of profit, main allows $70,000 and this PR $58,880.57.

Not changed

  • The Schedule C business stays the plan's business, so a farmer or partner with no Schedule C profit still gets no deduction (Let farm and partnership plans take the self-employed plan deduction #10131 proposes a retirement-plan business input).
  • The personal-services test of 401(c)(2)(A)(i), designated Roth deferrals (worksheet step 20) and SIMPLE IRA or SARSEP deferrals to other employers aren't modeled.

Source conflicts

Two readings are recorded in source_conflicts/candidates.yaml for the register:

  • us:catch-up-deferrals-after-402g1c-repeal. SECURE 2.0 § 603(b)(1) struck 402(g)(1)(C), the catch-up exclusion, for taxable years beginning after 2023. Notice 2023-62 § III.A says non-Roth catch-ups "are not includible in the participant's gross income", and Treas. Reg. 1.402(g)-2(a) still adds the catch-up amount to the 402(g) limit. The model follows the notice and the regulation: elective_deferral_limit already adds the catch-up in every year, and this limit adds the same catch-up room. The regulation binds unless it flatly contradicts the amended statute, so this goes to the project lead.
  • us:se-plan-step-2-multiple-businesses. With more than one business, the worksheet's step 2 enters all of Schedule 1 line 15. The statute doesn't say how to split the 164(f) deduction. Form 7206 splits it by net profit for the same 401(c) earned income, and so does this PR.

Invariants

Tested with Hypothesis in policyengine_us/tests/unit/test_self_employed_pension_limit_properties.py. Each draw is a batch of single filers over 2022, 2025 and 2026, run as one vectorized simulation.

  1. Differential. Schedule 1 line 15 equals half of the Schedule SE tax computed in the test (0.9235, the $400 floor, the published wage bases, 12.4% and 2.9%). The limit equals a literal implementation of worksheet steps 4 to 19, maximized over a grid of elective deferrals and catch-up contributions in the person's room, with the IRS-published dollar amounts rather than model parameters. Step 3 equals Schedule C profit less its share of line 15 (step 2).
  2. Bounds. deduction = min(desired, limit) ≥ 0; deduction ≤ earned income ≤ Schedule C profit (404(a)(8)(C)); limit ≤ dollar limit + the person's catch-up amount (414(v)(3)(A)).
  3. AGI. For a filer whose only income is a non-negative Schedule C profit, AGI is never negative, because the deduction is at most profit less line 15.
  4. Monotonicity and the SEP path (deterministic grid). At the grid's points the limit rises with Schedule C profit (it dips just past $433, where self-employment tax starts) and falls as deferrals to an employer's plan use up the room. With no room it equals min(dollar limit, 20% of step 3).

Test plan

  • YAML expected values come from the Pub. 560 worksheet, hand-computed with exact fractions and rechecked independently (each case shows its steps):
    • self_employed_pension_contribution_limit.yaml, 20 cases: the 2025 and 2022 Pub. 560 example profits; the SEP path (no deferral room); catch-ups at ages 55 and 61, including above the dollar limit and, new, a catch-up that earned income leaves no room for; low profit, where the limit is all of step 3; the 100%-of-compensation region; partial room after W-2 401(k) deferrals and, new, 403(b) deferrals; farm proration; a plan under Schedule C beside a partnership (new; it replaces a case that pinned a partner's limit at 0); the $400 threshold; and a loss.
    • self_employed_pension_contribution_ald.yaml, 3 cases: the $20,000 example (deduction $18,587.05, AGI 0), directly supplied contributions, and a couple.
    • Moved expectations in other files: self_employment_income.yaml's SSTB case ($8,000 → $7,434.82), self_employed_health_insurance_earned_income_limit.yaml Case 9 (plan deduction $20,000 → $18,587.045; the limit stays 0), two saver's credit cases (profit raised so the old amounts still fit), and Utah Case 12 ($72,000 → $51,087.05).
  • Source conflicts: the two candidates above.
  • ruff format and ruff check on the changed Python files.
  • Changelog fragment changelog.d/fix-se-pension-earned-income-limit.fixed.md.

Results

  • Runs at this head (Linux, Python 3.14, one file per process): limit YAML 20 passed, ALD YAML 3 passed, property test 4 passed, self_employed_health_insurance_earned_income_limit.yaml 10, self_employed_health_insurance_ald.yaml 8, self_employment_income.yaml 9, saver's credit 3, Utah 12, and the source-conflict (103) and reference code-health tests.

  • Readers of the deduction, at the merge commit e716bc1719: 29 files, 454 items, all passed. They cover the dependent and spouse ALD invariants, ALD determinism, QBI (4 files), IRA compensation, IRA active participation and the IRA invariants, the uprating series, TAXSIM outputs, and the AL, AZ, MI, MN, MO, MS, MT and UT consumers.

  • On main (4e71481e19, this PR's test files): the limit file has 19 failures and 1 pass (the $500,000 dollar-limit guard), and the ALD file 3 failures.

  • Mutants, each run in its own process with the code restored afterwards:

    • no halving in the 100%-of-compensation term;
    • catch-ups added outside the earned-income cap;
    • the unreduced 25% rate;
    • earned income not net of line 15;
    • other employers' deferrals ignored;
    • the ALD not reapplying the limit;
    • 403(b) deferrals left out of the room;
    • a 1% error in line 15.

    Each fails at least one file. The catch-up mutant passed both YAML files until the new catch-up case, and the line 15 error passes unless line 15 comes from Schedule SE.

  • Cost of the new property test. It runs in the Rest job's "remaining" pytest group, which the Makefile records at 14.4 GB and 22:18 in CI run 38141803955. Measured on this session's Linux VM, not CI, two runs: 44.6–56.4 s and 991 MB peak resident memory. The file is new, so there is no before. It catches what the YAML can't: the closed form against the literal worksheet across random filers, ages, wages and other-employer deferrals.

  • CI: not run; this is a draft.

Overlap with other open PRs

Issue #10529

  • Fixed: Case 2 (Schedule C $1,000, $1,380 desired) gives $929.35 and AGI 0, where main gives $1,000 and −$70.65. Case 5 (age 55, $300,000, $80,000 to a solo 401(k)) gives $80,000, where main gives $72,000. Also fixed: compensation from earned income net of the SE tax deduction, and the 20% rate for a person with no deferral room.
  • Not fixed: Case 1 (a SEP), which needs a plan-type input. Cases 3 and 4 (a partnership or farm plan): Let farm and partnership plans take the self-employed plan deduction #10131 proposes the business input, and Case 3 gives $929.35 here. Defined benefit plans.

impact: the hub will measure
axiom: (hub files pe-parity before landing)


Generated by Claude Code

MaxGhenis and others added 3 commits October 11, 2026 16:16
The limit used 100% of Schedule C net profit. Plan compensation for a
self-employed person is 401(c)(2) earned income, net of the deductible
part of self-employment tax and of the plan deduction itself, and
404(a)(8)(C) caps the deduction at that earned income before the plan
deduction (Pub. 560 Deduction Worksheet for Self-Employed).

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

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Main gained #10109's Form 7206 limit, which subtracts this branch's plan
deduction (Schedule 1 line 16), and its tests. No textual conflicts; the
SSTB pension case in self_employment_income.yaml keeps this branch's
expectation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's Case 9 (2023, $20,000 of profit, $20,000 desired) expected the old
plan deduction of $20,000. The worksheet caps it at earned income,
20,000 - 1,412.955 = 18,587.045, so line 10 is still 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A mutant that added catch-up contributions outside the earned income cap
passed both YAML files and failed only the property test. The new case
(2025, age 55, $20,000 of profit) has step 3 of $18,587.05 and leaves no
room in step 16 for the $7,500 catch-up, so the limit is $18,587.05; the
mutant gives $26,087.05.

Also correct worksheet comments: case 1's 20% figure is step 5 (step 6
caps step 7 at $90,000), the header now says step 3 subtracts only the
Schedule C share of line 15, and the ALD comments use the exact
$1,412.955. The changelog no longer says the deduction can't reach 100%
of profit, which it can below the $400 threshold.

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

Two readings go to the source-conflict backlog for the register:
- SECURE 2.0 section 603(b)(1) struck 402(g)(1)(C) for years after 2023,
  but Notice 2023-62 and Treas. Reg. 1.402(g)-2 keep pre-tax catch-ups
  excludable. The model follows them, in elective_deferral_limit and now in
  this limit's catch-up room.
- With more than one business, Pub. 560's step 2 enters all of Schedule 1
  line 15; the model splits it by net profit as Form 7206 does.

The limit's documentation now says it is the most a defined contribution
plan allows, so it overstates a SEP or SIMPLE IRA and cuts defined benefit
contributions above it, and that steps 6 and 7 are left out only because
they never bind at current law. Earned income says it assumes the
personal-services test is met. References name each source once, with the
2025 Pub. 560 at its stable URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The property test took Schedule 1 line 15 from the model, so a Schedule SE
error would have passed it. It now computes line 15 from Schedule SE with
the published wage bases, as the SEHI test does, allows float32 rounding
explicitly, and checks one year per grid case.

New and changed YAML cases:
- Deferrals to another employer's 403(b) plan use up the room (2026:
  $20,000 deferred leaves $4,500, so the limit is $23,087.05).
- The partnership-only case asserted a $0 limit that Pub. 560 step 1 and
  401(c)(4) contradict. It becomes a Schedule C plan beside a $50,000
  partnership share, which counts only the Schedule C business's $929.35
  of earned income (Pub. 560 page 7).
- The age 55 case adds the catch-up under the dollar limit, so its name no
  longer claims to show the catch-up sitting outside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The limit is the most a defined contribution plan allows, so defined
benefit contributions above it are cut, as the variable's documentation
says.

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I continue the earlier session's work on this branch (its last commit is 1c8823f230; it stopped before posting a hand-off). I'll post a hand-off comment here when it's ready.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants