Skip to content

Limit the self-employed health insurance deduction to net profit less half of SE tax and plan deductions - #10109

Merged
MaxGhenis merged 7 commits into
mainfrom
fix-sehi-earned-income-limit
Oct 29, 2026
Merged

MaxGhenis merged 7 commits into
mainfrom
fix-sehi-earned-income-limit

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Problem

self_employed_health_insurance_ald_person returned min(max(0, Schedule C net profit), premiums). The law caps the deduction at a smaller amount:

  • 26 U.S.C. 162(l)(2)(A). The deduction may not exceed "the taxpayer's earned income (within the meaning of section 401(c)) derived by the taxpayer from the trade or business with respect to which the plan providing the medical care coverage is established."
  • 401(c)(2)(A). Earned income is net earnings from self-employment determined "(v) with regard to the deductions allowed by section 404 to the taxpayer, and (vi) with regard to the deduction allowed to the taxpayer by section 164(f)."

Both texts were verified on 2026-10-11 at govinfo USCODE-2023.

The IRS computes this limit in three forms:

Example (2022). Schedule C profit $2,000, premiums $6,000.

  • Half of SE tax is 2,000 × 0.9235 × 0.153 / 2 = $141.30, so the limit is $1,858.70.
  • Main gives $2,000, which leaves AGI at −$141.30.

