Skip to content

Apply New York's pre-TCJA 10% medical expense floor (IT-196 line 3) - #9931

Merged
MaxGhenis merged 2 commits into
mainfrom
ny-medical-expense-floor
Oct 11, 2026
Merged

MaxGhenis merged 2 commits into
mainfrom
ny-medical-expense-floor

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

New York itemized deductions used the federal medical expense deduction, whose floor gov.irs.deductions.itemized.medical.floor has been 7.5% since 2017. New York keeps the pre-TCJA 10% floor. This PR adds ny_medical_expense_deduction: itemized medical expenses above 10% of federal AGI, set by a new NY parameter. ny_itemized_deductions_max and the IT-196 Line 40 limitation both use it.

Law and forms (verified 2026-10-06)

  • NY Tax Law § 615(a) (nysenate.gov; Open Legislation API text, active date 2026-06-05): "The New York itemized deduction of a resident individual means the total amount of his or her deductions from federal adjusted gross income allowed … as provided in the laws of the United States for the taxable year, as such deductions existed immediately prior to the enactment of Public Law 115-97 with the modifications specified in this section".
  • Pre-TCJA 26 U.S.C. 213(a) (2016 edition, govinfo): "to the extent that such expenses exceed 10 percent of adjusted gross income". Pre-TCJA § 213(f) substituted 7.5% only for filers 65 and over, and only for tax years "ending before January 1, 2017", so 10% applies to everyone from 2018.
  • Pub. L. 115-97 § 11027 (govinfo) rewrote § 213(f) to substitute 7.5% for tax years beginning after December 31, 2016 and ending before January 1, 2019. That is where the federal 7.5% comes from; NY does not follow it.
  • Form IT-196 line 3 reads "Multiply line 2 by 10% (0.10)" on every fill-in form from 2018 through 2025. I checked each one: https://www.tax.ny.gov/pdf/<year>/inc/it196_<year>_fill_in.pdf#page=1 for 2018, 2019, 2020, 2021, 2022, 2023, 2024 and 2025. Line 2 is "Enter amount from Form IT-201 or IT-203, line 19" (federal AGI), except 2020–2022, where it is line 19a. Line 19a is recomputed federal AGI: line 19 plus Form IT-558 adjustments (2021 IT-201-I, PDF p. 14), which PolicyEngine does not model, so federal AGI is used for every year.
  • IT-196-I line 1 (2025, p. 2): enter the amount from federal Schedule A line 1, so the expense base is the federal one (itemized_medical_expenses).

Changes

  • parameters/gov/states/ny/tax/income/deductions/itemized/medical/floor.yaml: 0.1 from 2018-01-01, the first year of § 615(a)'s pre-TCJA reference and of Form IT-196. It cites § 615(a), pre-TCJA § 213(a), and IT-196 line 3 for 2018 and 2025.
  • variables/.../itemized/pre_tcja/ny_medical_expense_deduction.py: max_(0, itemized_medical_expenses - floor * positive_agi), NY only. This mirrors the federal formula and the sibling ny_misc_deduction and ny_casualty_loss_deduction.
  • ny_itemized_deductions_max: adds ny_medical_expense_deduction instead of medical_expense_deduction.
  • ny_itemized_deductions_phase_out: worksheet line 2 (deductions not subject to the limitation) uses ny_medical_expense_deduction. Medical enters lines 1 and 2 equally, so it still cancels out of line 3. If line 2 had kept the federal amount, line 3 would come out understated by the floor difference.

Other NY consumers: I grepped all of policyengine_us/ for medical_expense_deduction and itemized_medical_expenses. The only NY consumers were these two variables, and no NY reform, contrib module, parameter list, NYC or Yonkers code reads them.

