From ccbb0273eeb263f9d1c433837198530aa921bada Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 10:30:12 -0400 Subject: [PATCH 1/3] Count dependents' other medical expenses in Idaho's claimant worksheet Form 39R worksheet lines 3-6 take the medical expenses claimed on federal Schedule A, and Schedule A line 1 counts bills the filers paid for their dependents. other_medical_expenses sits on the person cared for (the CPS ASEC asks each person's own care, paid by anyone in the household), so a dependent's amount is the filers' payment. The worksheet summed it over the head and spouse only, which dropped every dependent's bills from the premium overlap and from Idaho itemized deductions. Premiums stay filer-only. Co-Authored-By: Claude Opus 5.5 --- .../id-worksheet-dependent-medical.fixed.md | 1 + .../core/test_id_health_insurance_premiums.py | 185 +++++++++++++++++- .../id/tax/income/id_itemized_deductions.yaml | 98 ++++++++++ ...health_insurance_premiums_subtraction.yaml | 60 +++++- .../deductions/id_itemized_deductions.py | 6 +- ...th_insurance_premiums_medical_deduction.py | 24 ++- 6 files changed, 351 insertions(+), 23 deletions(-) create mode 100644 changelog.d/id-worksheet-dependent-medical.fixed.md diff --git a/changelog.d/id-worksheet-dependent-medical.fixed.md b/changelog.d/id-worksheet-dependent-medical.fixed.md new file mode 100644 index 00000000000..8d596e91c7d --- /dev/null +++ b/changelog.d/id-worksheet-dependent-medical.fixed.md @@ -0,0 +1 @@ +Count every tax-unit member's other medical expenses, including bills the filers paid for dependents, in Idaho's Form 39R claimant medical worksheet, as federal Schedule A does. diff --git a/policyengine_us/tests/core/test_id_health_insurance_premiums.py b/policyengine_us/tests/core/test_id_health_insurance_premiums.py index 7fa88266780..117759d4d1e 100644 --- a/policyengine_us/tests/core/test_id_health_insurance_premiums.py +++ b/policyengine_us/tests/core/test_id_health_insurance_premiums.py @@ -249,10 +249,8 @@ def test_deleted_medical_input_recalculated_federal_first(): "dependent": { "age": {year: 20}, "is_tax_unit_dependent": {year: True}, - # Other costs, which the federal subtotal counts for every - # member and Idaho's claimant worksheet leaves out, keep - # the two amounts apart. A dependent's own premiums would - # not: neither counts them. + # The federal subtotal and Idaho's claimant worksheet + # both count a dependent's other costs. "other_medical_expenses": {year: 500}, }, }, @@ -262,6 +260,10 @@ def test_deleted_medical_input_recalculated_federal_first(): "adjusted_gross_income": {year: 30_000}, "standard_deduction": {year: 15_750}, "id_itemized_deductions": {year: 20_000}, + # From derived inputs the claimant worksheet equals the + # federal deduction. A supplied Idaho premium amount below + # the 3,000 Schedule A premiums keeps the two apart. + "id_qualified_health_insurance_premiums": {year: 2_000}, } }, "households": { @@ -277,9 +279,176 @@ def test_deleted_medical_input_recalculated_federal_first(): # Federal first: 3,000 + 500 - 30,000 * 7.5% = 1,250. assert simulation.calculate("medical_expense_deduction", year).tolist() == [1_250] - # Idaho excludes the dependent's costs: medical overlap is - # 3,000 - 30,000 * 7.5% = 750, leaving 3,000 - 750 = 2,250. - # Core's provenance defect before 3.32.27 uses 1,250 and returns 1,750. + # Idaho's claimant overlap is 2,000 + 500 - 30,000 * 7.5% = 250, leaving + # 2,000 - 250 = 1,750. Core's provenance defect before 3.32.27 uses the + # 1,250 federal deduction and returns 2,000 - 1,250 = 750. assert simulation.calculate( "id_health_insurance_premiums_subtraction", year - ).tolist() == [2_250] + ).tolist() == [1_750] + + +# Form 39R worksheet lines 1-6 take the medical expenses claimed on federal +# Schedule A, which counts bills the filers paid for dependents. Premium inputs +# sit on the payer and other medical expenses on the person cared for, so the +# claimant worksheet counts the filers' premiums and every member's other +# costs. Each drawn return runs in one vectorized simulation per year with +# these variants: +VARIANTS = ( + "drawn", + # every member's other costs recorded on the head; + "bills_on_head", + # every member's other costs recorded on one dependent; + "bills_on_dependent", + # one dependent's other costs raised; + "dependent_bills_raised", + # every dependent's own premiums set to 0. + "no_dependent_premiums", +) +# The drawn high incomes reach the 2026 itemized limitation (IRC 68). +CLAIMANT_YEARS = (2025, 2026) +IDAHO_OUTPUTS = ( + "id_health_insurance_premiums_medical_deduction", + "id_qualified_health_insurance_premiums", + "id_itemized_deductions", + "id_itemizes", + "id_health_insurance_premiums_subtraction", + "id_deductions", +) +CENT = 0.01 + + +@st.composite +def claimant_returns(draw): + filers = draw(st.sampled_from([1, 2])) + dependents = draw(st.integers(1, 2)) + members = filers + dependents + return { + "filers": filers, + "premiums": [draw(st.integers(0, 20_000)) for _ in range(members)], + "other": [draw(st.integers(0, 40_000)) for _ in range(members)], + "agi": draw(st.integers(0, 150_000) | st.integers(600_000, 900_000)), + "interest": draw(st.integers(0, 40_000)), + "above_line": draw(st.integers(0, 25_000)), + "receiver": filers + draw(st.integers(0, dependents - 1)), + "raise": draw(st.integers(1, 10_000)), + } + + +def _claimant_inputs(case, variant): + premiums = list(case["premiums"]) + other = list(case["other"]) + if variant == "bills_on_head": + other = [sum(other)] + [0] * (len(other) - 1) + elif variant == "bills_on_dependent": + moved = [0] * len(other) + moved[case["receiver"]] = sum(other) + other = moved + elif variant == "dependent_bills_raised": + other[case["receiver"]] += case["raise"] + elif variant == "no_dependent_premiums": + premiums[case["filers"] :] = [0] * (len(premiums) - case["filers"]) + return premiums, other + + +def _claimant_simulation(cases, year): + people, tax_units, households, rows = {}, {}, {}, [] + for index, case in enumerate(cases): + for variant in VARIANTS: + unit = f"case_{index}_{variant}" + premiums, other = _claimant_inputs(case, variant) + members = [f"{unit}_{member}" for member in range(len(premiums))] + for member, person in enumerate(members): + filer = member < case["filers"] + people[person] = { + "age": {year: 40 if filer else 10}, + "is_tax_unit_head": {year: member == 0}, + "is_tax_unit_spouse": {year: member == 1 and filer}, + "is_tax_unit_dependent": {year: not filer}, + "health_insurance_premiums": {year: premiums[member]}, + "other_medical_expenses": {year: other[member]}, + } + tax_units[unit] = { + "members": members, + "adjusted_gross_income": {year: case["agi"]}, + "self_employed_health_insurance_ald": {year: case["above_line"]}, + "interest_deduction": {year: case["interest"]}, + "salt_deduction": {year: 0}, + "id_salt_deduction": {year: 0}, + } + households[unit] = {"members": members, "state_code": {year: "ID"}} + rows.append((index, variant)) + simulation = Simulation( + tax_benefit_system=SYSTEM, + situation={ + "people": people, + "tax_units": tax_units, + "households": households, + }, + ) + return simulation, rows + + +@settings(max_examples=8, deadline=None, derandomize=True) +@given(cases=st.lists(claimant_returns(), min_size=2, max_size=3)) +def test_claimant_worksheet_counts_bills_paid_for_dependents(cases): + for year in CLAIMANT_YEARS: + simulation, rows = _claimant_simulation(cases, year) + floor = SYSTEM.parameters(year).gov.irs.deductions.itemized.medical.floor + values = { + # id_itemizes compares as 0 or 1. + variable: dict( + zip(rows, simulation.calculate(variable, year).astype(float)) + ) + for variable in IDAHO_OUTPUTS + + ("medical_expense_deduction", "itemized_taxable_income_deductions") + } + for index, case in enumerate(cases): + + def value(variable, variant="drawn"): + return values[variable][(index, variant)] + + worksheet = value("id_health_insurance_premiums_medical_deduction") + # 1. Differential: an independent oracle, the federal Schedule A + # deduction, and Idaho's worksheet agree. + filer_premiums = sum(case["premiums"][: case["filers"]]) + oracle = max( + 0, + max(0, filer_premiums - case["above_line"]) + + sum(case["other"]) + - floor * max(0, case["agi"]), + ) + assert worksheet == pytest.approx(oracle, abs=CENT), case + assert value("medical_expense_deduction") == pytest.approx( + worksheet, abs=CENT + ), case + # 2. Idaho's medical correction leaves a derived federal itemized + # total unchanged: the two medical deductions agree. + assert value("id_itemized_deductions") == pytest.approx( + value("itemized_taxable_income_deductions"), abs=CENT + ), case + # 3. Relocation: whose input records a bill changes no Idaho + # amount, and neither do a dependent's own premiums. + for variant in ( + "bills_on_head", + "bills_on_dependent", + "no_dependent_premiums", + ): + for variable in IDAHO_OUTPUTS: + assert value(variable, variant) == pytest.approx( + value(variable), abs=CENT + ), (variable, variant, case) + # 4. Monotonicity: more bills for a dependent never lower the + # worksheet. + assert ( + value( + "id_health_insurance_premiums_medical_deduction", + "dependent_bills_raised", + ) + >= worksheet - CENT + ), case + # 5. Bounds: the subtraction stays within the qualified premiums. + assert ( + -CENT + <= value("id_health_insurance_premiums_subtraction") + <= value("id_qualified_health_insurance_premiums") + CENT + ), case diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/id_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/id_itemized_deductions.yaml index a4e5bbfee22..6402cdb20f5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/id_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/id_itemized_deductions.yaml @@ -55,6 +55,66 @@ id_health_insurance_premiums_subtraction: 2_250 id_taxable_income: 7_000 +- name: A bill the parent paid counts the same on the parent's input or the child's + # Other medical expenses sit on the person cared for, so unit a records the + # child's 3,000 bill on the parent and unit b on the child. Schedule A + # counts both, and Form 39R worksheet lines 3-6 take the Schedule A amounts. + # Medical deduction: 1,000 + 3,000 - 7.5% of 30,000 = 1,750. + # Itemized: 20,000 + 1,750 = 21,750; it beats standard 15,750 + 1,000. + # Subtraction: 1,000 - min(1,000, 1,750) = 0. + # Taxable income: 30,000 - 21,750 - 0 = 8,250. + # Leaving out the child's bill would take 1,750 off unit b's itemized + # deductions and restore its 1,000 subtraction: taxable income 9,000. + period: 2025 + input: + people: + a_parent: + age: 40 + health_insurance_premiums: 1_000 + other_medical_expenses: 3_000 + is_tax_unit_head: true + a_child: + age: 10 + is_tax_unit_dependent: true + b_parent: + age: 40 + health_insurance_premiums: 1_000 + is_tax_unit_head: true + b_child: + age: 10 + other_medical_expenses: 3_000 + is_tax_unit_dependent: true + tax_units: + a: + members: [a_parent, a_child] + adjusted_gross_income: 30_000 + interest_deduction: 20_000 + salt_deduction: 0 + id_salt_deduction: 0 + standard_deduction: 15_750 + b: + members: [b_parent, b_child] + adjusted_gross_income: 30_000 + interest_deduction: 20_000 + salt_deduction: 0 + id_salt_deduction: 0 + standard_deduction: 15_750 + households: + a: + members: [a_parent, a_child] + state_code: ID + b: + members: [b_parent, b_child] + state_code: ID + output: + medical_expense_deduction: [1_750, 1_750] + id_health_insurance_premiums_medical_deduction: [1_750, 1_750] + itemized_taxable_income_deductions: [21_750, 21_750] + id_itemized_deductions: [21_750, 21_750] + id_itemizes: [true, true] + id_health_insurance_premiums_subtraction: [0, 0] + id_taxable_income: [8_250, 8_250] + - name: Corrected Idaho medical costs also reduce the 2026 itemized limitation # P.L. 119-21 sec. 70111: $70,000 * (1 - 2/37) = $66,216.21622. # Subtracting $500 from the already-limited federal total would be too low. @@ -92,6 +152,44 @@ id_itemizes: true id_health_insurance_premiums_subtraction: 60_000 +- name: A dependent's bills stay in the 2026 limited Idaho itemized total + # Medical deduction: 40,000 + the child's 30,000 - 7.5% of 800,000 = 10,000. + # P.L. 119-21 sec. 70111: (30,000 + 10,000) * (1 - 2/37) = 37,837.83784. + # Leaving out the child's bills would take the 10,000 off before the + # limitation and give 28,378.37838. + # Subtraction: 40,000 - min(40,000, 10,000) = 30,000. + absolute_error_margin: 0.01 + period: 2026 + input: + people: + head: + age: 40 + health_insurance_premiums: 40_000 + is_tax_unit_head: true + child: + age: 10 + other_medical_expenses: 30_000 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: SINGLE + adjusted_gross_income: 800_000 + interest_deduction: 30_000 + salt_deduction: 0 + id_salt_deduction: 0 + households: + household: + members: [head, child] + state_code: ID + output: + medical_expense_deduction: 10_000 + id_health_insurance_premiums_medical_deduction: 10_000 + itemized_taxable_income_deductions: 37_837.83784 + id_itemized_deductions: 37_837.83784 + id_itemizes: true + id_health_insurance_premiums_subtraction: 30_000 + - name: Idaho does not add back the federal foreign tax credit when it is claimed as a credit period: 2025 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_subtraction.yaml index fffa2b999bf..a09558def91 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_subtraction.yaml @@ -282,8 +282,16 @@ id_itemizes: true id_health_insurance_premiums_subtraction: 2_000 -- name: Claimant medical overlap includes spouse expenses and excludes dependent expenses - # Claimants paid $5,000 premiums + $2,000 other costs; the floor is $6,000. +- name: Claimant medical overlap counts bills for a dependent but not the dependent's own premiums + # 2025 Form 39R worksheet, PDF page 49, lines 1-8: lines 1-6 take the + # Schedule A amounts, and Schedule A line 1 counts bills the filers paid for + # dependents. Other medical expenses sit on the person cared for. + # Premiums: the filers' 3,000 + 2,000 = 5,000; the dependent's own 500 is + # not theirs. Other costs: 1,000 + 1,000 + the dependent's 2,000 = 4,000. + # Medical deduction: 5,000 + 4,000 - 7.5% of 80,000 = 3,000. + # Subtraction: 5,000 - min(5,000, 3,000) = 2,000. Leaving out the + # dependent's bills would give 4,000. + # Itemized route 40,000 + 2,000 = 42,000 beats standard 31,500 + 5,000. period: 2025 input: people: @@ -300,7 +308,7 @@ dependent: age: 20 health_insurance_premiums: 500 - other_medical_expenses: 4_000 + other_medical_expenses: 2_000 is_tax_unit_dependent: true tax_units: tax_unit: @@ -313,12 +321,48 @@ members: [head, spouse, dependent] state_code: ID output: - # Federal: the filers' 5,000 of premiums and every member's 6,000 of - # other costs, less the 6,000 floor; not the dependent's own 500. - medical_expense_deduction: 5_000 - id_health_insurance_premiums_medical_deduction: 1_000 + medical_expense_deduction: 3_000 + id_qualified_health_insurance_premiums: 5_000 + id_health_insurance_premiums_medical_deduction: 3_000 + id_itemizes: true + id_health_insurance_premiums_subtraction: 2_000 + +- name: A bill a parent paid for a dependent's care enters the claimant worksheet + # Schedule A line 1 counts "medical and dental bills you paid ... for ... + # All dependents you claim on your return" (2025 instructions), and Form + # 39R worksheet lines 3-6 take those Schedule A amounts. + # Medical deduction: 3,000 + 1,500 - 7.5% of 30,000 = 2,250. + # Subtraction: 3,000 - min(3,000, 2,250) = 750. Leaving out the child's + # bills would give a 750 deduction and a 2,250 subtraction. + # Itemized route 20,000 + 750 = 20,750 beats standard 15,750 + 3,000. + # Taxable income: 30,000 - 20,000 - 750 = 9,250. + period: 2025 + input: + people: + head: + age: 40 + health_insurance_premiums: 3_000 + is_tax_unit_head: true + child: + age: 10 + other_medical_expenses: 1_500 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + adjusted_gross_income: 30_000 + standard_deduction: 15_750 + id_itemized_deductions: 20_000 + households: + household: + members: [head, child] + state_code: ID + output: + medical_expense_deduction: 2_250 + id_health_insurance_premiums_medical_deduction: 2_250 id_itemizes: true - id_health_insurance_premiums_subtraction: 4_000 + id_health_insurance_premiums_subtraction: 750 + id_taxable_income: 9_250 - name: Derived claimant medical overlap uses premiums excluding pretax payroll payments # Pretax payroll premiums are separate; remove only the Schedule 1 amount. diff --git a/policyengine_us/variables/gov/states/id/tax/income/deductions/id_itemized_deductions.py b/policyengine_us/variables/gov/states/id/tax/income/deductions/id_itemized_deductions.py index 1311e8e97cf..026ec50548e 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/deductions/id_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/id/tax/income/deductions/id_itemized_deductions.py @@ -43,8 +43,10 @@ def formula(tax_unit, period, parameters): ) medical_adjustment = federal_medical - claimant_medical - # Correct the medical component of derived itemized totals as well as - # premium overlap, so dependent-paid costs do not remain deducted here. + # Use the claimant's medical deduction in derived itemized totals, as + # the premium overlap does. From derived inputs it equals the federal + # deduction; they differ only when a caller supplies an Idaho amount + # or an intermediate federal one. limitation_adjustment = 0 p = parameters(period).gov.irs.deductions.itemized.limitation if p.applies and p.obbb.applies: diff --git a/policyengine_us/variables/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_medical_deduction.py b/policyengine_us/variables/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_medical_deduction.py index 04860677ccf..5195c616af9 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_medical_deduction.py +++ b/policyengine_us/variables/gov/states/id/tax/income/subtractions/id_health_insurance_premiums_medical_deduction.py @@ -13,11 +13,20 @@ class id_health_insurance_premiums_medical_deduction(Variable): # PDF pages 48-49 (Form 39R, line 18 and worksheet lines 1-8). "https://tax.idaho.gov/wp-content/uploads/forms/EIN00046/EIN00046_03-02-2026.pdf#page=48", "https://www.irs.gov/instructions/i1040sca", + # CPS ASEC wording of the premium and medical care questions, PDF + # pages 2-3. + "https://www.census.gov/content/dam/Census/library/working-papers/2015/demo/Medical-Out-of-pocket-Expenses-CPSASEC-2013-2014.pdf#page=2", ) documentation = ( "Medical deduction used to allocate the claimant's health premium " - "overlap. Only costs paid by the head and spouse enter the medical " - "floor calculation, including their payments covering dependents. " + "overlap. Form 39R worksheet lines 1-6 take the medical expenses " + "claimed on federal Schedule A, which include bills the claimant paid " + "for dependents. Premium inputs sit on the payer, so only the head's " + "and spouse's premiums count, including coverage they bought for " + "dependents; a dependent's own premium payments do not. Other medical " + "expenses sit on the person cared for, including amounts other " + "household members paid on that person's behalf, so every member's " + "count. " "Premium inputs already exclude pretax payroll premiums; premiums " "deducted on Schedule 1 are removed first. The " "actual federal medical deduction limits the derived result. A " @@ -44,13 +53,18 @@ def formula(tax_unit, period, parameters): federal_medical = max_(0, tax_unit("medical_expense_deduction", period)) if supplied_medical: return federal_medical + # Qualified premiums are the head's and spouse's own payments. premiums = tax_unit("id_qualified_health_insurance_premiums", period) + # Form 39R lines 3-6 take the Schedule A bills, which include those + # the claimant paid for dependents. Other medical expenses sit on the + # person cared for, so a dependent's are the filers' payments. other_medical = tax_unit_non_dep_add( - tax_unit, period, ["other_medical_expenses"] + tax_unit, + period, + ["other_medical_expenses"], + include_dependents=["other_medical_expenses"], ) p = parameters(period).gov.irs.deductions.itemized.medical medical_floor = p.floor * tax_unit("positive_agi", period) - # Form 39R lines 1-6 use costs claimed on this return, not payments - # independently made by dependents and aggregated by the federal model. claimant_medical = max_(0, premiums + other_medical - medical_floor) return min_(federal_medical, claimant_medical) From 128ae2b54d90fffb63526532238e8a0a150ac910 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 10:34:24 -0400 Subject: [PATCH 2/3] Test deleted itemized-input provenance on Idaho's itemized total From derived inputs Idaho's claimant worksheet now equals the federal medical deduction, so a deleted federal medical input changes no Idaho amount; the only separator, a supplied Idaho premium amount below the Schedule A premiums, is a case where the derived worksheet departs from Form 39R line 6. Test the same provenance defect where it still shows: a derived federal itemized total, whose medical component Idaho replaces with an explicit claimant deduction. Co-Authored-By: Claude Opus 5.5 --- .../core/test_id_health_insurance_premiums.py | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/policyengine_us/tests/core/test_id_health_insurance_premiums.py b/policyengine_us/tests/core/test_id_health_insurance_premiums.py index 117759d4d1e..61ea17e744c 100644 --- a/policyengine_us/tests/core/test_id_health_insurance_premiums.py +++ b/policyengine_us/tests/core/test_id_health_insurance_premiums.py @@ -231,11 +231,16 @@ def test_actual_medical_deduction_is_scoped_to_period_and_branch(): strict=True, reason=( "policyengine-core<3.32.27 retains deleted input provenance and " - "misclassifies a federal medical deduction recalculated before the " - "Idaho helper as supplied; fixed by policyengine-core#561." + "misclassifies a federal itemized total recalculated before the " + "Idaho formula as supplied; fixed by policyengine-core#561." ), ) -def test_deleted_medical_input_recalculated_federal_first(): +def test_deleted_itemized_input_recalculated_federal_first(): + # From derived inputs Idaho's claimant worksheet equals the federal medical + # deduction, so a deleted federal medical input cannot change any Idaho + # amount. Idaho's itemized total still tells a supplied federal total, + # used as given, from a derived one, whose medical component it replaces + # with an explicit Idaho claimant deduction. year = 2025 simulation = Simulation( tax_benefit_system=SYSTEM, @@ -249,8 +254,6 @@ def test_deleted_medical_input_recalculated_federal_first(): "dependent": { "age": {year: 20}, "is_tax_unit_dependent": {year: True}, - # The federal subtotal and Idaho's claimant worksheet - # both count a dependent's other costs. "other_medical_expenses": {year: 500}, }, }, @@ -258,12 +261,10 @@ def test_deleted_medical_input_recalculated_federal_first(): "tax_unit": { "members": ["head", "dependent"], "adjusted_gross_income": {year: 30_000}, - "standard_deduction": {year: 15_750}, - "id_itemized_deductions": {year: 20_000}, - # From derived inputs the claimant worksheet equals the - # federal deduction. A supplied Idaho premium amount below - # the 3,000 Schedule A premiums keeps the two apart. - "id_qualified_health_insurance_premiums": {year: 2_000}, + "interest_deduction": {year: 20_000}, + "salt_deduction": {year: 0}, + "id_salt_deduction": {year: 0}, + "id_health_insurance_premiums_medical_deduction": {year: 250}, } }, "households": { @@ -274,17 +275,18 @@ def test_deleted_medical_input_recalculated_federal_first(): }, }, ) - simulation.set_input("medical_expense_deduction", year, np.array([3_000])) - simulation.delete_arrays("medical_expense_deduction", year) + simulation.set_input("itemized_taxable_income_deductions", year, np.array([30_000])) + simulation.delete_arrays("itemized_taxable_income_deductions", year) - # Federal first: 3,000 + 500 - 30,000 * 7.5% = 1,250. - assert simulation.calculate("medical_expense_deduction", year).tolist() == [1_250] - # Idaho's claimant overlap is 2,000 + 500 - 30,000 * 7.5% = 250, leaving - # 2,000 - 250 = 1,750. Core's provenance defect before 3.32.27 uses the - # 1,250 federal deduction and returns 2,000 - 1,250 = 750. + # Federal first: 20,000 interest + (3,000 + 500 - 30,000 * 7.5%) medical + # = 21,250. assert simulation.calculate( - "id_health_insurance_premiums_subtraction", year - ).tolist() == [1_750] + "itemized_taxable_income_deductions", year + ).tolist() == [21_250] + # Idaho replaces the 1,250 federal medical deduction with the explicit 250: + # 21,250 - 1,000 = 20,250. Core's provenance defect before 3.32.27 takes the + # recalculated total as supplied and keeps 21,250. + assert simulation.calculate("id_itemized_deductions", year).tolist() == [20_250] # Form 39R worksheet lines 1-6 take the medical expenses claimed on federal From 14c9854c04fcd12be5a0b4ec5cd0adbd550fade1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 10:41:22 -0400 Subject: [PATCH 3/3] Supply graph-heavy inputs in the Idaho worksheet property test The standard deduction and taxable income before itemized deductions do not depend on medical expenses; deriving them pulled most of the federal tax graph into the core test group. Supplying them cuts the property test's added peak footprint from about 273 MB to about 36 MB locally, with the 2026 itemized limitation still binding in half of its 2026 simulations. Co-Authored-By: Claude Opus 5.5 --- .../tests/core/test_id_health_insurance_premiums.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/policyengine_us/tests/core/test_id_health_insurance_premiums.py b/policyengine_us/tests/core/test_id_health_insurance_premiums.py index 61ea17e744c..65b505b9b0f 100644 --- a/policyengine_us/tests/core/test_id_health_insurance_premiums.py +++ b/policyengine_us/tests/core/test_id_health_insurance_premiums.py @@ -330,6 +330,7 @@ def claimant_returns(draw): "other": [draw(st.integers(0, 40_000)) for _ in range(members)], "agi": draw(st.integers(0, 150_000) | st.integers(600_000, 900_000)), "interest": draw(st.integers(0, 40_000)), + "standard": draw(st.integers(0, 35_000)), "above_line": draw(st.integers(0, 25_000)), "receiver": filers + draw(st.integers(0, dependents - 1)), "raise": draw(st.integers(1, 10_000)), @@ -376,6 +377,10 @@ def _claimant_simulation(cases, year): "interest_deduction": {year: case["interest"]}, "salt_deduction": {year: 0}, "id_salt_deduction": {year: 0}, + # Supplied to keep the model graph small: neither depends on + # medical expenses. + "standard_deduction": {year: case["standard"]}, + "taxable_income_before_itemized_deductions": {year: case["agi"]}, } households[unit] = {"members": members, "state_code": {year: "ID"}} rows.append((index, variant))