Repository navigation
Count Ohio premiums on medical worksheet lines 1 and 3 (R.C. 5747.01(A)(10)) - #10020
Merged
Merged
Conversation
oh_unreimbursed_medical_care_expense_deduction_person listed the TaxUnit variable oh_insured_unreimbursed_medical_care_expenses in its adds. policyengine-core evaluates adds with map_to="person", which projects a group value onto every member, so each member deducted the full worksheet line 8 amount and Ohio AGI (the sum over members) fell by n times it. Each spouse's joint filing credit qualifying income also lost the whole amount. Use the existing person-level oh_insured_unreimbursed_medical_care_expenses_person, which prorates line 8 by each member's share of line 5 and sums to the return amount. Also enter federal AGI below zero as zero on worksheet line 6, as every year's instructions say, so line 8 never exceeds line 5. Adds YAML tests that fail on main (person split, total once, Ohio AGI, joint filing credit, negative AGI) and Hypothesis invariants (conservation, an independent worksheet, bounds, once-only accounting). Updates the 2026 retired-couple case, whose Medicare Part B line 8 had been deducted twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The invariants do not vary by year, so run Hypothesis on 2022 and 2025 with four examples and the seeded population on 2024 only. Test time falls from about 50 s to 15 s locally; both bugs still fail it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds failing cases for PolicyBench scenario_025 and for the line 1/line 3 split: line 3 takes premiums of anyone eligible for Medicare OR an employer-paid plan, and line 1 reads the premium inputs the data fills. Corrects existing expectations that encoded the AND gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Line 3 took premiums only of a person who was Medicare-eligible AND whose employer paid none of them; the statute and worksheet say eligible for Medicare OR an employer-paid plan. Line 1 read health_insurance_premiums, an input nothing fills, so premiums reported through the decomposed inputs (the data path) reached neither line. Together these zeroed the premium deduction for PolicyBench scenario_025. - oh_employer_subsidized_health_plan_eligible: the return is eligible when the head or spouse has employer coverage, an offer of it, or an employer paying some or all of their premiums. - oh_medical_care_insurance_premiums: the IRC 213 premium base the federal deduction uses, less the federal self-employed health insurance deduction (worksheet line 1 note). - Lines 1 and 3 split those premiums on Medicare or employer-plan eligibility; the property test checks the partition. - Cite the live 2025 booklet in place of dead tax.ohio.gov/static links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Line 44 heading starts at the foot of PDF p. 25; the operative text, including the pre-tax exclusion, is on p. 26. Adds the Department's FAQ (Q8 and Q9: after-tax premiums for an employer plan go on line 3). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #10020 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 4 6 +2
Lines 76 86 +10
Branches 2 0 -2
=========================================
+ Hits 76 86 +10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
Author
|
Handing off to the US + core hub.
Nothing is pending from the opening session. |
Contributor
Author
|
US + core hub merge audit, us#10020 at
|
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the defect behind PolicyBench scenario_025: Ohio's unreimbursed medical care worksheet never counted health insurance premiums that households report through the populated premium inputs.
Follows #9925 (one deduction per return, AGI floored at zero), merged as
f9f3887431. This branch was started on #9925's head before it merged and has since merged main, so the diff holds only this fix.The defect
R.C. 5747.01(A)(10)(a) gives a full deduction for premiums, except to "any taxpayer who is eligible to participate in any subsidized health plan maintained by any employer of the taxpayer or of the taxpayer's spouse, or to any taxpayer who is entitled to ... benefits under part A" of Medicare. Division (b) still counts those premiums as medical care above 7.5% of federal AGI. The 2025 worksheet (p. 41) splits them:
Ohio's FAQ (Q8, Q9) puts after-tax premiums for an employer plan, or for private coverage bought instead of a spouse's employer plan, on line 3. Q2 and the Line 44 instructions (pp. 25-26) exclude premiums paid with pre-tax dollars.
The model had two errors:
health_insurance_premiums, a direct input whose formula (addsof the decomposed premiums) Decompose SPM MOOP premiums #8178 removed. Microdata and most household inputs report premiums throughhealth_insurance_premiums_without_medicare_part_b, so line 1 was zero for them.For scenario_025 (head 61 with $6,500 of premiums and $800 of other medical expenses, both spouses with employer coverage), the $6,500 reached neither line, and the deduction was zero.
The fix
oh_medical_care_insurance_premiums(new, Person): the IRC 213 premium base the federal medical deduction already uses (medical_expense_health_insurance_premiums: the direct input if given, otherwise non-Medicare premiums plus the modeled Part B premium for enrollees), less the person's federal self-employed health insurance deduction (worksheet line 1 note). Pre-tax payroll premiums are a separate input excluded from wages, so they are not in this base.oh_employer_subsidized_health_plan_eligible(new, TaxUnit): true when the head or spouse has employer coverage (has_esi), an offer of it (offered_aca_disqualifying_esi), or an employer that pays some or all of their premiums. A dependent's own employer does not count; the statute names the taxpayer's or spouse's employer.oh_uninsured_unreimbursed_medical_care_expenses) takes a person's premiums when they are eligible for neither Medicare nor such a plan; line 3 (inoh_insured_unreimbursed_medical_care_expense_amount) takes them otherwise.tax.ohio.gov/static2022 booklet links with the live 2025 booklet and adds the FAQ.Invariants
Stated for every input and checked by the Hypothesis test in
tests/core/test_oh_unreimbursed_medical_deduction_invariants.py, which now draws Medicare eligibility, employer coverage, offers and the employer contribution category:Tests
YAML tests first (
97a3b7ed06), then the fix (38b67fafdc):oh_unreimbursed_medical_care_expense_deduction.yaml), with the scenario's exact inputs: line 3 = 6,500, line 5 = 7,300, line 8 = 7,300 − 0.075 × 94,925.29 = 180.60, Ohio AGI 94,744.69, taxable income 90,944.69 after 2 × $1,900 exemptions, and tax before refundable credits = 332 + 2.75% × (90,944.69 − 26,050) − 200 retirement credit = 1,916.60 (was 1,921.57). The audit's 180.63 rounds federal AGI to 94,925; with the exact 94,925.29 the line is 180.60.other_health_insurance_premiumsare not counted again.oh_insured_unreimbursed_medical_care_expense_amount.yaml; 0 → 350 and 950 → 1,150 inintegration.yaml).Local runs (macOS, Python 3.12; local results, not Linux CI). The first is on the merged head; the others ran on the pre-merge head
9bb7314d57, whose Ohio files are identical:policyengine-core testoverdeductions/medical_expense,integration.yamlandintegration_2025.yaml: 63 passed.oh_insured_unreimbursed_medical_care_expense_amount[800, 0] instead of [7,300, 0].pytest tests/core/test_oh_unreimbursed_medical_deduction_invariants.py: 2 passed in 202 s on a machine at load average about 60, with peak RSS 1.6 GB.Hub microsimulation impact (current head)
A real microsimulation on the default dataset, main at the merge base
f9f3887431against this PR's head5897e94c23, for 2025 and 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.oh_unreimbursed_medical_care_expense_deductionoh_income_taxstate_income_taxincome_taxhousehold_net_incomehousehold_benefitsMost of the change goes the direction the PR describes. Premiums that fell on worksheet line 1 for filers with neither an employer plan nor Medicare were previously zero, and after-tax premiums above the 7.5% floor now count on line 3. Ohio income tax falls about $134M a year. In each year 29 records' deduction falls and 23 tax units pay more Ohio tax. The hub hasn't traced those records one by one.
oh_medical_care_insurance_premiumsexists only on this branch, so the table doesn't compare it.Comparison files are in
fixes/10020-impact/on the hub host.Impact
This lowers Ohio income tax for two groups: filers with an employer plan or Medicare who pay after-tax premiums above the 7.5% floor (line 3), and filers with neither who report premiums through the decomposed inputs (line 1, previously zero for them). I did not run a microsimulation, because the machine was under heavy load, so the aggregate change is not measured.
Not changed
has_esi) or an offer of it counts as a subsidized plan even whenemployer_contribution_to_health_insurance_premiums_categoryis NONE. NONE is that variable's default, so it cannot tell "the employer pays nothing" from "not reported". An offer that is not ACA-disqualifying is not modeled, so such an offer does not bar line 1.self_employed_health_insurance_premiumsstill reads only the directhealth_insurance_premiumsinput, so the self-employed reduction applies only when premiums are entered that way. That is a federal-side question outside this PR.axiom: R.C. 5747.01(A)(10) TheAxiomFoundation/rulespec-us#1523 queued
🤖 Generated with Claude Code