Tests

  • ny_pre_tcja_deductions.yaml, "NY itemized deductions max includes all pre-TCJA components": medical 10,000 at AGI 100,000 was 10,000 − 7,500 = 2,500. Under IT-196 it is 10,000 − 10,000 = 0, so max goes from 26,500 to 24,000.
  • ny_itemized_deductions_phase_out.yaml: the medical_expense_deduction inputs are renamed to ny_medical_expense_deduction. Expected values are unchanged.
  • New ny_medical_expense_deduction.yaml (10 cases), every expected value computed from IT-196 lines 1–4:
    • the floor in 2018, 2025 and 2026, side by side with the federal amount;
    • the 7.5%–10% band (NY 0, federal positive);
    • exactly 10% (0) and 10% plus $1 (1);
    • negative AGI (floor 0);
    • a non-NY filer;
    • two integration cases: ny_itemized_deductions 13,000, and the Line 40 worksheet with medical in both lines (reduction 120,000).
  • These tests fail on main. I swapped only the model code back to upstream/main:
    • ny_itemized_deductions_max came out 15,000 against 13,000, and 375,000 against 250,000;
    • the components case came out 26,500 against 24,000.
  • Mutation check: with only the phase-out reverted to the federal deduction, the Line 40 case gives 20,000 against 120,000.

Invariants (tests/core/test_ny_medical_expense_deduction_invariants.py, Hypothesis plus a seeded 200-unit population, 2018/2021/2024/2025)

These hold for every tax unit, single or joint, in NY or elsewhere, with AGI from −200k to 6M:

  1. Bound: 0 ≤ ny_medical_expense_deduction ≤ itemized_medical_expenses, and the deduction is 0 outside NY.
  2. Differential: the deduction equals a numpy max(0, E − 0.10 × max(AGI, 0)), with 0.10 taken from IT-196 line 3, not from the model's parameter.
  3. Order: it is never more than the federal medical_expense_deduction.
  4. Monotone: raising expenses by d raises the deduction by between 0 and d.
  5. Composition: ny_itemized_deductions_max − ny_medical_expense_deduction doesn't depend on medical expenses.
  6. Line 40 invariance: ny_itemized_deductions_phase_out doesn't depend on medical expenses.

Comparisons allow 1e-6 relative error, because the model stores float32. Mutation check: a 0.075 floor breaks invariant 2, and a phase-out left on the federal deduction breaks invariant 6.

Ran locally by the author, targeted files only: the three YAML files and the invariants file passed. At this head the YAML files hold 12 (ny_medical_expense_deduction.yaml), 6 (ny_pre_tcja_deductions.yaml) and 7 (ny_itemized_deductions_phase_out.yaml) cases; the independent review counted six collected tests in the invariants file. CI runs all of them at this head. These code-health and metadata files also pass: test_known_dead_reference_urls, test_no_concatenated_reference_urls, test_reference_urls, test_rest_python_groups, test_parameter_files and test_build_metadata.

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base 6b80a8e447 against this PR's head b1cad7c17e, 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)
ny_itemized_deductions -2.46bn 733 (2,430,412)
ny_taxable_income +807.52M 156 (357,703)
ny_income_tax +46.23M 155 (357,703)
state_income_tax +46.23M 155 (357,703)
income_tax -1.29M 39 (71,157)
household_net_income -57.96M 154 (357,703)
household_benefits +0 0 (0)

ny_medical_expense_deduction is new in this PR, so it has no base-side value to compare; its effect shows in ny_itemized_deductions.

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

Impact

From 2018, for NY itemizers with medical expenses above 7.5% of federal AGI, the deduction falls by min(expenses − 7.5% × A, 2.5% × A), where A = max(federal AGI, 0); when A is 0 nothing changes. The floor parameter is 10% from 2013, 7.5% for 2017 (the federal Schedule A floor NY then followed) and 10% from 2018 (pre-TCJA 26 U.S.C. 213(a), Form IT-196 line 3). This corrects the model to the statute and the form.

Overlap with other branches

#9912 edits the same case in ny_pre_tcja_deductions.yaml: casualty becomes (15,000 − 100) − 10,000 = 4,900, and max goes from 26,500 to 26,400. Whichever PR merges second needs a rebase. With both applied the case is 5,000 + 0 + 12,000 + 2,000 + 4,900 = 23,900, with comment lines # NY Medical (Form IT-196 lines 1-4): 10,000 - (100,000 * 0.10) = 0 and # NY Casualty: (15,000 - 100) - (100,000 * 0.10) = 4,900.