Change

  • New self_employed_health_insurance_earned_income_limit (Person, Form 7206 lines 4–13):
    • line 4: Schedule C net profit (total_self_employment_income, floored at 0), treated as the business under which the plan is established, as before. SSTB and non-SSTB Schedule C income count as one business, so a loss in one reduces line 4 (stated in the variable; Let farm, partnership and S corporation plans set the self-employed health insurance limit #10120 takes up the choice of plan business);
    • lines 5–8: subtract the part of the deductible SE tax (self_employment_tax_ald_person, Schedule 1 line 15) attributable to that business, by its share of all profitable SE businesses. Line 5 is Schedule C + Schedule F (farm_operations_income) + K-1 box 14 A (partnership_self_employment_net_earnings), with losses left out. This is the same set of sources taxable_self_employment_income taxes;
    • lines 9–10: subtract self_employed_pension_contribution_ald_person (Schedule 1 line 16). The model bases that deduction on Schedule C income, so all of it is attributable to the plan business. That deduction's own cap is loose (100% of Schedule C profit rather than Pub. 560's maximum; Limit self-employed plan contributions to the Pub. 560 maximum deduction #10121 fixes it), so a hand-entered contribution above the legal maximum now also cuts this limit. The default dataset has 7 such records and in none of them does it change the deduction;
    • floored at 0.
  • self_employed_health_insurance_ald_person is now min(premiums, limit) (line 14).
  • Not modeled (documented in the variable):
    • S corporation wages (line 11);
    • qualified long-term care premiums (line 2). The SE premiums take only health_insurance_premiums; a separate follow-up covers 213(d)(10).
    • Form 2555 (line 12) is already reflected, because SE income inputs are net of the section 911 exclusion.
  • Schedule 1 line 15 is read per person (each person's own 164(f) deduction, as 401(c)(2)(A)(vi) reads). On a joint return where one spouse's earnings exceed the wage base, this differs from a literal joint Form 7206, which would prorate the couple's combined line 15 by each business's share. The couple's totals can differ. Example, 2025: Schedule C $200,000 and $10,000, premiums $200,000 and $0. Per person this gives $186,403.65; the joint proration gives $186,378.26.

No circularity. self_employment_tax reads taxable_self_employment_income and social_security_taxable_self_employment_income, neither of which reads this deduction; 162(l)(4) keeps it out of net earnings. self_employed_pension_contributions reads only its desired input and total_self_employment_income.

Invariants

Properties 1–4 hold for every input; property 5 holds for the generated filers. All are tested with Hypothesis in tests/unit/test_self_employed_health_insurance_properties.py. Each batch is one vectorized simulation over 2022, 2024 and 2026.

  1. Schedule 1 line 15 equals half of the Schedule SE tax computed in the test from 0.9235, the $400 floor, the SSA wage base, 12.4% and 2.9% (test data, not model parameters). The limit equals the closed-form Form 7206 lines 4–13 from that line 15 and the model's line 16, and the deduction is min(premiums, limit).
  2. 0 ≤ deduction ≤ premiums.
  3. Deduction ≤ Schedule C profit less the attributable SE tax deduction, which itself ≤ Schedule C profit.
  4. Raising premiums by d raises the deduction by between 0 and d (monotone, 1-Lipschitz), and the limit does not depend on premiums.
  5. For a generated filer whose only income is Schedule C profit, and whose only adjustments are the SE tax, plan and health insurance deductions and business losses, the deduction never makes AGI negative: AGI < 0 implies no deduction. Other adjustments can still make AGI negative, for example 2020's $300 charitable deduction for non-itemizers.

The strategies draw losses, SSTB and non-SSTB Schedule C income of either sign, the $400 threshold (434 vs 433 of profit), wages above the Social Security wage base, multiple businesses, and large plan deductions. Comparisons allow float32 rounding (rtol 1e-6) on top of a cent.

Tests

  • New YAML (expectations hand-computed from the forms):
    • self_employed_health_insurance_earned_income_limit.yaml, 10 cases: one business, plan deduction, farm and partnership proration, a farm loss, the wage base, the $400 threshold, a loss, a plan deduction that floors the limit at 0, and an SSTB loss netted against non-SSTB profit;
    • self_employed_health_insurance_ald.yaml, 8 cases, including:
      • the $2,000 example, with AGI 0;
      • a couple, each capped by their own earned income;
      • spouses on either side of the Social Security wage base;
      • a dependent whose deduction is capped and stays off the filer's return.
  • Fails on main. With the source files reverted in place:
    • the 4 positive-deduction cases fail by exactly the expected amounts: 141.30; 4,412.96 (half SE tax + the $3,000 plan); 2,119.43; 211.94;
    • all limit cases fail, because the variable is new;
    • the 2 guard cases (premiums under the limit, a loss) pass on main, as intended.
  • Updated expectations (premiums above the new limit). I scanned every YAML case that derives the deduction from positive premiums; these are all that move:
    • self_employment_income.yaml (SSTB $10,000 → 9,293.52);
    • oh_medical_care_insurance_premiums.yaml and oh_uninsured_unreimbursed_medical_care_expenses.yaml ($3,000 profit, $5,000 premiums → deduction 2,788.06, Ohio premiums 2,211.94);
    • medical_expense_deduction.yaml, two cases from Exclude SE health insurance deductions from Schedule A medical expenses #10055 ($1,000 profit, $6,000 premiums → deduction 929.35, Schedule A 5,570.65, medical deduction 3,320.65);
    • test_mi_household_resources_exclusions.py (deduction 3,000 → 2,788.06; Michigan resources unchanged, as its comment predicted) and the matching comment in mi_household_resources.yaml;
    • test_medical_expense_premium_conservation.py (Exclude SE health insurance deductions from Schedule A medical expenses #10055): Schedule A premium use + deduction = premiums paid now holds to a cent instead of exactly, because the derived deduction has cents.
  • Local runs (macOS, policyengine-core 3.33.2, merged with main at bf108e9, each file in its own process): both new YAML files (10 and 8 cases), the property test (2 tests), medical_expense_deduction.yaml (11), mi_household_resources.yaml (67), self_employment_income.yaml (9), both Ohio files (5 and 10), test_medical_expense_premium_conservation.py (5) and test_mi_household_resources_exclusions.py (9) all pass. After the round-2 review I reran the deduction YAML (8 cases) and the conservation test (5), and both pass. The remaining files that read SE income and premiums are left to CI: the host's heavy-run lock is reserved through 4 November.
  • Cost of the new Python test. It builds one Simulation per Hypothesis example (12 drawn batches of up to 40 people, plus one fixed batch) and runs in the Rest group's tests/unit collection. Locally it took 35 s wall clock, 11 s of test time, and a 1.07 GB peak footprint (/usr/bin/time -l, host load about 118). There is no before figure: the file is new. Linux CI timing not measured.

Consumers checked

These read the corrected amount and need no change:

  • federal AGI (gov.irs.ald.deductions, adjusted_gross_income_person);
  • the QBI deduction definition (199A(c)(1) reduction);
  • the Medicaid MAGI person map, dependent_taxable_ss_magi, dependents_self_employed_health_insurance_ald;
  • MO (mo_adjusted_gross_income), MS (ms_self_employed_health_insurance_adjustment), NJ (nj_medical_expense_deduction, Worksheet F);
  • the premium nets in ID (id_qualified_health_insurance_premiums) and OH (oh_medical_care_insurance_premiums);
  • SC (two-wage-earner subtractions), AL (al/tax/income/agi/deductions), MI (household resources);
  • every state that starts from federal AGI.

With a smaller deduction, consumers that net it from premiums (ID, OH, and Schedule A once #10055 lands) keep the premiums left over. Pub. 535 allows that: "any medical insurance payments not deductible on Schedule 1 … can be included as medical expenses on Schedule A".

Since merged. #10055 (Schedule A excludes the SE deduction) and #9947 (Montana 2021–2023 lines 2–3) landed after this branch was cut. This PR updates #10055's two derived cases and its conservation tolerance (listed above), as posted on #10055 (comment). #9947's Montana cases supply the deduction or keep it below the limit, so they don't move. self_employed_health_insurance_ald_excluded_premiums (#10055) reads this deduction, so Schedule A now picks up the premiums the corrected limit leaves over.

Stacked work. #10120 (plan business, including S corporation wages) and #10131 build on this branch and edit the same limit variable, YAML and property test; #10121 replaces the pension cap. Merge order #10109 → #10120 → #10121 → #10131.

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base bf108e931a against this PR's head 343c325710, for 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.

Output 2026 change 2026 records changed (weighted)
self_employed_health_insurance_ald_person -211.36M 187 (689,511)
self_employed_health_insurance_ald -211.36M 185 (689,511)
adjusted_gross_income +211.36M 185 (689,511)
qualified_business_income_deduction +5.10M 46 (122,550)
itemized_medical_expenses +211.36M 185 (689,511)
medical_expense_deduction +124.46M 104 (347,976)
income_tax +23.03M 137 (468,190)
state_income_tax +4.06M 106 (336,094)
premium_tax_credit -3.14M 15 (89,668)
household_net_income -27.31M 144 (530,695)

This full pair, at this PR's final head, replaces the 2026-10-11 population run at a885a16f12. The new limit variable self_employed_health_insurance_earned_income_limit has no counterpart on main, so it has no row.

Comparison files are in fixes/10109-impact/ on the hub host.

Impact

Households.

  • 2022: Schedule C $2,000, premiums $6,000, single (TX). Deduction $2,000 → $1,858.70; AGI −$141.30 → $0. Income tax is unchanged: −$142.19 either way, which is the EITC (7.65% of $1,858.70 of earned income, which doesn't depend on this deduction).
  • 2026: wages $20,000, Schedule C $10,000, premiums $10,000, single (TX), the EqRC student-debt pilot's case. Deduction $10,000 → $9,293.52; AGI and taxable income +$706.48; federal income tax $319.35 → $390.00 (+$70.65); household net income −$70.65.

These come from runs of this branch, with the old amount supplied as self_employed_health_insurance_ald_person for the "main" side.

Population, 2026. The hub's full-file pair at this PR's final head, above, replaces the 2026-10-11 run at a885a16f12. Every deduction change is a decrease (largest $2,707), since the new limit never exceeds the old one. Schedule A now takes back the premiums a smaller deduction leaves over: itemized_medical_expenses rises by the same $211.36M, and medical_expense_deduction by $124.46M for the 104 tax units where it binds.

Follow-ups (filed separately)

  • The self-employed plan contribution limit (self_employed_pension_contribution_limit) uses raw Schedule C profit, not 401(c) earned income (Pub. 560 reduced rate).
  • Plans under a farm or partnership business, and S corporation >2% shareholders (162(l)(5)), get no deduction.
  • 162(l)(2)(B) months eligible for an employer plan, and the premium tax credit interaction (Pub. 974).
  • Eligible long-term care premiums under 213(d)(10).

impact: 2026, hub full-file pair at 343c325: self-employed health insurance deduction −$211.36M (185 tax units, 689,511 weighted), income tax +$23.03M, state income tax +$4.06M, premium tax credit −$3.14M, household net income −$27.31M

axiom: us/statute/26/162/l queued (TheAxiomFoundation/rulespec-us#1676)

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 11, 2026 13:07
…ncome

26 U.S.C. 162(l)(2)(A) caps the deduction at earned income within the
meaning of section 401(c), which section 401(c)(2)(A)(v) and (vi) reduce
by the section 404 plan deduction and the section 164(f) deduction for
half of self-employment tax. Form 7206 (and Pub. 535 Worksheet 6-A before
2023) subtracts the share of Schedule 1 line 15 attributable to the plan
business and the line 16 plan deduction from that business's net profit.
The model used net profit alone.

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

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 28, 2026 12:13
…expectations

- The property test now computes Schedule 1 line 15 from Schedule SE
  (0.9235, the $400 floor, the wage base, 12.4% and 2.9%) instead of
  reading it from the model, draws SSTB Schedule C income, and allows
  float32 rounding (rtol 1e-6) on top of the cent tolerance.
- The limit's documentation states that SSTB and non-SSTB Schedule C
  income are one plan business and that Schedule 1 line 15 is read per
  person; YAML cases pin both the SSTB netting and a plan deduction that
  floors the limit at zero.
- YAML comments name Schedule 1 lines 15 and 16, and the deduction
  file's header names Pub. 535 Worksheet 6-A as what Form 7206 replaced.
- Tests merged since the branch was cut move with the corrected limit:
  two Schedule A cases from #10055 (1,000 -> 929.35225), the conservation
  check's exact equality (now a cent), and the Michigan comment and
  deduction assertion (3,000 -> 2,788.05675; Michigan resources unchanged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 28, 2026 16:38
Round-2 review: add YAML cases for spouses on either side of the Social
Security wage base (each uses their own Schedule 1 line 15) and for a
dependent whose deduction is capped by their own earned income and stays
off the filer's return, and pass rtol=0 so the conservation check allows
exactly a cent.

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

Copy link
Copy Markdown
Contributor Author

Hand-off to the US + core hub (per ~/reviews/hubs.md)

🤖 Generated with Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10109 at 343c325710da4e350fe5ea75848cddaa54beb18b (squash): limit the self-employed health insurance deduction to net profit less half of SE tax and plan deductions.

  • Reviews: GPT-6.1 Sol, three rounds, run by the authoring session before it handed the PR to the hub on 2026-10-28. Round 3 APPROVE at 343c325710 (~/reviews/sehi-earned-income-limit-2026-10/review/review-r3.md). Its one P3 asked the body to drop a claim that the per-person and joint readings give equal totals; the body now says the couple's totals can differ and gives a 2025 example.
  • Gates checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head (40731449322) concluded success;
    • MERGEABLE;
    • not a draft;
    • no reviewer's latest review requests changes;
    • head pinned.
  • Overlap with main: CI ran on base bf108e931a; main is now c55b806541 with Add North Dakota LIHEAP regular heating assistance #10342 (North Dakota LIHEAP) and a version bump. ND LIHEAP reads self_employment_income and sstb_self_employment_income as earned income, both inputs this PR doesn't change; it reads no deduction. The merge-tree is clean.
  • Impact: measured by the hub. A full-file microsimulation for 2026 compared main at the merge base bf108e931a with the head 343c325710; the hub checked that the PR-side snapshot matches the head file for file.
    • The self-employed health insurance deduction falls $211.36M across 185 tax units (689,511 weighted); every change is a decrease, the largest $2,707.
    • AGI and itemized_medical_expenses each rise $211.36M; medical_expense_deduction rises $124.46M for 104 tax units; the QBI deduction rises $5.10M.
    • Income tax +$23.03M, state income tax +$4.06M, premium tax credit −$3.14M, household net income −$27.31M.
    • Files: ~/reviews/us-hub/fixes/10109-impact/ (compare.2026.json).
  • Axiom: us/statute/26/162/l queued (Encode IRC §162(l): the self-employed health insurance deduction and its 401(c) earned-income limit TheAxiomFoundation/rulespec-us#1676).
  • Follow-ups: Exclude employer-plan-eligible premiums from the self-employed health insurance deduction #10122 (owner to be woken after this lands), then pe-us#10528 (cap at the plan business's own earnings, including farm and partnership income) with pe-us#10529 (the self-employed pension deduction this limit subtracts as Form 7206 line 9), then Net the premium tax credit out of the self-employed health insurance deduction (26 U.S.C. 280C(g), Rev. Proc. 2014-41) #10124.

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.

1 participant