The hub's Form 4952 branch wip/hub-fix-163d-r2 (no PR yet) edits lines next to this PR in three files: ny_itemized_deductions_phase_out.py, ny_itemized_deductions_phase_out.yaml and ny_pre_tcja_deductions.yaml (git merge-tree reports conflicts). Each resolution keeps both sides:

  • the medical lines become ny_medical_expense_deduction, and the investment interest lines become investment_interest_expense_deduction;
  • that branch's new miscellaneous-pool case is appended after the corrected 24,000 case.

The NY-itemizing branch fix-ny-itemizes-federal-gate touches none of these files.

axiom: TheAxiomFoundation/rulespec-us#1525 queued (N.Y. Tax Law § 615(a) medical component; blocked on corpus ingestion of pre-TCJA 26 U.S.C. 213 and Form IT-196, and on a signed-encoder run)

🤖 Generated with Claude Code

NY Tax Law § 615(a) defines NY itemized deductions as federal deductions
"as such deductions existed immediately prior to the enactment of Public
Law 115-97". Pre-TCJA 26 U.S.C. 213(a) allowed medical expenses above 10%
of AGI; TCJA § 11027 set the federal 7.5% floor, which NY does not follow.
Form IT-196 line 3 multiplies federal AGI by 10% on every form from 2018
through 2025.

ny_itemized_deductions_max and the Line 40 limitation used the federal
medical_expense_deduction (7.5% floor since 2017). Add
ny_medical_expense_deduction (10% of positive AGI, from a new NY floor
parameter) and use it in both, so the medical deduction still cancels
between worksheet lines 1 and 2.

Fix the pre-TCJA components test (medical 2,500 -> 0, max 26,500 ->
24,000), move the phase-out tests' medical input to the NY variable, and
add boundary, year, non-NY and integration cases plus Hypothesis
invariants (bound, differential against the published 10%, order against
the federal deduction, monotonicity, composition, Line 40 invariance).

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

Review of #9931: the parameter backdated 10% to 2017, when Form IT-201-D
line 1 still took federal Schedule A line 4 (7.5% under Pub. L. 115-97
§ 11027). The floor is now 0.1 from 2013, 0.075 for 2017, and 0.1 from
2018. Added 2016 and 2017 cases.

The invariants test scaled float32 slack by the result, so expenses within
rounding of a large floor failed. Slack now scales with each comparison's
largest operand, with the reviewer's counterexamples pinned as @example and
2017 added to the published-floor years.

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

codecov Bot commented Oct 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (909176a) to head (b1cad7c).
⚠️ Report is 266 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9931   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         3    -1     
  Lines           76        53   -23     
  Branches         2         0    -2     
=========================================
- Hits            76        53   -23     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MaxGhenis
MaxGhenis merged commit f86c343 into main Oct 11, 2026
37 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#9931 at b1cad7c17e661ce048642e5aad4a445e4bb65765 (squash): Apply New York's pre-TCJA 10% medical expense floor (Form IT-196 line 3).

  • Review: independent GPT-6.1 Sol review approves this exact head, with no P1 or P2 (reviews-gen/us-9931/review-r1.md). Its methodology flag is an approximation main already makes and this PR keeps; the body discloses it, and the PR does not change it relative to main.
  • Inherited approximation: federal AGI stands in for IT-201 line 19a's recomputed AGI in 2020-2022 (IT-558 adjustments are not modeled), as on main.
  • Body: the hub corrected the historical-floor and impact-formula lines and the case counts the review flagged (P3).
  • Follow-up (P3): downstream NY consumers (additional reductions, ny_itemizes, NYC, Yonkers) lack direct assertions for the new deduction.
  • Order: Apply the Form 4684 $100 per-casualty reduction and drop dependents' losses in the federal, NY, AL and HI casualty deductions #9912 edits the same ny_pre_tcja_deductions.yaml case; whichever lands second rebases (the body gives the combined value, 23,900).
  • Impact: a hub microsimulation on the default dataset for 2026, merge base against this head; see the body's "Hub microsimulation impact (current head)" section.
  • Gates checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head concluded success;
    • MERGEABLE, not a draft, no CHANGES_REQUESTED;
    • the semantic-overlap guard;
    • head pinned.
  • Axiom: the body's axiom: line.
  • --admin: used only because a required approving review is the sole block.

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