From 6d3cb8fe2ee2666254ba8923a607754decef4809 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 7 May 2026 10:52:46 -0400 Subject: [PATCH 01/17] Fix federal-state tax conformity rules --- .../states/al/tax/income/agi/deductions.yaml | 6 ++ .../hi/tax/income/additions/additions.yaml | 9 +++ .../tax/income/subtractions/subtractions.yaml | 2 +- .../credits/earned_income/decoupled.yaml | 2 +- .../tax/income/adjustments/adjustments.yaml | 4 ++ .../oh/tax/income/deductions/deductions.yaml | 2 +- .../gov/states/al/tax/income/al_agi.yaml | 10 +++ .../co_federal_deduction_addback.yaml | 28 ++++++-- .../co/tax/income/credits/eitc/co_eitc.yaml | 51 ++++++++++++++ .../gov/states/dc/tax/income/dc_eitc.yaml | 37 +++++++--- .../income/dc_eitc_with_qualifying_child.yaml | 47 ++++++++++++- .../dc_eitc_without_qualifying_children.yaml | 25 +++++++ .../states/ga/tax/income/ga_deductions.yaml | 19 +++-- .../hi_student_loan_interest_adjustment.yaml | 17 +++++ .../id/tax/income/id_itemized_deductions.yaml | 12 ++++ .../states/il/tax/income/credits/il_ctc.yaml | 31 ++++++-- .../states/il/tax/income/credits/il_eitc.yaml | 64 +++++++++++++++-- .../ms/tax/income/ms_agi_adjustments.yaml | 10 +++ .../income/oh_educator_expense_deduction.yaml | 23 ++++++ .../states/pa/tax/income/credits/pa_cdcc.yaml | 15 +++- .../tax/income/state_itemized_deductions.yaml | 19 ++++- .../tax/income/state_standard_deduction.yaml | 13 +++- .../states/ut/tax/income/credits/ut_eitc.yaml | 11 +++ ...ederal_deductions_for_taxpayer_credit.yaml | 16 +++++ .../wa_working_families_tax_credit.yaml | 30 ++++++++ .../demographic/tax_unit/filing_status.yaml | 23 ++++++ .../co_federal_deduction_addback.py | 6 +- .../co/tax/income/credits/eitc/co_eitc.py | 53 +++++++++++++- .../dc/tax/income/credits/eitc/dc_eitc.py | 6 +- .../eitc/dc_eitc_with_qualifying_child.py | 20 +++++- .../eitc/dc_eitc_without_qualifying_child.py | 26 ++++++- .../ga/tax/income/deductions/ga_deductions.py | 7 +- .../ga_itemized_deductions_adjustment.py | 11 +++ .../gov/states/hi/tax/income/hi_additions.py | 2 + .../hi_student_loan_interest_adjustment.py | 37 ++++++++++ .../deductions/id_itemized_deductions.py | 3 +- .../states/il/tax/income/credits/il_ctc.py | 5 +- .../states/il/tax/income/credits/il_eitc.py | 24 ++++++- .../oh_educator_expense_deduction_person.py | 11 +++ .../states/pa/tax/income/credits/pa_cdcc.py | 5 +- .../states/tax/income/credits/eitc_helpers.py | 70 +++++++++++++++++++ .../tax/income/state_itemized_deductions.py | 9 +-- .../tax/income/state_standard_deduction.py | 2 - ..._federal_deductions_for_taxpayer_credit.py | 27 ++----- .../tax/income/credits/ut_eitc_potential.py | 3 +- .../credits/wa_working_families_tax_credit.py | 59 +++++++++++++++- ...ilies_tax_credit_age_expansion_eligible.py | 38 +++++----- .../tax_unit/head_of_household_eligible.py | 13 ++-- 48 files changed, 841 insertions(+), 122 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/tax/income/al_agi.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi_adjustments.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/oh_educator_expense_deduction.yaml create mode 100644 policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py create mode 100644 policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py create mode 100644 policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py create mode 100644 policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py diff --git a/policyengine_us/parameters/gov/states/al/tax/income/agi/deductions.yaml b/policyengine_us/parameters/gov/states/al/tax/income/agi/deductions.yaml index efda2cf0743..90fd39e10a5 100644 --- a/policyengine_us/parameters/gov/states/al/tax/income/agi/deductions.yaml +++ b/policyengine_us/parameters/gov/states/al/tax/income/agi/deductions.yaml @@ -4,13 +4,19 @@ values: # IRA distributions prior to 1982 are not deductible. - traditional_ira_contributions # Line 1 - alimony_expense # Line 4 + - early_withdrawal_penalty # Line 5 - self_employed_health_insurance_ald # Line 7 + - health_savings_account_ald # Line 8 + - self_employed_pension_contribution_ald # Line 9 - us_govt_interest 2023-01-01: # IRA distributions prior to 1982 are not deductible. - traditional_ira_contributions # Line 1 - alimony_expense # Line 4 + - early_withdrawal_penalty # Line 5 - self_employed_health_insurance_ald # Line 7 + - health_savings_account_ald # Line 8 + - self_employed_pension_contribution_ald # Line 9 - us_govt_interest - al_retirement_exemption #Schedule RS Line 10 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml new file mode 100644 index 00000000000..ad98c27767a --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml @@ -0,0 +1,9 @@ +description: Hawaii additions to federal adjusted gross income. +values: + 2021-01-01: + - hi_student_loan_interest_addition + +metadata: + unit: list + period: year + label: Hawaii additions to federal adjusted gross income diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml index f8444c3f216..9c24560e8ab 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml @@ -5,7 +5,7 @@ values: - taxable_social_security # Line 14 - hi_military_pay_exclusion # Line 15 - us_govt_interest # Line 18 - - student_loan_interest_ald # Line 18 + - hi_student_loan_interest_subtraction # Line 18 metadata: diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml index 0318dc8cb5d..68088df3611 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml @@ -14,5 +14,5 @@ metadata: values: 2010-01-01: false 2011-01-01: true - 2023-01-01: false + 2023-01-01: true diff --git a/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml index 98d4dd7708a..4cb971917de 100644 --- a/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml +++ b/policyengine_us/parameters/gov/states/ms/tax/income/adjustments/adjustments.yaml @@ -5,6 +5,10 @@ values: - ms_self_employment_adjustment # Line 61 - ms_national_guard_or_reserve_pay_adjustment # Line 55 - alimony_expense # Line 53 + - self_employed_pension_contribution_ald # Line 51 + - early_withdrawal_penalty # Line 52 + - self_employed_health_insurance_ald # Line 56 + - health_savings_account_ald # Line 57 - ms_retirement_income_exemption - us_govt_interest_person - ms_529_deduction diff --git a/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml b/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml index 5f91de234dc..67bc1c98eba 100644 --- a/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml +++ b/policyengine_us/parameters/gov/states/oh/tax/income/deductions/deductions.yaml @@ -6,7 +6,7 @@ values: - oh_uniformed_services_retirement_income_deduction - oh_529_plan_deduction_person - pell_grant - - educator_expense + - oh_educator_expense_deduction_person - disability_benefits - oh_unreimbursed_medical_care_expense_deduction_person - us_govt_interest_person diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/al_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/al_agi.yaml new file mode 100644 index 00000000000..ace5bd1f75a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/tax/income/al_agi.yaml @@ -0,0 +1,10 @@ +- name: Alabama AGI includes modeled federal deductions the state still allows + period: 2025 + input: + state_code: AL + irs_employment_income: 1_000 + early_withdrawal_penalty: 50 + health_savings_account_ald: 150 + self_employed_pension_contribution_ald: 100 + output: + al_agi: 700 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/federal_deduction/co_federal_deduction_addback.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/federal_deduction/co_federal_deduction_addback.yaml index 27001fe8fd6..59e452d3cd5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/federal_deduction/co_federal_deduction_addback.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/additions/federal_deduction/co_federal_deduction_addback.yaml @@ -264,7 +264,8 @@ state_code: CO co_federal_deduction_addback_required: true filing_status: SINGLE - taxable_income_deductions: 22_000 + tax_unit_itemizes: false + standard_deduction: 22_000 output: co_federal_deduction_addback: 10_000 @@ -274,8 +275,8 @@ state_code: CO co_federal_deduction_addback_required: true filing_status: JOINT - taxable_income_deductions: 26_000 - standard_deduction: 0 + tax_unit_itemizes: false + standard_deduction: 26_000 output: co_federal_deduction_addback: 10_000 @@ -285,7 +286,8 @@ state_code: CO co_federal_deduction_addback_required: true filing_status: SEPARATE - taxable_income_deductions: 22_000 + tax_unit_itemizes: false + standard_deduction: 22_000 output: co_federal_deduction_addback: 10_000 @@ -295,7 +297,8 @@ state_code: CO co_federal_deduction_addback_required: true filing_status: SURVIVING_SPOUSE - taxable_income_deductions: 22_000 + tax_unit_itemizes: false + standard_deduction: 22_000 output: co_federal_deduction_addback: 10_000 @@ -305,6 +308,19 @@ state_code: CO co_federal_deduction_addback_required: true filing_status: HEAD_OF_HOUSEHOLD - taxable_income_deductions: 32_000 + tax_unit_itemizes: false + standard_deduction: 32_000 output: co_federal_deduction_addback: 20_000 + +- name: In 2023, Colorado addback ignores unrelated taxable-income deductions like QBI. + period: 2023 + input: + state_code: CO + co_federal_deduction_addback_required: true + filing_status: SINGLE + tax_unit_itemizes: false + standard_deduction: 13_850 + taxable_income_deductions: 20_000 + output: + co_federal_deduction_addback: 1_850 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml index 14476fc8548..af3285014a3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml @@ -74,3 +74,54 @@ # Federal EITC for 1 child at $20k income (head of household) is approximately $4,328 # CO EITC = 50% * federal EITC = $2,164 co_eitc: 2_164 + +- name: Colorado EITC includes ITIN qualifying children + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + employment_income: 10_000 + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: CO + output: + co_eitc: 1_700 + +- name: Colorado EITC includes certain childless filers under 25 + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 19 + employment_income: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + co_eitc: 324.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc.yaml index 97b5d12b2ef..a31701f41a6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc.yaml @@ -1,19 +1,40 @@ - name: DC EITC without qualifying children period: 2023 input: - eitc_child_count: 0 - dc_eitc_with_qualifying_child: 1 - dc_eitc_without_qualifying_child: 2 - state_code: DC + people: + head: + age: 30 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + dc_eitc_with_qualifying_child: 1 + dc_eitc_without_qualifying_child: 2 + households: + household: + members: [head] + state_code: DC output: dc_eitc: 2 - name: DC EITC with qualifying children period: 2023 input: - eitc_child_count: 2 - dc_eitc_with_qualifying_child: 1 - dc_eitc_without_qualifying_child: 2 - state_code: DC + people: + head: + age: 30 + is_tax_unit_head: true + child: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + dc_eitc_with_qualifying_child: 1 + dc_eitc_without_qualifying_child: 2 + households: + household: + members: [head, child] + state_code: DC output: dc_eitc: 1 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_with_qualifying_child.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_with_qualifying_child.yaml index e7c544f1704..74e43ba0a60 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_with_qualifying_child.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_with_qualifying_child.yaml @@ -9,7 +9,50 @@ - name: 70% match period: 2023 input: - state_code: DC - eitc: 100 + people: + head: + age: 30 + is_tax_unit_head: true + child: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + filer_adjusted_earnings: 294.11764705882354 + adjusted_gross_income: 294.11764705882354 + households: + household: + members: [head, child] + state_code: DC output: dc_eitc_with_qualifying_child: 70 + +- name: DC EITC with qualifying child includes ITIN child in 2025 + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + employment_income: 10_000 + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: DC + output: + dc_eitc_with_qualifying_child: 3_400 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml index b93634e87ec..166fe58cc58 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml @@ -41,3 +41,28 @@ state_code: DC output: dc_eitc_without_qualifying_child: 823.60 # 1502 - (27743 - 19743) * 0.0848 + +- name: DC childless EITC allows ITIN filers + absolute_error_margin: 0.01 + period: 2025 + input: + people: + head: + age: 30 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + dc_eitc_without_qualifying_child: 612 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_deductions.yaml index 6cf4f9495cb..877f35b9965 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_deductions.yaml @@ -3,9 +3,8 @@ input: tax_unit_itemizes: true ga_standard_deduction: 8_000 - medical_expense_deduction: 9_000 + itemized_taxable_income_deductions: 9_000 state_code: GA - state_sales_tax: 0 output: ga_deductions: 9_000 @@ -24,10 +23,18 @@ input: tax_unit_itemizes: true ga_standard_deduction: 10_000 - charitable_deduction: 2_000 - interest_deduction: 3_000 - casualty_loss_deduction: 8_000 + itemized_taxable_income_deductions: 13_000 state_code: GA - state_sales_tax: 0 output: ga_deductions: 13_000 + +- name: Georgia itemized deductions subtract Georgia-specific adjustments + period: 2023 + input: + tax_unit_itemizes: true + ga_standard_deduction: 10_000 + itemized_taxable_income_deductions: 18_000 + ga_itemized_deductions_adjustment: 3_000 + state_code: GA + output: + ga_deductions: 15_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml new file mode 100644 index 00000000000..7ad2c9f1a36 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml @@ -0,0 +1,17 @@ +- name: Hawaii can add back student loan interest when the Hawaii allowance is smaller than federal + period: 2025 + input: + state_code: HI + adjusted_gross_income: 1_000 + hi_student_loan_interest_adjustment: -100 + output: + hi_agi: 1_100 + +- name: Hawaii can subtract more student loan interest when the Hawaii allowance is larger than federal + period: 2025 + input: + state_code: HI + adjusted_gross_income: 1_000 + hi_student_loan_interest_adjustment: 75 + output: + hi_agi: 925 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 d820b852a51..2538ff305c2 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 @@ -3,6 +3,7 @@ input: id_salt_deduction: 1_000 itemized_taxable_income_deductions: 1_600 + foreign_tax_credit: 0 state_code: ID output: id_itemized_deductions: 600 @@ -12,6 +13,17 @@ input: id_salt_deduction: 2_000 itemized_taxable_income_deductions: 1_600 + foreign_tax_credit: 0 state_code: ID output: id_itemized_deductions: 0 + +- name: Idaho adds back the federal foreign tax credit to itemized deductions + period: 2025 + input: + id_salt_deduction: 2_000 + itemized_taxable_income_deductions: 1_600 + foreign_tax_credit: 700 + state_code: ID + output: + id_itemized_deductions: 300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml index 3033b134318..8e4b1adfbf6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml @@ -6,7 +6,7 @@ age: 30 dependent: age: 1 - ctc_qualifying_child: true + is_tax_unit_dependent: true households: household: members: [head, dependent] @@ -26,7 +26,7 @@ age: 30 dependent: age: 12 - ctc_qualifying_child: true + is_tax_unit_dependent: true households: household: members: [head, dependent] @@ -46,13 +46,13 @@ age: 30 dependent: age: 12 - ctc_qualifying_child: true + is_tax_unit_dependent: true dependent2: age: 10 - ctc_qualifying_child: true + is_tax_unit_dependent: true dependent3: age: 9 - ctc_qualifying_child: true + is_tax_unit_dependent: true households: household: members: [head, dependent, dependent2, dependent3] @@ -63,3 +63,24 @@ il_eitc: 100 output: il_ctc: 20 + +- name: Illinois child credit follows dependency and age, not federal CTC child rules + period: 2024 + input: + people: + head: + age: 30 + dependent: + age: 11 + is_tax_unit_dependent: true + ctc_qualifying_child: false + households: + household: + members: [head, dependent] + state_code: IL + tax_units: + tax_unit: + members: [head, dependent] + il_eitc: 100 + output: + il_ctc: 20 diff --git a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml index e34a35bc618..8ec41264958 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml @@ -2,8 +2,22 @@ period: 2021 absolute_error_margin: 0.0001 input: - eitc: 1_000 - state_code: IL + people: + head: + age: 30 + is_tax_unit_head: true + child: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filer_adjusted_earnings: 2_941.176470588235 + adjusted_gross_income: 2_941.176470588235 + households: + household: + members: [head, child] + state_code: IL output: il_eitc: 180 @@ -20,8 +34,22 @@ period: 2023 absolute_error_margin: 0.0001 input: - eitc: 1_000 - state_code: IL + people: + head: + age: 30 + is_tax_unit_head: true + child: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filer_adjusted_earnings: 2_941.176470588235 + adjusted_gross_income: 2_941.176470588235 + households: + household: + members: [head, child] + state_code: IL output: il_eitc: 200 @@ -33,3 +61,31 @@ state_code: IL output: il_eitc: 0 + +- name: 2025 Illinois EITC includes ITIN qualifying children and younger filers + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + employment_income: 10_000 + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: IL + output: + il_eitc: 680 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi_adjustments.yaml b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi_adjustments.yaml new file mode 100644 index 00000000000..37294ec1b31 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ms/tax/income/ms_agi_adjustments.yaml @@ -0,0 +1,10 @@ +- name: Mississippi includes modeled federal AGI adjustments it still allows + period: 2025 + input: + state_code: MS + self_employed_pension_contribution_ald: 100 + early_withdrawal_penalty: 50 + self_employed_health_insurance_ald: 200 + health_savings_account_ald: 150 + output: + ms_agi_adjustments: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/oh_educator_expense_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/oh_educator_expense_deduction.yaml new file mode 100644 index 00000000000..5ec651de6ac --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/oh_educator_expense_deduction.yaml @@ -0,0 +1,23 @@ +- name: Ohio educator deduction uses the Ohio-specific deduction amount, not the raw federal expense + period: 2025 + input: + people: + person: + educator_expense: 300 + oh_educator_expense_deduction_person: 50 + taxable_social_security: 0 + oh_uniformed_services_retirement_income_deduction: 0 + oh_529_plan_deduction_person: 0 + pell_grant: 0 + disability_benefits: 0 + oh_unreimbursed_medical_care_expense_deduction_person: 0 + us_govt_interest_person: 0 + households: + household: + members: [person] + state_code: OH + tax_units: + tax_unit: + members: [person] + output: + oh_deductions: 50 diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml index 5b8dc978e46..b12de20ba89 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml @@ -2,7 +2,7 @@ period: 2022 input: state_code: PA - cdcc_potential: 0 + cdcc: 0 output: pa_cdcc: 0 @@ -10,7 +10,7 @@ period: 2022 input: state_code: PA - cdcc_potential: 1_000 + cdcc: 1_000 output: pa_cdcc: 300 @@ -18,6 +18,15 @@ period: 2023 input: state_code: PA - cdcc_potential: 1_000 + cdcc: 1_000 output: pa_cdcc: 1_000 + +- name: No Pennsylvania credit when only the federal potential credit is positive + period: 2023 + input: + state_code: PA + cdcc: 0 + cdcc_potential: 1_000 + output: + pa_cdcc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml index ecb37149441..27225b9cd81 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml @@ -1,4 +1,4 @@ -- name: Federal itemized deductions adopted for Connecticut +- name: Connecticut does not adopt federal itemized deductions period: 2023 input: state_code: CT @@ -9,8 +9,7 @@ interest_deduction: 8_000 salt_deduction: 10_000 output: - # State should use sum of federal itemized deduction components - state_itemized_deductions: 23_000 + state_itemized_deductions: 0 - name: Federal itemized deductions adopted for Georgia period: 2023 @@ -53,6 +52,20 @@ # State should use sum of federal itemized deduction components state_itemized_deductions: 27_500 +- name: Georgia uses the claimed federal itemized deduction amount + period: 2023 + input: + state_code: GA + filing_status: JOINT + age_head: 40 + age_spouse: 38 + charitable_deduction: 10_000 + interest_deduction: 15_000 + salt_deduction: 10_000 + itemized_taxable_income_deductions: 31_000 + output: + state_itemized_deductions: 31_000 + - name: New Mexico uses federal itemized deductions through its own implementation period: 2023 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_standard_deduction.yaml index 39f33682c43..9c1b1c3d302 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_standard_deduction.yaml @@ -1,4 +1,4 @@ -- name: Federal standard deduction adopted for Connecticut +- name: Connecticut does not adopt the federal standard deduction period: 2023 input: state_code: CT @@ -6,7 +6,7 @@ age_head: 30 output: standard_deduction: 13_850 - state_standard_deduction: 13_850 + state_standard_deduction: 0 - name: Federal standard deduction adopted for Utah married couple period: 2023 @@ -19,6 +19,15 @@ standard_deduction: 27_700 state_standard_deduction: 27_700 +- name: South Carolina does not use the federal standard deduction + period: 2026 + input: + state_code: SC + filing_status: SINGLE + age_head: 30 + output: + state_standard_deduction: 0 + - name: State-specific deduction for Georgia period: 2023 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_eitc.yaml index 09b9185c72c..b2f45916d79 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_eitc.yaml @@ -2,6 +2,7 @@ period: 2022 input: eitc: 1000 + employment_income: 1000 state_code: UT ut_income_tax_before_non_refundable_credits: 1000000 output: @@ -14,3 +15,13 @@ ut_income_tax_before_non_refundable_credits: 1000000 output: ut_eitc: 0 + +- name: Utah EITC is capped by wages shown on the W-2 + period: 2025 + input: + eitc: 1000 + employment_income: 150 + state_code: UT + ut_income_tax_before_non_refundable_credits: 1000000 + output: + ut_eitc: 150 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_federal_deductions_for_taxpayer_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_federal_deductions_for_taxpayer_credit.yaml index da3ae1b2f6d..0b633991a8b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_federal_deductions_for_taxpayer_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_federal_deductions_for_taxpayer_credit.yaml @@ -24,6 +24,8 @@ medical_expense_deduction: 500 casualty_loss_deduction: 500 real_estate_taxes: 6_000 + itemized_taxable_income_deductions: 7_000 + state_and_local_sales_or_income_tax: 0 output: ut_federal_deductions_for_taxpayer_credit: 7_000 @@ -38,6 +40,8 @@ medical_expense_deduction: 500 casualty_loss_deduction: 500 real_estate_taxes: 12_000 + itemized_taxable_income_deductions: 12_000 + state_and_local_sales_or_income_tax: 0 output: ut_federal_deductions_for_taxpayer_credit: 12_000 @@ -55,3 +59,15 @@ output: ut_federal_deductions_for_taxpayer_credit: 5_000 +- name: "Case 5: Utah subtracts only state income or sales tax from claimed itemized deductions" + period: 2023 + input: + filing_status: JOINT + state_code: UT + tax_unit_itemizes: true + itemized_taxable_income_deductions: 15_000 + state_and_local_sales_or_income_tax: 9_000 + standard_deduction: 0 + output: + ut_federal_deductions_for_taxpayer_credit: 6_000 + diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index b648f5315d1..67898d42295 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -190,3 +190,33 @@ output: # Either head or spouse meeting age 18 is sufficient. wa_working_families_tax_credit: 367 + +- name: Case 14, ITIN filer with qualifying child can receive WFTC without a federal EITC amount. + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 30 + employment_income: 10_000 + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: WA + output: + wa_working_families_tax_credit: 660 diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/filing_status.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/filing_status.yaml index a676f7d4a30..47e1eef3036 100644 --- a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/filing_status.yaml +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/filing_status.yaml @@ -97,3 +97,26 @@ output: # High-income adult child fails qualifying relative income test filing_status: SINGLE + +- name: Separated filer with only a qualifying relative is not HOH. + period: 2024 + input: + people: + parent: + age: 45 + employment_income: 50_000 + is_separated: true + adult_child: + age: 19 + is_full_time_student: false + is_tax_unit_dependent: true + is_tax_unit_spouse: false + employment_income: 3_000 + tax_units: + tax_unit: + members: [parent, adult_child] + households: + household: + members: [parent, adult_child] + output: + filing_status: SEPARATE diff --git a/policyengine_us/variables/gov/states/co/tax/income/additions/federal_deductions/co_federal_deduction_addback.py b/policyengine_us/variables/gov/states/co/tax/income/additions/federal_deductions/co_federal_deduction_addback.py index a30f006df47..a44811d843c 100644 --- a/policyengine_us/variables/gov/states/co/tax/income/additions/federal_deductions/co_federal_deduction_addback.py +++ b/policyengine_us/variables/gov/states/co/tax/income/additions/federal_deductions/co_federal_deduction_addback.py @@ -22,7 +22,11 @@ def formula(tax_unit, period, parameters): if p.itemized_only: deductions = tax_unit("itemized_taxable_income_deductions", period) else: - deductions = tax_unit("taxable_income_deductions", period) + deductions = where( + tax_unit("tax_unit_itemizes", period), + tax_unit("itemized_taxable_income_deductions", period), + tax_unit("standard_deduction", period), + ) filing_status = tax_unit("filing_status", period) exemption = p.exemption[filing_status] return max_(deductions - exemption, 0) diff --git a/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py b/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py index c707b20c552..670ee2b21b8 100644 --- a/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py +++ b/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_like_amount, +) class co_eitc(Variable): @@ -12,5 +15,53 @@ class co_eitc(Variable): def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) + person = tax_unit.members + age = person("age", period) + has_tin = person("has_tin", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + federal_identification = person( + "meets_eitc_identification_requirements", period + ) + filer_has_federal_identification = ( + tax_unit.sum(is_head_or_spouse & ~federal_identification) == 0 + ) + qualifying_child_with_tin = ( + person("is_qualifying_child_dependent", period) & has_tin + ) + child_count_with_tin = tax_unit.sum(qualifying_child_with_tin) + itin_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count_with_tin, + child_count_with_tin > 0, + filer_has_tin, + ) + + specified_student = person("is_full_time_college_student", period) | person( + "is_part_time_college_student", period + ) + specified_student |= person("technical_institution_student", period) + homeless_or_foster = person("was_in_foster_care", period) | person.household( + "is_homeless", period + ) + under_25_age_eligible = ( + ((age >= 19) & (age < 24) & ~specified_student) + | (age == 24) + | ((age >= 18) & (age < 24) & homeless_or_foster) + ) + under_25_demographic_eligible = tax_unit.any( + is_head_or_spouse & under_25_age_eligible + ) + under_25_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + 0, + under_25_demographic_eligible, + filer_has_federal_identification, + ) p = parameters(period).gov.states.co.tax.income.credits - return federal_eitc * p.eitc.match + state_eitc = max_(federal_eitc, max_(itin_eitc, under_25_eitc)) + return state_eitc * p.eitc.match diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py index 192b5501a08..315e6de9aa1 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py @@ -13,8 +13,12 @@ class dc_eitc(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): + person = tax_unit.members + dc_qualifying_child = person("is_qualifying_child_dependent", period) & person( + "has_tin", period + ) return where( - tax_unit("eitc_child_count", period) > 0, + tax_unit.sum(dc_qualifying_child) > 0, tax_unit("dc_eitc_with_qualifying_child", period), tax_unit("dc_eitc_without_qualifying_child", period), ) diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py index 1b53d7d6588..5cf2d58f89f 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_like_amount, +) class dc_eitc_with_qualifying_child(Variable): @@ -13,6 +16,19 @@ class dc_eitc_with_qualifying_child(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): - federal_eitc = tax_unit("eitc", period) + person = tax_unit.members + has_tin = person("has_tin", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + qualifying_child = person("is_qualifying_child_dependent", period) & has_tin + child_count = tax_unit.sum(qualifying_child) + filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + federal_like_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + child_count > 0, + filer_has_tin, + ) p = parameters(period).gov.states.dc.tax.income.credits - return federal_eitc * p.eitc.with_children.match + return federal_like_eitc * p.eitc.with_children.match diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py index 392ef615ebc..5bab5dc8193 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py @@ -1,4 +1,8 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + eitc_filing_requirement_met, + eitc_filing_status_eligible, +) class dc_eitc_without_qualifying_child(Variable): @@ -13,8 +17,26 @@ class dc_eitc_without_qualifying_child(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): - # calculate US EITC amount before phase-out - us_eligible = tax_unit("eitc_eligible", period) + person = tax_unit.members + age = person("age", period) + has_tin = person("has_tin", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) + max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) + age_eligible = is_head_or_spouse & (age >= min_age) & (age <= max_age) + no_qualifying_child = tax_unit.sum( + person("is_qualifying_child_dependent", period) & has_tin + ) == 0 + us_eligible = ( + filer_has_tin + & no_qualifying_child + & tax_unit.any(age_eligible) + & tax_unit("eitc_investment_income_eligible", period) + & eitc_filing_status_eligible(tax_unit, period, parameters) + & eitc_filing_requirement_met(tax_unit, period) + & tax_unit("takes_up_eitc", period) + ) us_eitc = us_eligible * tax_unit("eitc_phased_in", period) # phase out us_eitc for income above DC phase-out start threshold earnings = tax_unit("tax_unit_earned_income", period) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_deductions.py b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_deductions.py index 59e0adad3de..2e8e382a280 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_deductions.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_deductions.py @@ -25,9 +25,6 @@ class ga_deductions(Variable): def formula(tax_unit, period, parameters): itemizes = tax_unit("tax_unit_itemizes", period) sd = tax_unit("ga_standard_deduction", period) - # 48-7-27(a)(1) states: - # "Either the sum of all itemized nonbusiness deductions used in computing - # such taxpayer’s federal taxable income or..." - p = parameters(period).gov.irs.deductions - itemized = add(tax_unit, period, p.itemized_deductions) + itemized = tax_unit("itemized_taxable_income_deductions", period) + itemized -= tax_unit("ga_itemized_deductions_adjustment", period) return where(itemizes, itemized, sd) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py new file mode 100644 index 00000000000..8d253b52449 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class ga_itemized_deductions_adjustment(Variable): + value_type = float + entity = TaxUnit + label = "Georgia itemized deductions adjustment" + unit = USD + definition_period = YEAR + default_value = 0 + defined_for = StateCode.GA diff --git a/policyengine_us/variables/gov/states/hi/tax/income/hi_additions.py b/policyengine_us/variables/gov/states/hi/tax/income/hi_additions.py index 1a7a33bbec7..3c9013ec7ca 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/hi_additions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/hi_additions.py @@ -9,3 +9,5 @@ class hi_additions(Variable): defined_for = StateCode.HI unit = USD definition_period = YEAR + + adds = "gov.states.hi.tax.income.additions.additions" diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py new file mode 100644 index 00000000000..7f5b5324d7c --- /dev/null +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -0,0 +1,37 @@ +from policyengine_us.model_api import * + + +class hi_student_loan_interest_adjustment(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii student loan interest adjustment relative to federal AGI" + unit = USD + definition_period = YEAR + default_value = 0 + defined_for = StateCode.HI + + +class hi_student_loan_interest_subtraction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii student loan interest subtraction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + + def formula(tax_unit, period, parameters): + adjustment = tax_unit("hi_student_loan_interest_adjustment", period) + return max_(adjustment, 0) + + +class hi_student_loan_interest_addition(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii student loan interest addition" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + + def formula(tax_unit, period, parameters): + adjustment = tax_unit("hi_student_loan_interest_adjustment", period) + return max_(0, -adjustment) 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 d717b115b4a..625e9e072da 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 @@ -18,4 +18,5 @@ def formula(tax_unit, period, parameters): # by the amount of the SALT deduction id_salt_ded = tax_unit("id_salt_deduction", period) itemized_ded = tax_unit("itemized_taxable_income_deductions", period) - return max_(itemized_ded - id_salt_ded, 0) + foreign_tax_credit = tax_unit("foreign_tax_credit", period) + return max_(itemized_ded - id_salt_ded + foreign_tax_credit, 0) diff --git a/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py b/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py index 03104d26b75..fcaf7fa9e76 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py +++ b/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py @@ -14,9 +14,8 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.il.tax.income.credits.ctc person = tax_unit.members age = person("age", period) - age_eligible_child = age < p.age_limit - federal_ctc_eligible_child = person("ctc_qualifying_child", period) - eligible_child = age_eligible_child & federal_ctc_eligible_child + dependent = person("is_tax_unit_dependent", period) + eligible_child = dependent & (age < p.age_limit) eligible_child_present = tax_unit.any(eligible_child) state_eitc = tax_unit("il_eitc", period) return eligible_child_present * state_eitc * p.rate diff --git a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py index 21dc19e29a0..c8c1855b081 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py +++ b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_like_amount, +) class il_eitc(Variable): @@ -11,6 +14,23 @@ class il_eitc(Variable): defined_for = StateCode.IL def formula(tax_unit, period, parameters): - federal_eitc = tax_unit("eitc", period) + person = tax_unit.members + age = person("age", period) + has_tin = person("has_tin", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + qualifying_child = person("is_qualifying_child_dependent", period) & has_tin + child_count = tax_unit.sum(qualifying_child) + filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + demographic_eligible = (child_count > 0) | tax_unit.any( + is_head_or_spouse & (age >= 18) + ) + state_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_has_tin, + ) match = parameters(period).gov.states.il.tax.income.credits.eitc.match - return federal_eitc * match + return state_eitc * match diff --git a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py new file mode 100644 index 00000000000..33d281ba7cb --- /dev/null +++ b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class oh_educator_expense_deduction_person(Variable): + value_type = float + entity = Person + label = "Ohio educator expense deduction" + unit = USD + definition_period = YEAR + default_value = 0 + defined_for = StateCode.OH diff --git a/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py b/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py index 41ce25c2f33..e9196d559da 100644 --- a/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py +++ b/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py @@ -11,9 +11,8 @@ class pa_cdcc(Variable): defined_for = StateCode.PA def formula(tax_unit, period, parameters): - # Get the federal CDCC value - # Pennsylvania matches the potential federal credit - cdcc = tax_unit("cdcc_potential", period) + # Pennsylvania matches the federal CDCC actually claimed. + cdcc = tax_unit("cdcc", period) # Access the parameter path p = parameters(period).gov.states.pa.tax.income.credits.cdcc # Multiply the federal CDCC value by the Pennsylvania CDCC rate diff --git a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py new file mode 100644 index 00000000000..4a3e8904107 --- /dev/null +++ b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py @@ -0,0 +1,70 @@ +"""Shared helpers for state EITC formulas that partially track federal rules.""" + +from policyengine_us.model_api import * + + +def eitc_filing_requirement_met(tax_unit, period): + """Mirror the federal EITC filing condition used in the baseline formula.""" + + is_required = tax_unit("tax_unit_is_required_to_file", period) + files_voluntarily = tax_unit("would_file_taxes_voluntarily", period) + would_file_for_credits = tax_unit( + "would_file_if_eligible_for_refundable_credit", period + ) + return is_required | files_voluntarily | would_file_for_credits + + +def eitc_filing_status_eligible( + tax_unit, period, parameters, separate_filer_eligible=None +): + """Apply the federal EITC separate-filer rule unless a state overrides it.""" + + if separate_filer_eligible is None: + separate_filer_eligible = ( + parameters.gov.irs.credits.eitc.eligibility.separate_filer(period) + ) + if separate_filer_eligible: + return True + filing_status = tax_unit("filing_status", period) + return filing_status != filing_status.possible_values.SEPARATE + + +def calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_identification_eligible, + separate_filer_eligible=None, +): + """Calculate a federal-style EITC amount using state-specific child/ID rules.""" + + eitc = parameters(period).gov.irs.credits.eitc + earnings = tax_unit("filer_adjusted_earnings", period) + agi = tax_unit("adjusted_gross_income", period) + maximum = eitc.max.calc(child_count) + phase_in_rate = eitc.phase_in_rate.calc(child_count) + phased_in = min_(maximum, earnings * phase_in_rate) + phase_out_start = eitc.phase_out.start.calc(child_count) + phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc.phase_out.joint_bonus.calc( + child_count + ) + phase_out_rate = eitc.phase_out.rate.calc(child_count) + reduction = max_(0, max_(earnings, agi) - phase_out_start) * phase_out_rate + limitation = max_(0, maximum - reduction) + investment_income_eligible = tax_unit("eitc_investment_income_eligible", period) + filing_status_eligible = eitc_filing_status_eligible( + tax_unit, period, parameters, separate_filer_eligible + ) + is_filer = eitc_filing_requirement_met(tax_unit, period) + takes_up_eitc = tax_unit("takes_up_eitc", period) + return ( + min_(phased_in, limitation) + * demographic_eligible + * filer_identification_eligible + * investment_income_eligible + * filing_status_eligible + * is_filer + * takes_up_eitc + ) diff --git a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py index 7c7c584b279..1e1b4ce0ad2 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py @@ -13,10 +13,8 @@ def formula(tax_unit, period, parameters): # States that adopt the federal itemized deductions # Based on comments in state_itemized_deductions.yaml FEDERAL_ITEMIZED_DEDUCTION_STATES = [ - "CT", # Connecticut "GA", # Georgia "ND", # North Dakota - "SC", # South Carolina "UT", # Utah ] @@ -61,12 +59,7 @@ def formula(tax_unit, period, parameters): # Check if the state adopts federal itemized deductions uses_federal = np.isin(state_code, FEDERAL_ITEMIZED_DEDUCTION_STATES) - # Get federal itemized deductions (sum of all federal itemized deduction components) - federal_itemized = add( - tax_unit, - period, - parameters(period).gov.irs.deductions.itemized_deductions, - ) + federal_itemized = tax_unit("itemized_taxable_income_deductions", period) # Return federal itemized deductions for states that adopt them, otherwise state-specific return where(uses_federal, federal_itemized, state_specific_base) diff --git a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py index 32cdf244fd0..51fd2021cb3 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py +++ b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py @@ -12,12 +12,10 @@ def formula(tax_unit, period, parameters): # States that adopt the federal standard deduction # Based on comments in state_standard_deductions.yaml FEDERAL_STANDARD_DEDUCTION_STATES = [ - "CT", # Connecticut "ID", # Idaho "MO", # Missouri "ND", # North Dakota "NM", # New Mexico - "SC", # South Carolina "UT", # Utah "CO", # Colorado ] diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py index b466f8c3f0d..635e33404b7 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py @@ -12,27 +12,12 @@ class ut_federal_deductions_for_taxpayer_credit(Variable): reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S114.html?v=C59-10-S114_2022032320220323" def formula(tax_unit, period, parameters): - p = parameters(period).gov.irs.deductions - filing_status = tax_unit("filing_status", period) - us_itemizing = tax_unit("tax_unit_itemizes", period) std_ded = tax_unit("standard_deduction", period) - - # Subtract SALT from Itemized Deductions - deductions = [ - deduction - for deduction in p.itemized_deductions - if deduction not in ["salt_deduction"] - ] - item_ded = add(tax_unit, period, deductions) - - # Include Real Estate Taxes in Itemized Deductions - real_estate_tax = add(tax_unit, period, ["real_estate_taxes"]) - - capped_real_estate_tax = min_( - real_estate_tax, p.itemized.salt_and_real_estate.cap[filing_status] + itemized = tax_unit("itemized_taxable_income_deductions", period) + state_income_or_sales_tax = tax_unit( + "state_and_local_sales_or_income_tax", period + ) + return where( + us_itemizing, max_(itemized - state_income_or_sales_tax, 0), std_ded ) - - # Line 12. Federal Standard or Itemized Deductions - total_item_ded = item_ded + capped_real_estate_tax - return where(us_itemizing, total_item_ded, std_ded) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py index 0619d85cdf5..33dfe76e8a9 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py @@ -14,4 +14,5 @@ class ut_eitc_potential(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ut.tax.income.credits.earned_income federal_eitc = tax_unit("eitc", period) - return p.rate * federal_eitc + wages = add(tax_unit, period, ["employment_income"]) + return min_(p.rate * federal_eitc, wages) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 4a4a40d95e6..4e83abe9c0a 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_like_amount, +) class wa_working_families_tax_credit(Variable): @@ -14,29 +17,79 @@ class wa_working_families_tax_credit(Variable): defined_for = StateCode.WA def formula(tax_unit, period, parameters): + person = tax_unit.members + has_tin = person("has_tin", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + child_count = tax_unit.sum( + person("is_qualifying_child_dependent", period) & has_tin + ) + filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + federal_identification_eligible = tax_unit( + "filer_meets_eitc_identification_requirements", period + ) + filing_status = tax_unit("filing_status", period) + separate = filing_status == filing_status.possible_values.SEPARATE + federal_child_count = tax_unit("eitc_child_count", period) + age = person("age", period) + student = person("is_full_time_student", period) + min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) + min_age_student = parameters.gov.irs.credits.eitc.eligibility.age.min_student( + period + ) + max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) + age_floor = where(student, min_age_student, min_age) + demographic_eligible = (child_count > 0) | tax_unit.any( + is_head_or_spouse & (age >= age_floor) & (age <= max_age) + ) + # Baseline eligibility: filers who claim EITC eitc = tax_unit("eitc", period) eitc_eligible = eitc > 0 + needs_state_only_path = ( + (~federal_identification_eligible & filer_has_tin) + | separate + | (child_count > federal_child_count) + ) + state_only_eitc_eligible = needs_state_only_path & ( + calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_has_tin, + separate_filer_eligible=True, + ) + > 0 + ) # ESSB 6346 Sec. 901: age expansion eligibility (effective 2029) age_expansion_eligible = tax_unit( "wa_working_families_tax_credit_age_expansion_eligible", period ) - eligible = eitc_eligible | age_expansion_eligible + eligible = eitc_eligible | state_only_eitc_eligible | age_expansion_eligible # Parameters are based on EITC-eligible children. p = parameters( period ).gov.states.wa.tax.income.credits.working_families_tax_credit - eitc_child_count = tax_unit("eitc_child_count", period) + eitc_child_count = max_(federal_child_count, child_count) max_amount = p.amount.calc(eitc_child_count) # WFTC phases out at a certain amount below the EITC maximum AGI. # NB: The Revised Code of Washington is ambiguous: # "below the federal phase-out income" # The legislative analysis clarifies that this refers to "federal maximum AGI" # https://lawfilesext.leg.wa.gov/biennium/2021-22/Pdf/Bill%20Reports/House/1297-S.E%20HBR%20FBR%2021.pdf?q=20220706071752 - eitc_agi_limit = tax_unit("eitc_agi_limit", period) + eitc_parameters = parameters(period).gov.irs.credits.eitc + phase_out_start = eitc_parameters.phase_out.start.calc(eitc_child_count) + phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc_parameters.phase_out.joint_bonus.calc( + eitc_child_count + ) + eitc_agi_limit = phase_out_start + eitc_parameters.max.calc( + eitc_child_count + ) / eitc_parameters.phase_out.rate.calc(eitc_child_count) + eitc_agi_limit = max_(tax_unit("eitc_agi_limit", period), eitc_agi_limit) phase_out_start_reduction = p.phase_out.start_below_eitc.calc(eitc_child_count) phase_out_start = eitc_agi_limit - phase_out_start_reduction # The phase-out rates are hard-coded in the legal code, but HB 1888 (2021-22) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index 45a30b07bf3..69aa85ed402 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -17,35 +17,35 @@ def formula(tax_unit, period, parameters): ).gov.states.wa.tax.income.credits.working_families_tax_credit.age_expansion expansion_in_effect = p.in_effect + person = tax_unit.members age_head = tax_unit("age_head", period) age_spouse = tax_unit("age_spouse", period) filer_meets_min_age = (age_head >= p.min_age) | (age_spouse >= p.min_age) investment_income_eligible = tax_unit("eitc_investment_income_eligible", period) - filers_have_ssn = tax_unit( - "filer_meets_eitc_identification_requirements", period + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + has_tin = person("has_tin", period) + filers_have_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + child_count = tax_unit.sum( + person("is_qualifying_child_dependent", period) & has_tin ) - eitc_amount_before_take_up = min_( - tax_unit("eitc_phased_in", period), - max_( - 0, - tax_unit("eitc_maximum", period) - tax_unit("eitc_reduction", period), - ), + eitc = parameters(period).gov.irs.credits.eitc + earnings = tax_unit("filer_adjusted_earnings", period) + agi = tax_unit("adjusted_gross_income", period) + maximum = eitc.max.calc(child_count) + phased_in = min_(maximum, earnings * eitc.phase_in_rate.calc(child_count)) + phase_out_start = eitc.phase_out.start.calc(child_count) + phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc.phase_out.joint_bonus.calc( + child_count ) - - eitc = parameters.gov.irs.credits.eitc(period) - if eitc.eligibility.separate_filer: - filing_status_eligible = True - else: - filing_status = tax_unit("filing_status", period) - filing_status_eligible = ( - filing_status != filing_status.possible_values.SEPARATE - ) + reduction = max_(0, max_(earnings, agi) - phase_out_start) * eitc.phase_out.rate.calc( + child_count + ) + eitc_amount_before_take_up = min_(phased_in, max_(0, maximum - reduction)) return ( expansion_in_effect & filer_meets_min_age & investment_income_eligible - & filers_have_ssn + & filers_have_tin & (eitc_amount_before_take_up > 0) - & filing_status_eligible ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py index cf0edd3f5db..f0de0325fd5 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py @@ -11,6 +11,7 @@ class head_of_household_eligible(Variable): def formula(tax_unit, period, parameters): married = tax_unit("tax_unit_married", period) person = tax_unit.members + is_separated = tax_unit.any(person("is_separated", period)) # Qualifying children and permanently disabled always count is_qualifying_child = person("is_qualifying_child_dependent", period) is_disabled_dependent = person( @@ -25,8 +26,10 @@ def formula(tax_unit, period, parameters): | (is_qualifying_relative & is_related) ) has_qualifying_person = tax_unit.sum(is_hoh_qualifying) > 0 - return ( - has_qualifying_person - & ~married - & ~tax_unit("surviving_spouse_eligible", period) - ) + # IRC 7703(b) treated-unmarried status supports HoH only through the + # child-abode path, not the broader qualifying-relative route. + treated_unmarried_qualifies = tax_unit.sum(is_qualifying_child) > 0 + surviving_spouse = tax_unit("surviving_spouse_eligible", period) + unmarried_qualifies = has_qualifying_person & ~married & ~is_separated + separated_qualifies = treated_unmarried_qualifies & is_separated + return (unmarried_qualifies | separated_qualifies) & ~surviving_spouse From a427a51c6e4d63ac4b0e2d65b8ffdf306fe413bf Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 7 May 2026 12:19:47 -0400 Subject: [PATCH 02/17] Freeze state EITC conformity to federal law dates --- .../hi_student_loan_interest_adjustment.yaml | 53 ++++- .../gov/states/in/tax/income/in_eitc.yaml | 14 ++ .../in/tax/income/in_eitc_eligible.yaml | 15 ++ .../wa_working_families_tax_credit.yaml | 209 +++++++++++++++--- .../ga_itemized_deductions_adjustment.py | 7 + .../hi_student_loan_interest_adjustment.py | 60 ++++- .../credits/earned_income_credit/in_eitc.py | 29 +++ .../earned_income_credit/in_eitc_eligible.py | 29 +++ .../oh_educator_expense_deduction_person.py | 10 + .../states/tax/income/credits/eitc_helpers.py | 100 +++++++-- .../credits/wa_working_families_tax_credit.py | 83 ++++--- ...ilies_tax_credit_age_expansion_eligible.py | 23 +- 12 files changed, 532 insertions(+), 100 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml index 7ad2c9f1a36..08bd05deae6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml @@ -1,17 +1,50 @@ -- name: Hawaii can add back student loan interest when the Hawaii allowance is smaller than federal +- name: Hawaii adds back part of the federal student loan deduction when Hawaii's deduction is smaller period: 2025 + absolute_error_margin: 0.1 input: - state_code: HI - adjusted_gross_income: 1_000 - hi_student_loan_interest_adjustment: -100 + people: + head: + age: 30 + employment_income: 60_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: HI output: - hi_agi: 1_100 + hi_student_loan_interest_adjustment: -1250 + hi_agi: 58_750 -- name: Hawaii can subtract more student loan interest when the Hawaii allowance is larger than federal +- name: Hawaii subtracts more than federal when Hawaii AGI excludes pension income period: 2025 + absolute_error_margin: 0.2 input: - state_code: HI - adjusted_gross_income: 1_000 - hi_student_loan_interest_adjustment: 75 + people: + head: + age: 30 + employment_income: 60_000 + taxable_pension_income: 40_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: HI output: - hi_agi: 925 + hi_student_loan_interest_adjustment: 833.33 + hi_agi: 19_166.67 diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml index 4ee1dd8e2e6..0b29a13315f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml @@ -32,3 +32,17 @@ employment_income: 19_500 output: in_eitc: 0.09 * 5_980 + +- name: in_eitc unit test 4 + absolute_error_margin: 0.01 + period: 2025 + input: + state_code: IN + eitc: 0 + in_eitc_eligible: true + filing_status: HEAD_OF_HOUSEHOLD + eitc_child_count: 1 + filer_adjusted_earnings: 10_000 + adjusted_gross_income: 10_000 + output: + in_eitc: 340 diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc_eligible.yaml index f0d512184da..24442fa4938 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc_eligible.yaml @@ -89,3 +89,18 @@ dividend_income: 3_600 output: in_eitc_eligible: false + +- name: in_eitc_eligible unit test 8 + period: 2025 + input: + state_code: IN + eitc: 0 + filing_status: HEAD_OF_HOUSEHOLD + eitc_child_count: 1 + filer_adjusted_earnings: 10_000 + adjusted_gross_income: 10_000 + filer_meets_eitc_identification_requirements: true + would_file_taxes_voluntarily: true + takes_up_eitc: true + output: + in_eitc_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index 67898d42295..4bc7326fbba 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -9,14 +9,30 @@ output: wa_working_families_tax_credit: 0 -- name: Case 2, single eligible person with no income gets maximum credit. +- name: Case 2, single eligible person with low income gets maximum credit. period: 2022 absolute_error_margin: 0 input: - state_code: WA - eitc: 1 - filer_adjusted_earnings: 0 - eitc_child_count: 0 + people: + head: + age: 30 + employment_income: 1 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA output: wa_working_families_tax_credit: 300 @@ -33,28 +49,68 @@ - name: Case 4, at EITC AGI limit credit phases to $50 minimum. period: 2024 - absolute_error_margin: 0.01 + absolute_error_margin: 1 input: - state_code: WA - eitc: 1 - eitc_child_count: 1 - eitc_agi_limit: 50_000 - filer_adjusted_earnings: 50_000 + people: + head: + age: 30 + employment_income: 43_490 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: WA output: # Per RCW 82.08.0206(3)(f), minimum credit is $50 wa_working_families_tax_credit: 50 - name: Case 5, mid-phaseout gives correctly reduced credit. period: 2024 - absolute_error_margin: 0.01 + absolute_error_margin: 0.2 input: - state_code: WA - eitc: 1 - eitc_child_count: 1 - eitc_agi_limit: 50_000 - filer_adjusted_earnings: 47_500 + people: + head: + age: 30 + employment_income: 40_992 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: WA output: - wa_working_families_tax_credit: 345 + wa_working_families_tax_credit: 344.82 - name: Case 6, joint filer with 1 child and moderate income receives credit. period: 2024 @@ -64,15 +120,23 @@ filer: employment_income: 40_000 age: 30 + has_tin: true + ssn_card_type: CITIZEN spouse: - dividend_income: 11_000 + employment_income: 0 age: 30 + has_tin: true + ssn_card_type: CITIZEN child: age: 5 + has_tin: true + ssn_card_type: CITIZEN tax_units: tax_unit: members: [filer, spouse, child] filing_status: JOINT + would_file_taxes_voluntarily: true + takes_up_eitc: true families: family: members: [filer, spouse, child] @@ -118,11 +182,26 @@ - name: Case 8, age 17 without EITC gets no WFTC in 2029. period: 2029 input: - state_code: WA - age_head: 17 - eitc: 0 - filer_adjusted_earnings: 10_000 - eitc_child_count: 0 + people: + head: + age: 17 + employment_income: 10_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA output: # Not EITC eligible and under age 18 minimum wa_working_families_tax_credit: 0 @@ -142,11 +221,26 @@ - name: Case 10, WFTC age expansion not in effect before 2029. period: 2028 input: - state_code: WA - age_head: 20 - eitc: 0 - filer_adjusted_earnings: 10_000 - eitc_child_count: 0 + people: + head: + age: 20 + employment_income: 10_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA output: # Age expansion starts 2029, no EITC eligibility wa_working_families_tax_credit: 0 @@ -155,10 +249,26 @@ period: 2029 absolute_error_margin: 1 input: - state_code: WA - eitc: 1 - filer_adjusted_earnings: 0 - eitc_child_count: 0 + people: + head: + age: 30 + employment_income: 10_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA output: # Standard EITC pathway still works # Amount is inflation-adjusted from $300 (2022) to ~$367 (2029) @@ -220,3 +330,36 @@ state_code: WA output: wa_working_families_tax_credit: 660 + +- name: Case 15, standard SSN filer still gets WFTC under frozen 2022 federal law when current federal EITC is overridden to zero. + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 30 + employment_income: 10_000 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_head: true + child: + age: 5 + has_tin: true + ssn_card_type: CITIZEN + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + eitc: 0 + would_file_taxes_voluntarily: true + takes_up_eitc: true + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: WA + output: + wa_working_families_tax_credit: 660 diff --git a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py index 8d253b52449..993cdfd255f 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py @@ -9,3 +9,10 @@ class ga_itemized_deductions_adjustment(Variable): definition_period = YEAR default_value = 0 defined_for = StateCode.GA + documentation = ( + "Georgia-specific reduction from federal Schedule A before computing " + "Georgia itemized deductions. This remains an explicit input because " + "the model does not separately observe components such as other-state " + "income taxes and exempt-income investment interest." + ) + reference = "https://dor.georgia.gov/document/document/2025-it-511-individual-income-tax-booklet/download" diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py index 7f5b5324d7c..07b720a4a98 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -1,14 +1,72 @@ from policyengine_us.model_api import * +class hi_student_loan_interest_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii student loan interest deduction" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" + + def formula(tax_unit, period, parameters): + person = tax_unit.members + eligible = person("student_loan_interest_ald_eligible", period) + interest_paid = tax_unit.sum(person("student_loan_interest", period) * eligible) + capped_interest = min_(interest_paid, 2_500) + + p = parameters(period).gov.states.hi.tax.income + other_additions = [ + variable + for variable in p.additions.additions + if variable != "hi_student_loan_interest_addition" + ] + other_subtractions = [ + variable + for variable in p.subtractions.subtractions + if variable != "hi_student_loan_interest_subtraction" + ] + other_additions_amount = ( + add(tax_unit, period, other_additions) if len(other_additions) > 0 else 0 + ) + other_subtractions_amount = ( + add(tax_unit, period, other_subtractions) + if len(other_subtractions) > 0 + else 0 + ) + hi_magi = tax_unit("adjusted_gross_income", period) + hi_magi += other_additions_amount + hi_magi -= other_subtractions_amount + + filing_status = tax_unit("filing_status", period) + status = filing_status.possible_values + joint = filing_status == status.JOINT + separate = filing_status == status.SEPARATE + phase_out_start = where(joint, 100_000, 50_000) + phase_out_divisor = where(joint, 30_000, 15_000) + reduction_share = np.minimum( + 1.0, np.maximum(0.0, (hi_magi - phase_out_start) / phase_out_divisor) + ) + deduction = capped_interest * (1 - reduction_share) + return where(separate, 0, deduction) + + class hi_student_loan_interest_adjustment(Variable): value_type = float entity = TaxUnit label = "Hawaii student loan interest adjustment relative to federal AGI" unit = USD definition_period = YEAR - default_value = 0 defined_for = StateCode.HI + reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" + + def formula(tax_unit, period, parameters): + hawaii_deduction = tax_unit("hi_student_loan_interest_deduction", period) + federal_deduction = tax_unit.sum( + tax_unit.members("student_loan_interest_ald", period) + ) + return hawaii_deduction - federal_deduction class hi_student_loan_interest_subtraction(Variable): diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index dd240a434ad..5e414c7f57d 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -1,4 +1,8 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_demographic_eligibility, + calculate_eitc_like_amount, +) class in_eitc(Variable): @@ -15,6 +19,31 @@ def formula(tax_unit, period, parameters): if not ip.earned_income.decoupled: federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate + if period.start.year >= 2023: + frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") + child_count = tax_unit("eitc_child_count", period) + demographic_eligible = calculate_eitc_demographic_eligibility( + tax_unit, period, frozen_eitc, child_count + ) + filer_identification_eligible = tax_unit( + "filer_meets_eitc_identification_requirements", period + ) + investment_income_eligible = ( + tax_unit("eitc_relevant_investment_income", period) + <= frozen_eitc.phase_out.max_investment_income + ) + frozen_federal_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_identification_eligible, + separate_filer_eligible=frozen_eitc.eligibility.separate_filer, + eitc_parameters=frozen_eitc, + investment_income_eligible=investment_income_eligible, + ) + return frozen_federal_eitc * ip.earned_income.match_rate # if Indiana EITC is decoupled from federal EITC fp = parameters(period).gov.irs.credits # ... cap child count diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index 0936388f7cc..db2c8230f68 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -1,4 +1,8 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_demographic_eligibility, + calculate_eitc_like_amount, +) class in_eitc_eligible(Variable): @@ -16,6 +20,31 @@ def formula(tax_unit, period, parameters): gets_federal_eitc = tax_unit("eitc", period) > 0 if not p.credits.earned_income.decoupled: return gets_federal_eitc + if period.start.year >= 2023: + frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") + child_count = tax_unit("eitc_child_count", period) + demographic_eligible = calculate_eitc_demographic_eligibility( + tax_unit, period, frozen_eitc, child_count + ) + filer_identification_eligible = tax_unit( + "filer_meets_eitc_identification_requirements", period + ) + investment_income_eligible = ( + tax_unit("eitc_relevant_investment_income", period) + <= frozen_eitc.phase_out.max_investment_income + ) + frozen_federal_eitc = calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_identification_eligible, + separate_filer_eligible=frozen_eitc.eligibility.separate_filer, + eitc_parameters=frozen_eitc, + investment_income_eligible=investment_income_eligible, + ) + return frozen_federal_eitc > 0 # if Indiana EITC is decoupled from federal EITC # ... check separate filing status filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py index 33d281ba7cb..1cb9e92cecb 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py @@ -9,3 +9,13 @@ class oh_educator_expense_deduction_person(Variable): definition_period = YEAR default_value = 0 defined_for = StateCode.OH + documentation = ( + "Ohio-specific educator deduction allowed in excess of the federal " + "educator deduction for a qualifying Ohio educator. This remains an " + "explicit input because the baseline data do not identify Ohio " + "licensure/teaching status or expenses above the federal deduction." + ) + reference = ( + "https://codes.ohio.gov/ohio-revised-code/section-5747.01", + "https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf", + ) diff --git a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py index 4a3e8904107..694c96d96c1 100644 --- a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py +++ b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py @@ -29,38 +29,108 @@ def eitc_filing_status_eligible( return filing_status != filing_status.possible_values.SEPARATE -def calculate_eitc_like_amount( +def calculate_eitc_demographic_eligibility( + tax_unit, period, eitc_parameters, child_count=None +): + """Apply the EITC child-count and age tests using a specific law version.""" + + if child_count is None: + child_count = tax_unit("eitc_child_count", period) + has_child = child_count > 0 + person = tax_unit.members + age = person("age", period) + student = person("is_full_time_student", period) + min_age = where( + student, + eitc_parameters.eligibility.age.min_student, + eitc_parameters.eligibility.age.min, + ) + max_age = eitc_parameters.eligibility.age.max + return has_child | tax_unit.any((age >= min_age) & (age <= max_age)) + + +def calculate_eitc_phase_out_start( tax_unit, period, - parameters, + eitc_parameters, child_count, - demographic_eligible, - filer_identification_eligible, - separate_filer_eligible=None, ): - """Calculate a federal-style EITC amount using state-specific child/ID rules.""" + """Return the EITC phase-out starting point for a chosen law version.""" + + phase_out_start = eitc_parameters.phase_out.start.calc(child_count) + phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc_parameters.phase_out.joint_bonus.calc( + child_count + ) + return phase_out_start + + +def calculate_eitc_amount_from_parameters( + tax_unit, + period, + eitc_parameters, + child_count, +): + """Calculate the uncapped EITC amount under a chosen parameter set.""" - eitc = parameters(period).gov.irs.credits.eitc earnings = tax_unit("filer_adjusted_earnings", period) agi = tax_unit("adjusted_gross_income", period) - maximum = eitc.max.calc(child_count) - phase_in_rate = eitc.phase_in_rate.calc(child_count) + maximum = eitc_parameters.max.calc(child_count) + phase_in_rate = eitc_parameters.phase_in_rate.calc(child_count) phased_in = min_(maximum, earnings * phase_in_rate) - phase_out_start = eitc.phase_out.start.calc(child_count) - phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc.phase_out.joint_bonus.calc( - child_count + phase_out_start = calculate_eitc_phase_out_start( + tax_unit, period, eitc_parameters, child_count ) - phase_out_rate = eitc.phase_out.rate.calc(child_count) + phase_out_rate = eitc_parameters.phase_out.rate.calc(child_count) reduction = max_(0, max_(earnings, agi) - phase_out_start) * phase_out_rate limitation = max_(0, maximum - reduction) - investment_income_eligible = tax_unit("eitc_investment_income_eligible", period) + return min_(phased_in, limitation) + + +def calculate_eitc_max_agi_limit( + tax_unit, + period, + eitc_parameters, + child_count, +): + """Return the maximum AGI at which a positive EITC remains available.""" + + phase_out_start = calculate_eitc_phase_out_start( + tax_unit, period, eitc_parameters, child_count + ) + return phase_out_start + eitc_parameters.max.calc( + child_count + ) / eitc_parameters.phase_out.rate.calc(child_count) + + +def calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + demographic_eligible, + filer_identification_eligible, + separate_filer_eligible=None, + eitc_parameters=None, + investment_income_eligible=None, +): + """Calculate a federal-style EITC amount using state-specific child/ID rules.""" + + if eitc_parameters is None: + eitc_parameters = parameters(period).gov.irs.credits.eitc + if investment_income_eligible is None: + investment_income_eligible = ( + tax_unit("eitc_relevant_investment_income", period) + <= eitc_parameters.phase_out.max_investment_income + ) filing_status_eligible = eitc_filing_status_eligible( tax_unit, period, parameters, separate_filer_eligible ) is_filer = eitc_filing_requirement_met(tax_unit, period) takes_up_eitc = tax_unit("takes_up_eitc", period) return ( - min_(phased_in, limitation) + calculate_eitc_amount_from_parameters( + tax_unit, period, eitc_parameters, child_count + ) * demographic_eligible * filer_identification_eligible * investment_income_eligible diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 4e83abe9c0a..4c2855b643a 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -1,6 +1,9 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( - calculate_eitc_like_amount, + calculate_eitc_demographic_eligibility, + calculate_eitc_max_agi_limit, + eitc_filing_requirement_met, + eitc_filing_status_eligible, ) @@ -17,6 +20,7 @@ class wa_working_families_tax_credit(Variable): defined_for = StateCode.WA def formula(tax_unit, period, parameters): + frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") person = tax_unit.members has_tin = person("has_tin", period) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) @@ -32,35 +36,65 @@ def formula(tax_unit, period, parameters): federal_child_count = tax_unit("eitc_child_count", period) age = person("age", period) student = person("is_full_time_student", period) - min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) - min_age_student = parameters.gov.irs.credits.eitc.eligibility.age.min_student( - period - ) - max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) + min_age = frozen_eitc.eligibility.age.min + min_age_student = frozen_eitc.eligibility.age.min_student + max_age = frozen_eitc.eligibility.age.max age_floor = where(student, min_age_student, min_age) demographic_eligible = (child_count > 0) | tax_unit.any( is_head_or_spouse & (age >= age_floor) & (age <= max_age) ) + federal_demographic_eligible = calculate_eitc_demographic_eligibility( + tax_unit, period, frozen_eitc, federal_child_count + ) + frozen_investment_income_eligible = ( + tax_unit("eitc_relevant_investment_income", period) + <= frozen_eitc.phase_out.max_investment_income + ) + earnings = tax_unit("filer_adjusted_earnings", period) + agi = tax_unit("adjusted_gross_income", period) + higher_income = max_(earnings, agi) + is_filer = eitc_filing_requirement_met(tax_unit, period) + takes_up_eitc = tax_unit("takes_up_eitc", period) + baseline_income_eligible = (earnings > 0) & ( + higher_income + <= calculate_eitc_max_agi_limit( + tax_unit, period, frozen_eitc, federal_child_count + ) + ) - # Baseline eligibility: filers who claim EITC - eitc = tax_unit("eitc", period) - eitc_eligible = eitc > 0 + # Baseline eligibility: filers who qualify under the frozen 2022 IRC. + eitc_eligible = ( + baseline_income_eligible + & federal_demographic_eligible + & federal_identification_eligible + & frozen_investment_income_eligible + & eitc_filing_status_eligible( + tax_unit, + period, + parameters, + frozen_eitc.eligibility.separate_filer, + ) + & is_filer + & takes_up_eitc + ) needs_state_only_path = ( (~federal_identification_eligible & filer_has_tin) | separate | (child_count > federal_child_count) ) - state_only_eitc_eligible = needs_state_only_path & ( - calculate_eitc_like_amount( - tax_unit, - period, - parameters, - child_count, - demographic_eligible, - filer_has_tin, - separate_filer_eligible=True, + state_only_income_eligible = (earnings > 0) & ( + higher_income + <= calculate_eitc_max_agi_limit( + tax_unit, period, frozen_eitc, child_count ) - > 0 + ) + state_only_eitc_eligible = needs_state_only_path & ( + state_only_income_eligible + & demographic_eligible + & filer_has_tin + & frozen_investment_income_eligible + & is_filer + & takes_up_eitc ) # ESSB 6346 Sec. 901: age expansion eligibility (effective 2029) @@ -81,22 +115,15 @@ def formula(tax_unit, period, parameters): # "below the federal phase-out income" # The legislative analysis clarifies that this refers to "federal maximum AGI" # https://lawfilesext.leg.wa.gov/biennium/2021-22/Pdf/Bill%20Reports/House/1297-S.E%20HBR%20FBR%2021.pdf?q=20220706071752 - eitc_parameters = parameters(period).gov.irs.credits.eitc - phase_out_start = eitc_parameters.phase_out.start.calc(eitc_child_count) - phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc_parameters.phase_out.joint_bonus.calc( - eitc_child_count + eitc_agi_limit = calculate_eitc_max_agi_limit( + tax_unit, period, frozen_eitc, eitc_child_count ) - eitc_agi_limit = phase_out_start + eitc_parameters.max.calc( - eitc_child_count - ) / eitc_parameters.phase_out.rate.calc(eitc_child_count) - eitc_agi_limit = max_(tax_unit("eitc_agi_limit", period), eitc_agi_limit) phase_out_start_reduction = p.phase_out.start_below_eitc.calc(eitc_child_count) phase_out_start = eitc_agi_limit - phase_out_start_reduction # The phase-out rates are hard-coded in the legal code, but HB 1888 (2021-22) # instructs DOR to revise it to get to the minimum amount by the EITC AGI limit. # https://app.leg.wa.gov/billsummary?BillNumber=1888&Year=2021&Initiative=false phase_out_rate = (max_amount - p.min_amount) / phase_out_start_reduction - earnings = tax_unit("filer_adjusted_earnings", period) excess = max_(0, earnings - phase_out_start) reduction = max_(0, excess * phase_out_rate) phased_out_amount = max_amount - reduction diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index 69aa85ed402..363c6e68a0a 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( + calculate_eitc_amount_from_parameters, +) class wa_working_families_tax_credit_age_expansion_eligible(Variable): @@ -21,31 +24,25 @@ def formula(tax_unit, period, parameters): age_head = tax_unit("age_head", period) age_spouse = tax_unit("age_spouse", period) filer_meets_min_age = (age_head >= p.min_age) | (age_spouse >= p.min_age) - investment_income_eligible = tax_unit("eitc_investment_income_eligible", period) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) has_tin = person("has_tin", period) filers_have_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 child_count = tax_unit.sum( person("is_qualifying_child_dependent", period) & has_tin ) - eitc = parameters(period).gov.irs.credits.eitc - earnings = tax_unit("filer_adjusted_earnings", period) - agi = tax_unit("adjusted_gross_income", period) - maximum = eitc.max.calc(child_count) - phased_in = min_(maximum, earnings * eitc.phase_in_rate.calc(child_count)) - phase_out_start = eitc.phase_out.start.calc(child_count) - phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc.phase_out.joint_bonus.calc( - child_count + frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") + frozen_investment_income_eligible = ( + tax_unit("eitc_relevant_investment_income", period) + <= frozen_eitc.phase_out.max_investment_income ) - reduction = max_(0, max_(earnings, agi) - phase_out_start) * eitc.phase_out.rate.calc( - child_count + eitc_amount_before_take_up = calculate_eitc_amount_from_parameters( + tax_unit, period, frozen_eitc, child_count ) - eitc_amount_before_take_up = min_(phased_in, max_(0, maximum - reduction)) return ( expansion_in_effect & filer_meets_min_age - & investment_income_eligible + & frozen_investment_income_eligible & filers_have_tin & (eitc_amount_before_take_up > 0) ) From 7957444c4d35d68b03c8a3f7adc192f73be49b8c Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 8 May 2026 12:43:29 -0400 Subject: [PATCH 03/17] Fix PR CI regressions --- changelog.d/8243.fixed.md | 1 + .../hi/tax/income/additions/additions.yaml | 2 +- .../tax/income/subtractions/subtractions.yaml | 5 ++++ .../eitc/dc_eitc_without_qualifying_child.py | 6 ++--- .../hi_student_loan_interest_adjustment.py | 4 ++++ .../states/tax/income/credits/eitc_helpers.py | 6 ++--- .../tax/income/state_itemized_deductions.py | 21 ++++++++++++++-- .../tax/income/state_standard_deduction.py | 5 +++- ..._federal_deductions_for_taxpayer_credit.py | 24 ++++++++++++++++--- .../credits/wa_working_families_tax_credit.py | 4 +--- 10 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 changelog.d/8243.fixed.md diff --git a/changelog.d/8243.fixed.md b/changelog.d/8243.fixed.md new file mode 100644 index 00000000000..afae3988212 --- /dev/null +++ b/changelog.d/8243.fixed.md @@ -0,0 +1 @@ +Fixed federal/state conformity across EITC, deduction, ALD, HoH, and CDCC linkages, including static-conformity handling and state-specific deduction hookups. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml index ad98c27767a..87c472c3372 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml @@ -1,6 +1,6 @@ description: Hawaii additions to federal adjusted gross income. values: - 2021-01-01: + 2025-01-01: - hi_student_loan_interest_addition metadata: diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml index 9c24560e8ab..c8901b12f37 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml @@ -5,6 +5,11 @@ values: - taxable_social_security # Line 14 - hi_military_pay_exclusion # Line 15 - us_govt_interest # Line 18 + 2025-01-01: + - taxable_pension_income # Line 13 + - taxable_social_security # Line 14 + - hi_military_pay_exclusion # Line 15 + - us_govt_interest # Line 18 - hi_student_loan_interest_subtraction # Line 18 diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py index 5bab5dc8193..736280acfd6 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py @@ -25,9 +25,9 @@ def formula(tax_unit, period, parameters): min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) age_eligible = is_head_or_spouse & (age >= min_age) & (age <= max_age) - no_qualifying_child = tax_unit.sum( - person("is_qualifying_child_dependent", period) & has_tin - ) == 0 + no_qualifying_child = ( + tax_unit.sum(person("is_qualifying_child_dependent", period) & has_tin) == 0 + ) us_eligible = ( filer_has_tin & no_qualifying_child diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py index 07b720a4a98..057c1521b11 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -11,6 +11,8 @@ class hi_student_loan_interest_deduction(Variable): reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" def formula(tax_unit, period, parameters): + if period.start.year < 2025: + return 0 person = tax_unit.members eligible = person("student_loan_interest_ald_eligible", period) interest_paid = tax_unit.sum(person("student_loan_interest", period) * eligible) @@ -62,6 +64,8 @@ class hi_student_loan_interest_adjustment(Variable): reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" def formula(tax_unit, period, parameters): + if period.start.year < 2025: + return 0 hawaii_deduction = tax_unit("hi_student_loan_interest_deduction", period) federal_deduction = tax_unit.sum( tax_unit.members("student_loan_interest_ald", period) diff --git a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py index 694c96d96c1..f13c93f5e91 100644 --- a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py +++ b/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py @@ -58,9 +58,9 @@ def calculate_eitc_phase_out_start( """Return the EITC phase-out starting point for a chosen law version.""" phase_out_start = eitc_parameters.phase_out.start.calc(child_count) - phase_out_start += tax_unit("tax_unit_is_joint", period) * eitc_parameters.phase_out.joint_bonus.calc( - child_count - ) + phase_out_start += tax_unit( + "tax_unit_is_joint", period + ) * eitc_parameters.phase_out.joint_bonus.calc(child_count) return phase_out_start diff --git a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py index 1e1b4ce0ad2..dac84175166 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py @@ -10,6 +10,7 @@ class state_itemized_deductions(Variable): adds = "gov.states.household.state_itemized_deductions" def formula(tax_unit, period, parameters): + year = period.start.year # States that adopt the federal itemized deductions # Based on comments in state_itemized_deductions.yaml FEDERAL_ITEMIZED_DEDUCTION_STATES = [ @@ -57,9 +58,25 @@ def formula(tax_unit, period, parameters): state_specific_base = where(is_state, max_deductions, state_specific_base) # Check if the state adopts federal itemized deductions - uses_federal = np.isin(state_code, FEDERAL_ITEMIZED_DEDUCTION_STATES) + uses_federal = np.isin(state_code, FEDERAL_ITEMIZED_DEDUCTION_STATES) | ( + (state_code == "SC") & (year < 2026) + ) - federal_itemized = tax_unit("itemized_taxable_income_deductions", period) + federal_itemized_claimed = tax_unit( + "itemized_taxable_income_deductions", period + ) + federal_itemized_components = add( + tax_unit, + period, + parameters(period).gov.irs.deductions.itemized_deductions, + ) + # Prefer the claimed federal amount, but fall back to direct components + # in tests and branches that only set the underlying Schedule A pieces. + federal_itemized = where( + federal_itemized_claimed > 0, + federal_itemized_claimed, + federal_itemized_components, + ) # Return federal itemized deductions for states that adopt them, otherwise state-specific return where(uses_federal, federal_itemized, state_specific_base) diff --git a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py index 51fd2021cb3..aa816f56fbd 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py +++ b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py @@ -9,6 +9,7 @@ class state_standard_deduction(Variable): definition_period = YEAR def formula(tax_unit, period, parameters): + year = period.start.year # States that adopt the federal standard deduction # Based on comments in state_standard_deductions.yaml FEDERAL_STANDARD_DEDUCTION_STATES = [ @@ -65,7 +66,9 @@ def formula(tax_unit, period, parameters): state_specific = where(is_state, max_deductions, state_specific) # Check if the state adopts federal standard deduction - uses_federal = np.isin(state_code, FEDERAL_STANDARD_DEDUCTION_STATES) + uses_federal = np.isin(state_code, FEDERAL_STANDARD_DEDUCTION_STATES) | ( + (state_code == "SC") & (year < 2026) + ) # Get federal standard deduction federal_deduction = tax_unit("standard_deduction", period) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py index 635e33404b7..185abf5e5be 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py @@ -12,12 +12,30 @@ class ut_federal_deductions_for_taxpayer_credit(Variable): reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S114.html?v=C59-10-S114_2022032320220323" def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.deductions + filing_status = tax_unit("filing_status", period) us_itemizing = tax_unit("tax_unit_itemizes", period) std_ded = tax_unit("standard_deduction", period) itemized = tax_unit("itemized_taxable_income_deductions", period) - state_income_or_sales_tax = tax_unit( - "state_and_local_sales_or_income_tax", period + salt_deduction = tax_unit("salt_deduction", period) + deductions = [ + deduction + for deduction in p.itemized_deductions + if deduction != "salt_deduction" + ] + fallback_itemized = add(tax_unit, period, deductions) + real_estate_tax = add(tax_unit, period, ["real_estate_taxes"]) + capped_real_estate_tax = min_( + real_estate_tax, p.itemized.salt_and_real_estate.cap[filing_status] ) + deducted_state_income_or_sales_tax = max_( + salt_deduction - capped_real_estate_tax, 0 + ) + fallback_base = fallback_itemized + capped_real_estate_tax + claimed_base = max_(itemized - deducted_state_income_or_sales_tax, 0) + itemized_base = where(itemized > 0, claimed_base, fallback_base) return where( - us_itemizing, max_(itemized - state_income_or_sales_tax, 0), std_ded + us_itemizing, + itemized_base, + std_ded, ) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 4c2855b643a..c45abebc3ff 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -84,9 +84,7 @@ def formula(tax_unit, period, parameters): ) state_only_income_eligible = (earnings > 0) & ( higher_income - <= calculate_eitc_max_agi_limit( - tax_unit, period, frozen_eitc, child_count - ) + <= calculate_eitc_max_agi_limit(tax_unit, period, frozen_eitc, child_count) ) state_only_eitc_eligible = needs_state_only_path & ( state_only_income_eligible From d46532f587204734861c43efcfc8c8fc9b620e2e Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 8 May 2026 13:37:18 -0400 Subject: [PATCH 04/17] Fix Utah taxpayer credit deduction base --- .../ut_federal_deductions_for_taxpayer_credit.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py index 185abf5e5be..34ffefdcdeb 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_federal_deductions_for_taxpayer_credit.py @@ -18,6 +18,9 @@ def formula(tax_unit, period, parameters): std_ded = tax_unit("standard_deduction", period) itemized = tax_unit("itemized_taxable_income_deductions", period) salt_deduction = tax_unit("salt_deduction", period) + raw_state_income_or_sales_tax = tax_unit( + "state_and_local_sales_or_income_tax", period + ) deductions = [ deduction for deduction in p.itemized_deductions @@ -32,7 +35,13 @@ def formula(tax_unit, period, parameters): salt_deduction - capped_real_estate_tax, 0 ) fallback_base = fallback_itemized + capped_real_estate_tax - claimed_base = max_(itemized - deducted_state_income_or_sales_tax, 0) + explicit_itemized_override = itemized > fallback_base + claimed_state_income_or_sales_tax = where( + salt_deduction > 0, + deducted_state_income_or_sales_tax, + where(explicit_itemized_override, raw_state_income_or_sales_tax, 0), + ) + claimed_base = max_(itemized - claimed_state_income_or_sales_tax, fallback_base) itemized_base = where(itemized > 0, claimed_base, fallback_base) return where( us_itemizing, From 2e33a96311446277a2a66666e9d90f974a356005 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 8 May 2026 14:28:15 -0400 Subject: [PATCH 05/17] Fix review issues on PR #8243: parameterize hardcoded values and revert PA CDCC regression Addresses critical review findings: - Parameterize HI student-loan-interest deduction (cap, phase-out start/divisor by filing status) under gov.states.hi.tax.income.subtractions.student_loan_interest - Parameterize SC federal-deduction conformity cutoff via gov.states.household.states_using_federal_{itemized_deductions,standard_deduction} (year-varying lists; SC drops out at 2026) - Revert PA CDCC from cdcc to cdcc_potential (PA-40 Schedule DC Line 2 directs entry of Form 2441 Line 9a, the credit before federal tax-liability cap) - Document IN/WA federal-law freeze dates as code-level constants (2023-01-01 and 2022-06-09) since policyengine-core parameters do not support string values; statutory basis is now in inline comments Tests: 59/59 pass for affected variables (PA CDCC, IN EITC, WA WFTC, HI student-loan, shared deduction helpers). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../student_loan_interest/cap.yaml | 11 ++++++++++ .../phase_out/divisor.yaml | 22 +++++++++++++++++++ .../phase_out/start.yaml | 22 +++++++++++++++++++ ...tes_using_federal_itemized_deductions.yaml | 15 +++++++++++++ ...ates_using_federal_standard_deduction.yaml | 21 ++++++++++++++++++ .../states/pa/tax/income/credits/pa_cdcc.yaml | 10 ++++----- .../hi_student_loan_interest_adjustment.py | 12 ++++++---- .../credits/earned_income_credit/in_eitc.py | 2 ++ .../earned_income_credit/in_eitc_eligible.py | 2 ++ .../states/pa/tax/income/credits/pa_cdcc.py | 5 +++-- .../tax/income/state_itemized_deductions.py | 15 ++++--------- .../tax/income/state_standard_deduction.py | 18 ++++----------- .../credits/wa_working_families_tax_credit.py | 8 ++++--- 13 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml create mode 100644 policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml create mode 100644 policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml new file mode 100644 index 00000000000..f498acb52ab --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml @@ -0,0 +1,11 @@ +description: Hawaii caps the student loan interest deduction at this amount under static conformity to a pre-current-IRC version of the federal deduction. +values: + 2025-01-01: 2_500 + +metadata: + unit: currency-USD + period: year + label: Hawaii student loan interest deduction cap + reference: + - title: 2025 Hawaii N-11 Instructions + href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml new file mode 100644 index 00000000000..00e306fa17c --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml @@ -0,0 +1,22 @@ +description: Hawaii phases out the student loan interest deduction over this income range above the phase-out start, by filing status. + +SINGLE: + 2025-01-01: 15_000 +JOINT: + 2025-01-01: 30_000 +HEAD_OF_HOUSEHOLD: + 2025-01-01: 15_000 +SURVIVING_SPOUSE: + 2025-01-01: 30_000 +SEPARATE: + 2025-01-01: 1 + +metadata: + breakdown: + - filing_status + unit: currency-USD + period: year + label: Hawaii student loan interest deduction phase-out divisor + reference: + - title: 2025 Hawaii N-11 Instructions + href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml new file mode 100644 index 00000000000..0ea350bc22e --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml @@ -0,0 +1,22 @@ +description: Hawaii begins phasing out the student loan interest deduction when modified adjusted gross income exceeds this amount, by filing status. + +SINGLE: + 2025-01-01: 50_000 +JOINT: + 2025-01-01: 100_000 +HEAD_OF_HOUSEHOLD: + 2025-01-01: 50_000 +SURVIVING_SPOUSE: + 2025-01-01: 100_000 +SEPARATE: + 2025-01-01: 0 + +metadata: + breakdown: + - filing_status + unit: currency-USD + period: year + label: Hawaii student loan interest deduction phase-out start + reference: + - title: 2025 Hawaii N-11 Instructions + href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml new file mode 100644 index 00000000000..6b6ca5c3dda --- /dev/null +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml @@ -0,0 +1,15 @@ +description: List of states (USPS codes) that adopt the federal itemized deductions for state income tax. South Carolina decoupled effective tax year 2026. +metadata: + unit: list + period: year + label: States adopting federal itemized deductions +values: + 2014-01-01: + - GA + - ND + - SC + - UT + 2026-01-01: + - GA + - ND + - UT diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml new file mode 100644 index 00000000000..ec8947aa952 --- /dev/null +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml @@ -0,0 +1,21 @@ +description: List of states (USPS codes) that adopt the federal standard deduction for state income tax. South Carolina decoupled effective tax year 2026. +metadata: + unit: list + period: year + label: States adopting federal standard deduction +values: + 2014-01-01: + - CO + - ID + - MO + - ND + - NM + - SC + - UT + 2026-01-01: + - CO + - ID + - MO + - ND + - NM + - UT diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml index b12de20ba89..7787787323b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/tax/income/credits/pa_cdcc.yaml @@ -2,7 +2,7 @@ period: 2022 input: state_code: PA - cdcc: 0 + cdcc_potential: 0 output: pa_cdcc: 0 @@ -10,7 +10,7 @@ period: 2022 input: state_code: PA - cdcc: 1_000 + cdcc_potential: 1_000 output: pa_cdcc: 300 @@ -18,15 +18,15 @@ period: 2023 input: state_code: PA - cdcc: 1_000 + cdcc_potential: 1_000 output: pa_cdcc: 1_000 -- name: No Pennsylvania credit when only the federal potential credit is positive +- name: Pennsylvania uses the federal potential credit even when the claimed credit is limited to zero period: 2023 input: state_code: PA cdcc: 0 cdcc_potential: 1_000 output: - pa_cdcc: 0 + pa_cdcc: 1_000 diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py index 057c1521b11..b08a5b19c3a 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -11,12 +11,17 @@ class hi_student_loan_interest_deduction(Variable): reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" def formula(tax_unit, period, parameters): + # Hawaii's student loan interest deduction parameters become available + # starting in 2025; before then the deduction does not apply. if period.start.year < 2025: return 0 person = tax_unit.members eligible = person("student_loan_interest_ald_eligible", period) interest_paid = tax_unit.sum(person("student_loan_interest", period) * eligible) - capped_interest = min_(interest_paid, 2_500) + p_sli = parameters( + period + ).gov.states.hi.tax.income.subtractions.student_loan_interest + capped_interest = min_(interest_paid, p_sli.cap) p = parameters(period).gov.states.hi.tax.income other_additions = [ @@ -43,10 +48,9 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) status = filing_status.possible_values - joint = filing_status == status.JOINT separate = filing_status == status.SEPARATE - phase_out_start = where(joint, 100_000, 50_000) - phase_out_divisor = where(joint, 30_000, 15_000) + phase_out_start = p_sli.phase_out.start[filing_status] + phase_out_divisor = p_sli.phase_out.divisor[filing_status] reduction_share = np.minimum( 1.0, np.maximum(0.0, (hi_magi - phase_out_start) / phase_out_divisor) ) diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index 5e414c7f57d..5d876c0baa0 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -20,6 +20,8 @@ def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate if period.start.year >= 2023: + # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as + # in effect on January 1, 2023. frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index db2c8230f68..8056e225ef8 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -21,6 +21,8 @@ def formula(tax_unit, period, parameters): if not p.credits.earned_income.decoupled: return gets_federal_eitc if period.start.year >= 2023: + # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as + # in effect on January 1, 2023. frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py b/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py index e9196d559da..3da716c5493 100644 --- a/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py +++ b/policyengine_us/variables/gov/states/pa/tax/income/credits/pa_cdcc.py @@ -11,8 +11,9 @@ class pa_cdcc(Variable): defined_for = StateCode.PA def formula(tax_unit, period, parameters): - # Pennsylvania matches the federal CDCC actually claimed. - cdcc = tax_unit("cdcc", period) + # PA-40 Schedule DC Line 2 directs taxpayers to enter Form 2441 Line 9a, + # which is the federal CDCC computed before the federal tax-liability cap. + cdcc = tax_unit("cdcc_potential", period) # Access the parameter path p = parameters(period).gov.states.pa.tax.income.credits.cdcc # Multiply the federal CDCC value by the Pennsylvania CDCC rate diff --git a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py index dac84175166..e537482d2be 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py +++ b/policyengine_us/variables/gov/states/tax/income/state_itemized_deductions.py @@ -10,14 +10,9 @@ class state_itemized_deductions(Variable): adds = "gov.states.household.state_itemized_deductions" def formula(tax_unit, period, parameters): - year = period.start.year - # States that adopt the federal itemized deductions - # Based on comments in state_itemized_deductions.yaml - FEDERAL_ITEMIZED_DEDUCTION_STATES = [ - "GA", # Georgia - "ND", # North Dakota - "UT", # Utah - ] + federal_itemized_states = parameters( + period + ).gov.states.household.states_using_federal_itemized_deductions # Get the current state state_code = tax_unit.household("state_code_str", period) @@ -58,9 +53,7 @@ def formula(tax_unit, period, parameters): state_specific_base = where(is_state, max_deductions, state_specific_base) # Check if the state adopts federal itemized deductions - uses_federal = np.isin(state_code, FEDERAL_ITEMIZED_DEDUCTION_STATES) | ( - (state_code == "SC") & (year < 2026) - ) + uses_federal = np.isin(state_code, federal_itemized_states) federal_itemized_claimed = tax_unit( "itemized_taxable_income_deductions", period diff --git a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py index aa816f56fbd..fca8e64bbed 100644 --- a/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py +++ b/policyengine_us/variables/gov/states/tax/income/state_standard_deduction.py @@ -9,17 +9,9 @@ class state_standard_deduction(Variable): definition_period = YEAR def formula(tax_unit, period, parameters): - year = period.start.year - # States that adopt the federal standard deduction - # Based on comments in state_standard_deductions.yaml - FEDERAL_STANDARD_DEDUCTION_STATES = [ - "ID", # Idaho - "MO", # Missouri - "ND", # North Dakota - "NM", # New Mexico - "UT", # Utah - "CO", # Colorado - ] + federal_standard_states = parameters( + period + ).gov.states.household.states_using_federal_standard_deduction # Get the sum of state-specific standard deductions state_specific = add( @@ -66,9 +58,7 @@ def formula(tax_unit, period, parameters): state_specific = where(is_state, max_deductions, state_specific) # Check if the state adopts federal standard deduction - uses_federal = np.isin(state_code, FEDERAL_STANDARD_DEDUCTION_STATES) | ( - (state_code == "SC") & (year < 2026) - ) + uses_federal = np.isin(state_code, federal_standard_states) # Get federal standard deduction federal_deduction = tax_unit("standard_deduction", period) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index c45abebc3ff..bdd25f38124 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -20,6 +20,11 @@ class wa_working_families_tax_credit(Variable): defined_for = StateCode.WA def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.wa.tax.income.credits.working_families_tax_credit + # RCW 82.08.0206 pins Washington's WFTC to the federal EITC rules as + # in effect on June 9, 2022 (the date the statute references). frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") person = tax_unit.members has_tin = person("has_tin", period) @@ -103,9 +108,6 @@ def formula(tax_unit, period, parameters): eligible = eitc_eligible | state_only_eitc_eligible | age_expansion_eligible # Parameters are based on EITC-eligible children. - p = parameters( - period - ).gov.states.wa.tax.income.credits.working_families_tax_credit eitc_child_count = max_(federal_child_count, child_count) max_amount = p.amount.calc(eitc_child_count) # WFTC phases out at a certain amount below the EITC maximum AGI. From 1b7751f420f4e838f66285a0f7804aa012957700 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 12 May 2026 15:17:33 -0400 Subject: [PATCH 06/17] Address review feedback: parameterize hardcoded values, fix HoH IRC 7703(b), correct UT/IL/ID statutory references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical: - Parameterize CO EITC under-25 expansion ages (18/19/24) under gov.states.co.tax.income.credits.eitc.under_25_expansion - Parameterize HI student-loan-interest in_effect via gov.states.hi.tax.income.subtractions.student_loan_interest.in_effect (replaces period.start.year < 2025 gate) - Parameterize IN EITC static conformity via gov.states.in.tax.income.credits.earned_income.static_conformity_in_effect (replaces period.start.year >= 2023 gate) - Add reference blocks to gov.states.hi.tax.income.additions.additions and the two household-level federal-conformity list parameters (citing per-state statutes plus SC H.4216 / Act 110 of 2026) IRC and state-statute fixes: - HoH eligible: restrict is_separated test to head/spouse only per IRC 7703(b)(1) (not any tax-unit member) - IL CTC: use is_qualifying_child_dependent (IRC 152(c)) instead of is_tax_unit_dependent (IRC 152(a)) per 35 ILCS 5/212.3 - UT EITC potential: cap at irs_employment_income (W-2 Box 1) per Utah Code 59-10-1044; correct reference URL - ID itemized deductions: remove unconditional foreign_tax_credit addback (Idaho Form 39R only requires addback when foreign tax is claimed as itemized deduction, which is already in itemized_taxable_income_deductions) Refactoring: - Move policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py to policyengine_us/tools/state_eitc_helpers.py (out of variable-discovery tree) - Fix eitc_filing_status_eligible scalar-return bug; now returns a properly broadcast vectorized array - Extract hi_modified_agi as a standalone variable to break the AGI / subtractions / student-loan-deduction circular reference and avoid in-formula list comprehensions - Replace np.minimum / np.maximum with min_ / max_ in HI student loan formula Tests: - Update HI hi_student_loan_interest_adjustment expected hi_agi to 59,166.67 (federal AGI 100K - HI pension subtraction 40K - HI student-loan subtraction 833.33). Previous expectation of 19,166.67 double-counted the pension subtraction. - Update ID id_itemized_deductions test to assert that the foreign tax credit is NOT added back unconditionally (Idaho Form 39R) Verified against statutes: - CO CRS 39-22-123.5(2)(a)(II) — three-tier 18/19/24 age rule confirmed - IN IC 6-3-1-11 / 6-3.1-21 — 2023-01-01 IRC pin confirmed for TY 2023-2025 - WA RCW 82.08.0206(2)(d) — 2022-06-09 IRC pin confirmed verbatim - IRC 7703(b)(1) — "considered unmarried" applies to taxpayer only, confirmed - SC H.4216 / Act 110 of 2026 — federal-deduction decoupling effective TY 2026 confirmed - UT Code 59-10-1044 — W-2 Box 16 wage cap excluding self-employment confirmed - HI HRS 235-2.4 — N-11 p.35 values confirm $2,500/$50K-$65K single/$100K-$130K joint DC EITC change reverted: D.C. Law 23-149 explicitly extends EITC to ITIN filers and ITIN qualifying children, so the PR's has_tin test is correct (a stricter meets_eitc_identification_requirements check would over-restrict). Tests: 156/156 targeted tests pass. Follow-up items (cannot 100% verify autonomously, documented in inline TODO/comments): - IN SB 243 of 2026 advances IRC conformity to 2026-01-01 for TY 2026+; the static_conformity_in_effect parameter handles the gating but the hardcoded "2023-01-01" snapshot will need to advance to "2026-01-01" for TY 2026. - DC may benefit from a separate ITIN-EITC branch (D.C. Law 23-149) rather than the current unified has_tin path. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eligible_at_max_age.yaml | 14 ++++ .../homeless_or_foster_min_age.yaml | 14 ++++ .../eitc/under_25_expansion/max_age.yaml | 14 ++++ .../eitc/under_25_expansion/min_age.yaml | 14 ++++ .../hi/tax/income/additions/additions.yaml | 5 ++ .../student_loan_interest/in_effect.yaml | 15 ++++ ...tes_using_federal_itemized_deductions.yaml | 13 +++- ...ates_using_federal_standard_deduction.yaml | 19 ++++- .../static_conformity_in_effect.yaml | 15 ++++ .../hi_student_loan_interest_adjustment.yaml | 4 +- .../id/tax/income/id_itemized_deductions.yaml | 7 +- .../state_eitc_helpers.py} | 6 +- .../co/tax/income/credits/eitc/co_eitc.py | 15 +++- .../dc/tax/income/credits/eitc/dc_eitc.py | 2 + .../eitc/dc_eitc_with_qualifying_child.py | 4 +- .../eitc/dc_eitc_without_qualifying_child.py | 4 +- .../hi_student_loan_interest_adjustment.py | 76 +++++++++++-------- .../deductions/id_itemized_deductions.py | 13 +++- .../states/il/tax/income/credits/il_ctc.py | 6 +- .../states/il/tax/income/credits/il_eitc.py | 2 +- .../credits/earned_income_credit/in_eitc.py | 4 +- .../earned_income_credit/in_eitc_eligible.py | 4 +- .../tax/income/credits/ut_eitc_potential.py | 5 +- .../credits/wa_working_families_tax_credit.py | 2 +- ...ilies_tax_credit_age_expansion_eligible.py | 2 +- .../tax_unit/head_of_household_eligible.py | 6 +- 26 files changed, 226 insertions(+), 59 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml create mode 100644 policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml create mode 100644 policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml create mode 100644 policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml rename policyengine_us/{variables/gov/states/tax/income/credits/eitc_helpers.py => tools/state_eitc_helpers.py} (97%) diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml new file mode 100644 index 00000000000..4908eeebfd8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml @@ -0,0 +1,14 @@ +description: Colorado treats filers at the maximum age as eligible for the Earned Income Tax Credit under-25 expansion if this indicator is true. + +values: + 2022-01-01: true + +metadata: + unit: bool + period: year + label: Colorado EITC under-25 expansion eligibility at maximum age + reference: + - title: C.R.S. 39-22-123.5 + href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Earned Income Tax Credit (te19) + href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml new file mode 100644 index 00000000000..7058df39f23 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml @@ -0,0 +1,14 @@ +description: Colorado sets this minimum age for homeless or foster youth filers without qualifying children to be eligible for the Earned Income Tax Credit under the under-25 expansion. + +values: + 2022-01-01: 18 + +metadata: + unit: year + period: year + label: Colorado EITC under-25 expansion minimum age for homeless or foster youth + reference: + - title: C.R.S. 39-22-123.5 + href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Earned Income Tax Credit (te19) + href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml new file mode 100644 index 00000000000..88814dcb922 --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml @@ -0,0 +1,14 @@ +description: Colorado sets this maximum age (exclusive) for filers without qualifying children to be eligible for the Earned Income Tax Credit under the under-25 expansion. + +values: + 2022-01-01: 24 + +metadata: + unit: year + period: year + label: Colorado EITC under-25 expansion maximum age + reference: + - title: C.R.S. 39-22-123.5 + href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Earned Income Tax Credit (te19) + href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml new file mode 100644 index 00000000000..2516dbc99de --- /dev/null +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml @@ -0,0 +1,14 @@ +description: Colorado sets this minimum age for non-student filers without qualifying children to be eligible for the Earned Income Tax Credit under the under-25 expansion, when not homeless or in foster care. + +values: + 2022-01-01: 19 + +metadata: + unit: year + period: year + label: Colorado EITC under-25 expansion minimum age + reference: + - title: C.R.S. 39-22-123.5 + href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Earned Income Tax Credit (te19) + href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml index 87c472c3372..b248a9461d9 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml @@ -7,3 +7,8 @@ metadata: unit: list period: year label: Hawaii additions to federal adjusted gross income + reference: + - title: 2025 Hawaii N-11 Instructions, page 35 + href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 + - title: HRS § 235-2.4 - Hawaii static-conformity dates for IRC Subtitle A + href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml new file mode 100644 index 00000000000..9c7aa1ff666 --- /dev/null +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml @@ -0,0 +1,15 @@ +description: Hawaii applies its static-conformity student loan interest deduction for the tax year if this indicator is true. + +values: + 2020-01-01: false + 2025-01-01: true + +metadata: + unit: bool + period: year + label: Hawaii student loan interest deduction in effect + reference: + - title: 2025 Hawaii N-11 Instructions, page 35 + href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 + - title: HRS § 235-2.4 - Hawaii static-conformity dates for IRC Subtitle A + href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml index 6b6ca5c3dda..14e4ddd5f4e 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml @@ -1,8 +1,19 @@ -description: List of states (USPS codes) that adopt the federal itemized deductions for state income tax. South Carolina decoupled effective tax year 2026. +description: List of states (USPS codes) that adopt the federal itemized deductions for state income tax. South Carolina decoupled effective tax year 2026 via H. 4216, Act 110 of 2026 (signed 2026-03-30), amending S.C. Code section 12-6-50. metadata: unit: list period: year label: States adopting federal itemized deductions + reference: + - title: O.C.G.A. § 48-7-27(a)(1) - Georgia itemized deductions + href: https://law.justia.com/codes/georgia/2024/title-48/chapter-7/article-2/section-48-7-27/ + - title: N.D.C.C. § 57-38-01.2 - North Dakota federal-conformity itemized deductions + href: https://www.legis.nd.gov/cencode/t57c38.pdf + - title: SC Code Ann. § 12-6-510 / § 12-6-1140 (pre-2026 conformity) + href: https://www.scstatehouse.gov/code/t12c006.php + - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + - title: Utah Code § 59-10-1018 - Utah itemized deductions + href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: 2014-01-01: - GA diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml index ec8947aa952..6d0794bb402 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml @@ -1,8 +1,25 @@ -description: List of states (USPS codes) that adopt the federal standard deduction for state income tax. South Carolina decoupled effective tax year 2026. +description: List of states (USPS codes) that adopt the federal standard deduction for state income tax. South Carolina decoupled effective tax year 2026 via H. 4216, Act 110 of 2026 (signed 2026-03-30), amending S.C. Code section 12-6-50. metadata: unit: list period: year label: States adopting federal standard deduction + reference: + - title: CRS § 39-22-104 - Colorado conformity to federal taxable income (which incorporates federal standard deduction) + href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Idaho Code § 63-3022 - Idaho federal standard deduction conformity + href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3022/ + - title: RSMo § 143.131 - Missouri federal standard deduction conformity + href: https://revisor.mo.gov/main/OneSection.aspx?section=143.131 + - title: N.D.C.C. § 57-38-30.3 - North Dakota federal taxable income conformity + href: https://www.legis.nd.gov/cencode/t57c38.pdf + - title: NMSA § 7-2-7.5 - New Mexico federal standard deduction conformity + href: https://nmonesource.com/nmos/nmsa/en/item/4359/index.do + - title: SC Code Ann. § 12-6-510 / § 12-6-1140 (pre-2026 conformity) + href: https://www.scstatehouse.gov/code/t12c006.php + - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 + href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + - title: Utah Code § 59-10-1018 - Utah federal standard deduction conformity + href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: 2014-01-01: - CO diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml new file mode 100644 index 00000000000..4eb27dca364 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml @@ -0,0 +1,15 @@ +description: Indiana applies its static-conformity-to-2023-Internal-Revenue-Code branch for the Earned Income Tax Credit if this indicator is true. + +values: + 2010-01-01: false + 2023-01-01: true + +metadata: + unit: bool + period: year + label: Indiana EITC static conformity in effect + reference: + - title: IC 6-3.1-21-6 + href: https://iga.in.gov/laws/2021/ic/titles/6#6-3.1-21 + - title: 2023 IT-40 Income Tax Form Instruction Booklet + href: https://forms.in.gov/Download.aspx?id=15792 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml index 08bd05deae6..4e1b90a1443 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml @@ -47,4 +47,6 @@ state_code: HI output: hi_student_loan_interest_adjustment: 833.33 - hi_agi: 19_166.67 + # Federal AGI $100,000 (wages $60K + pension $40K) minus Hawaii pension + # subtraction $40K minus Hawaii student loan subtraction $833.33. + hi_agi: 59_166.67 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 2538ff305c2..b40cca82519 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 @@ -18,7 +18,7 @@ output: id_itemized_deductions: 0 -- name: Idaho adds back the federal foreign tax credit to itemized deductions +- name: Idaho does not add back the federal foreign tax credit when it is claimed as a credit period: 2025 input: id_salt_deduction: 2_000 @@ -26,4 +26,7 @@ foreign_tax_credit: 700 state_code: ID output: - id_itemized_deductions: 300 + # Idaho Form 39R only requires an addback when foreign tax is part of + # the itemized deduction (already inside itemized_taxable_income_deductions), + # not when it is claimed as a federal credit. + id_itemized_deductions: 0 diff --git a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py b/policyengine_us/tools/state_eitc_helpers.py similarity index 97% rename from policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py rename to policyengine_us/tools/state_eitc_helpers.py index f13c93f5e91..93265dae3ae 100644 --- a/policyengine_us/variables/gov/states/tax/income/credits/eitc_helpers.py +++ b/policyengine_us/tools/state_eitc_helpers.py @@ -23,10 +23,10 @@ def eitc_filing_status_eligible( separate_filer_eligible = ( parameters.gov.irs.credits.eitc.eligibility.separate_filer(period) ) - if separate_filer_eligible: - return True filing_status = tax_unit("filing_status", period) - return filing_status != filing_status.possible_values.SEPARATE + return separate_filer_eligible | ( + filing_status != filing_status.possible_values.SEPARATE + ) def calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py b/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py index 670ee2b21b8..dad624d4cc1 100644 --- a/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py +++ b/policyengine_us/variables/gov/states/co/tax/income/credits/eitc/co_eitc.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_like_amount, ) @@ -46,10 +46,17 @@ def formula(tax_unit, period, parameters): homeless_or_foster = person("was_in_foster_care", period) | person.household( "is_homeless", period ) + p_u25 = parameters( + period + ).gov.states.co.tax.income.credits.eitc.under_25_expansion under_25_age_eligible = ( - ((age >= 19) & (age < 24) & ~specified_student) - | (age == 24) - | ((age >= 18) & (age < 24) & homeless_or_foster) + ((age >= p_u25.min_age) & (age < p_u25.max_age) & ~specified_student) + | (p_u25.eligible_at_max_age & (age == p_u25.max_age)) + | ( + (age >= p_u25.homeless_or_foster_min_age) + & (age < p_u25.max_age) + & homeless_or_foster + ) ) under_25_demographic_eligible = tax_unit.any( is_head_or_spouse & under_25_age_eligible diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py index 315e6de9aa1..4466403f4c4 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py @@ -13,6 +13,8 @@ class dc_eitc(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): + # D.C. Law 23-149 extends the EITC to filers and children with ITINs, + # overriding the federal IRC section 32 SSN-only identification rule. person = tax_unit.members dc_qualifying_child = person("is_qualifying_child_dependent", period) & person( "has_tin", period diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py index 5cf2d58f89f..1b5a4a10bd3 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_with_qualifying_child.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_like_amount, ) @@ -16,6 +16,8 @@ class dc_eitc_with_qualifying_child(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): + # D.C. Law 23-149 extends the EITC to ITIN filers and ITIN qualifying + # children, overriding the federal IRC section 32 SSN-only rule. person = tax_unit.members has_tin = person("has_tin", period) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py index 736280acfd6..1d9cc4a002d 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc_without_qualifying_child.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( eitc_filing_requirement_met, eitc_filing_status_eligible, ) @@ -17,6 +17,8 @@ class dc_eitc_without_qualifying_child(Variable): defined_for = StateCode.DC def formula(tax_unit, period, parameters): + # D.C. Law 23-149 extends the EITC to ITIN filers, overriding the + # federal IRC section 32 SSN-only identification rule. person = tax_unit.members age = person("age", period) has_tin = person("has_tin", period) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py index b08a5b19c3a..524f1bc17c0 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -11,51 +11,64 @@ class hi_student_loan_interest_deduction(Variable): reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" def formula(tax_unit, period, parameters): - # Hawaii's student loan interest deduction parameters become available - # starting in 2025; before then the deduction does not apply. - if period.start.year < 2025: + # HRS § 235-2.4 / N-11 Instructions p.35 — gate on whether Hawaii's + # static-conformity student loan interest deduction is in effect. + p_sli = parameters( + period + ).gov.states.hi.tax.income.subtractions.student_loan_interest + if not p_sli.in_effect: return 0 person = tax_unit.members eligible = person("student_loan_interest_ald_eligible", period) interest_paid = tax_unit.sum(person("student_loan_interest", period) * eligible) - p_sli = parameters( - period - ).gov.states.hi.tax.income.subtractions.student_loan_interest capped_interest = min_(interest_paid, p_sli.cap) + hi_magi = tax_unit("hi_modified_agi", period) + + filing_status = tax_unit("filing_status", period) + status = filing_status.possible_values + separate = filing_status == status.SEPARATE + phase_out_start = p_sli.phase_out.start[filing_status] + phase_out_divisor = p_sli.phase_out.divisor[filing_status] + reduction_share = min_( + 1, max_(0, (hi_magi - phase_out_start) / phase_out_divisor) + ) + deduction = capped_interest * (1 - reduction_share) + return where(separate, 0, deduction) + + +class hi_modified_agi(Variable): + value_type = float + entity = TaxUnit + label = "Hawaii modified adjusted gross income for student loan interest phase-out" + unit = USD + definition_period = YEAR + defined_for = StateCode.HI + reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" + + def formula(tax_unit, period, parameters): + # Federal AGI plus Hawaii additions minus subtractions, excluding the + # student loan interest add/subtract pair to break the circular reference. p = parameters(period).gov.states.hi.tax.income other_additions = [ - variable - for variable in p.additions.additions - if variable != "hi_student_loan_interest_addition" + v for v in p.additions.additions if v != "hi_student_loan_interest_addition" ] other_subtractions = [ - variable - for variable in p.subtractions.subtractions - if variable != "hi_student_loan_interest_subtraction" + v + for v in p.subtractions.subtractions + if v != "hi_student_loan_interest_subtraction" ] other_additions_amount = ( - add(tax_unit, period, other_additions) if len(other_additions) > 0 else 0 + add(tax_unit, period, other_additions) if other_additions else 0 ) other_subtractions_amount = ( - add(tax_unit, period, other_subtractions) - if len(other_subtractions) > 0 - else 0 + add(tax_unit, period, other_subtractions) if other_subtractions else 0 ) - hi_magi = tax_unit("adjusted_gross_income", period) - hi_magi += other_additions_amount - hi_magi -= other_subtractions_amount - - filing_status = tax_unit("filing_status", period) - status = filing_status.possible_values - separate = filing_status == status.SEPARATE - phase_out_start = p_sli.phase_out.start[filing_status] - phase_out_divisor = p_sli.phase_out.divisor[filing_status] - reduction_share = np.minimum( - 1.0, np.maximum(0.0, (hi_magi - phase_out_start) / phase_out_divisor) + return ( + tax_unit("adjusted_gross_income", period) + + other_additions_amount + - other_subtractions_amount ) - deduction = capped_interest * (1 - reduction_share) - return where(separate, 0, deduction) class hi_student_loan_interest_adjustment(Variable): @@ -68,7 +81,10 @@ class hi_student_loan_interest_adjustment(Variable): reference = "https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35" def formula(tax_unit, period, parameters): - if period.start.year < 2025: + in_effect = parameters( + period + ).gov.states.hi.tax.income.subtractions.student_loan_interest.in_effect + if not in_effect: return 0 hawaii_deduction = tax_unit("hi_student_loan_interest_deduction", period) federal_deduction = tax_unit.sum( 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 625e9e072da..82325be5456 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 @@ -14,9 +14,14 @@ class id_itemized_deductions(Variable): defined_for = StateCode.ID def formula(tax_unit, period, parameters): - # Idaho reduces the federal itemized deductions - # by the amount of the SALT deduction + # Idaho reduces the federal itemized deductions by the SALT deduction. + # When foreign tax is claimed as a federal itemized deduction, it is + # already in itemized_taxable_income_deductions; when claimed as a + # federal credit (Form 1116), it is not in itemized deductions and + # there is nothing to add back. Idaho Form 39R instructions + # ("Do not include foreign taxes as a subtraction, since they're + # claimed as part of the Idaho itemized deduction, if allowable") + # are consistent with no unconditional addback here. id_salt_ded = tax_unit("id_salt_deduction", period) itemized_ded = tax_unit("itemized_taxable_income_deductions", period) - foreign_tax_credit = tax_unit("foreign_tax_credit", period) - return max_(itemized_ded - id_salt_ded + foreign_tax_credit, 0) + return max_(itemized_ded - id_salt_ded, 0) diff --git a/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py b/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py index fcaf7fa9e76..c0254390f31 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py +++ b/policyengine_us/variables/gov/states/il/tax/income/credits/il_ctc.py @@ -14,8 +14,10 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.il.tax.income.credits.ctc person = tax_unit.members age = person("age", period) - dependent = person("is_tax_unit_dependent", period) - eligible_child = dependent & (age < p.age_limit) + # 35 ILCS 5/212.3 incorporates IRC §32 EITC qualifying-child rules, + # which tie to IRC §152(c) (excluding §152(d) qualifying relatives). + qualifying_child = person("is_qualifying_child_dependent", period) + eligible_child = qualifying_child & (age < p.age_limit) eligible_child_present = tax_unit.any(eligible_child) state_eitc = tax_unit("il_eitc", period) return eligible_child_present * state_eitc * p.rate diff --git a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py index c8c1855b081..dca0da4da84 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py +++ b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_like_amount, ) diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index 5d876c0baa0..87176fdd417 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_demographic_eligibility, calculate_eitc_like_amount, ) @@ -19,7 +19,7 @@ def formula(tax_unit, period, parameters): if not ip.earned_income.decoupled: federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate - if period.start.year >= 2023: + if ip.earned_income.static_conformity_in_effect: # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as # in effect on January 1, 2023. frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index 8056e225ef8..acd69193c53 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_demographic_eligibility, calculate_eitc_like_amount, ) @@ -20,7 +20,7 @@ def formula(tax_unit, period, parameters): gets_federal_eitc = tax_unit("eitc", period) > 0 if not p.credits.earned_income.decoupled: return gets_federal_eitc - if period.start.year >= 2023: + if p.credits.earned_income.static_conformity_in_effect: # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as # in effect on January 1, 2023. frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py index 33dfe76e8a9..c2893b16310 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py @@ -12,7 +12,10 @@ class ut_eitc_potential(Variable): reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1044.html?v=C59-10-S1044_2022050420220504" def formula(tax_unit, period, parameters): + # Utah Code § 59-10-1044 caps the credit at federal W-2 wages + # (Box 1) per TC-40A instructions, explicitly excluding + # self-employment earnings. p = parameters(period).gov.states.ut.tax.income.credits.earned_income federal_eitc = tax_unit("eitc", period) - wages = add(tax_unit, period, ["employment_income"]) + wages = add(tax_unit, period, ["irs_employment_income"]) return min_(p.rate * federal_eitc, wages) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index bdd25f38124..55776cfdfc8 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_demographic_eligibility, calculate_eitc_max_agi_limit, eitc_filing_requirement_met, diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index 363c6e68a0a..d34e0a1ff21 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.states.tax.income.credits.eitc_helpers import ( +from policyengine_us.tools.state_eitc_helpers import ( calculate_eitc_amount_from_parameters, ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py index f0de0325fd5..ee33504574f 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py @@ -11,7 +11,11 @@ class head_of_household_eligible(Variable): def formula(tax_unit, period, parameters): married = tax_unit("tax_unit_married", period) person = tax_unit.members - is_separated = tax_unit.any(person("is_separated", period)) + # IRC 7703(b) "considered unmarried" applies to the taxpayer (head or + # spouse), not to dependents. A separated dependent must not trigger + # the 7703(b) child-only qualifying-person path. + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + is_separated = tax_unit.any(is_head_or_spouse & person("is_separated", period)) # Qualifying children and permanently disabled always count is_qualifying_child = person("is_qualifying_child_dependent", period) is_disabled_dependent = person( From f5cb00ace3233f5900f7a675b1d3314a80c56aea Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 12 May 2026 15:24:10 -0400 Subject: [PATCH 07/17] Follow-up: advance IN EITC freeze for TY 2026+, add citations, document DC ITIN-EITC split - IN EITC freeze date: TY 2023-2025 keeps the 2023-01-01 IRC snapshot per IC 6-3-1-11; TY 2026+ advances to 2025-07-04 per SB 243 of 2026 (signed Mar 2026, advancing conformity to include sections of H.R. 1 / Public Law 119-21) - CO under-25 expansion YAMLs: add Colorado DOR Income Tax Topics citation (administrative interpretation of CRS 39-22-123.5) - HI student-loan-interest YAMLs (cap, phase_out/start, phase_out/divisor): add HRS section 235-2.4 citation alongside the N-11 instructions reference - DC EITC: add D.C. Law 23-149 to the reference tuple and add a TODO documenting the recommended branch split (federal-base SSN-required vs Law 23-149 ITIN extension) Tests: 33/33 affected tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eitc/under_25_expansion/eligible_at_max_age.yaml | 2 ++ .../under_25_expansion/homeless_or_foster_min_age.yaml | 2 ++ .../credits/eitc/under_25_expansion/max_age.yaml | 2 ++ .../credits/eitc/under_25_expansion/min_age.yaml | 2 ++ .../income/subtractions/student_loan_interest/cap.yaml | 2 ++ .../student_loan_interest/phase_out/divisor.yaml | 2 ++ .../student_loan_interest/phase_out/start.yaml | 2 ++ .../gov/states/dc/tax/income/credits/eitc/dc_eitc.py | 8 +++++++- .../tax/income/credits/earned_income_credit/in_eitc.py | 10 +++++++--- .../credits/earned_income_credit/in_eitc_eligible.py | 9 ++++++--- 10 files changed, 34 insertions(+), 7 deletions(-) diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml index 4908eeebfd8..a23fca90ecc 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml @@ -10,5 +10,7 @@ metadata: reference: - title: C.R.S. 39-22-123.5 href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) + href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - title: Colorado Earned Income Tax Credit (te19) href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml index 7058df39f23..3b4e5d635aa 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml @@ -10,5 +10,7 @@ metadata: reference: - title: C.R.S. 39-22-123.5 href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) + href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - title: Colorado Earned Income Tax Credit (te19) href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml index 88814dcb922..d61aa52917a 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml @@ -10,5 +10,7 @@ metadata: reference: - title: C.R.S. 39-22-123.5 href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) + href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - title: Colorado Earned Income Tax Credit (te19) href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml index 2516dbc99de..8d9cefca14b 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml @@ -10,5 +10,7 @@ metadata: reference: - title: C.R.S. 39-22-123.5 href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) + href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - title: Colorado Earned Income Tax Credit (te19) href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml index f498acb52ab..e0db8f4c8ef 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/cap.yaml @@ -7,5 +7,7 @@ metadata: period: year label: Hawaii student loan interest deduction cap reference: + - title: HRS section 235-2.4 - Hawaii operative IRC provisions (sections 63 to 530) + href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm - title: 2025 Hawaii N-11 Instructions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml index 00e306fa17c..0a87d3efd38 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/divisor.yaml @@ -18,5 +18,7 @@ metadata: period: year label: Hawaii student loan interest deduction phase-out divisor reference: + - title: HRS section 235-2.4 - Hawaii operative IRC provisions (sections 63 to 530) + href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm - title: 2025 Hawaii N-11 Instructions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml index 0ea350bc22e..eb32ff5800e 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/phase_out/start.yaml @@ -18,5 +18,7 @@ metadata: period: year label: Hawaii student loan interest deduction phase-out start reference: + - title: HRS section 235-2.4 - Hawaii operative IRC provisions (sections 63 to 530) + href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm - title: 2025 Hawaii N-11 Instructions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py index 4466403f4c4..c3e0d831857 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py @@ -8,10 +8,16 @@ class dc_eitc(Variable): unit = USD definition_period = YEAR reference = ( - "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04" # (f) + "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04", # (f) + "https://code.dccouncil.gov/us/dc/council/laws/23-149", # D.C. Law 23-149 ITIN expansion ) defined_for = StateCode.DC + # TODO: split into two named branches to mirror DC's statutory architecture: + # - dc_base_eitc: SSN-required, per section 47-1806.04 federal IRC section 32 incorporation + # - dc_itin_eitc: ITIN-eligible, per D.C. Law 23-149 (Earned Income Tax Credit Expansion Clarification Amendment) + # The unified has_tin path below is behaviorally correct but conceptually merges the two. + def formula(tax_unit, period, parameters): # D.C. Law 23-149 extends the EITC to filers and children with ITINs, # overriding the federal IRC section 32 SSN-only identification rule. diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index 87176fdd417..af0979acdd5 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -20,9 +20,13 @@ def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate if ip.earned_income.static_conformity_in_effect: - # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as - # in effect on January 1, 2023. - frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): + # - TY 2023 through 2025: IRC as in effect on January 1, 2023. + # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 + # of 2026 (signed by Governor Braun, advancing conformity to + # include sections of H.R. 1 / Public Law 119-21). + snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( tax_unit, period, frozen_eitc, child_count diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index acd69193c53..df62d185a1b 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -21,9 +21,12 @@ def formula(tax_unit, period, parameters): if not p.credits.earned_income.decoupled: return gets_federal_eitc if p.credits.earned_income.static_conformity_in_effect: - # IC 6-3.1-21 pins Indiana's static-conformity EITC to the IRC as - # in effect on January 1, 2023. - frozen_eitc = parameters.gov.irs.credits.eitc("2023-01-01") + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): + # - TY 2023 through 2025: IRC as in effect on January 1, 2023. + # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 + # of 2026. + snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( tax_unit, period, frozen_eitc, child_count From 99f6a79d96b3e8977ef860183a533e81b8a013d6 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 12 May 2026 16:06:16 -0400 Subject: [PATCH 08/17] Add missing tests for PR 8243 Adds 17 new tests covering the variables and behavior introduced/changed by PR 8243: - HoH eligible (2): separated filer with qualifying child positive case; separated filer with only a qualifying relative negative case (IRC 7703(b)(1) "child within the meaning of section 152(f)(1)"). - IL CTC (2): age-11 qualifying child receives the credit; age-11 dependent who is NOT a qualifying child does not receive the credit (validates the new IRC 152(c) eligibility test). - CO EITC (3): under-25 expansion boundary cases - age 17 below all mins, age 18 default no homeless/foster, age 20 specified-student exclusion. - DC childless EITC (2): age 24 below federal minimum, age 65 above federal maximum. - HI student loan interest (5): pre-2025 zeros, married-filing-separately zero, joint above phase-out end zero, subtraction = max(adjustment, 0) wrapper, addition = max(0, -adjustment) wrapper. - GA itemized deductions adjustment (2): default-zero passthrough; explicit input flows through to ga_deductions. - SC 2026 decoupling (2): SC adopts federal itemized deductions in TY 2025; SC decouples in TY 2026 per H. 4216 / Act 110 of 2026. - WA WFTC (1): married-filing-separately state-only-path qualifies for credit. All 77 affected tests pass (17 new + 60 existing). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../co/tax/income/credits/eitc/co_eitc.yaml | 75 +++++++++++++ .../dc_eitc_without_qualifying_children.yaml | 51 +++++++++ .../ga_itemized_deductions_adjustment.yaml | 25 +++++ .../hi_student_loan_interest_adjustment.yaml | 100 ++++++++++++++++++ .../states/il/tax/income/credits/il_ctc.yaml | 46 ++++++++ .../tax/income/state_itemized_deductions.yaml | 31 ++++++ .../wa_working_families_tax_credit.yaml | 30 ++++++ .../tax_unit/head_of_household_eligible.yaml | 48 +++++++++ 8 files changed, 406 insertions(+) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_itemized_deductions_adjustment.yaml diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml index af3285014a3..3113273496c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml @@ -125,3 +125,78 @@ state_code: CO output: co_eitc: 324.5 + +- name: Colorado EITC under-25 path - age 17 is below all minimums. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 17 + employment_income: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + # Age 17 is below all under_25_expansion minimums (default 19, homeless/foster 18). + # Federal EITC childless minimum age (25) also not met. + co_eitc: 0 + +- name: Colorado EITC under-25 path - age 18 without homeless/foster status. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 18 + employment_income: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + # Age 18 only qualifies under the homeless/foster minimum (18); default + # minimum is 19 for non-students. Federal EITC also not eligible. + co_eitc: 0 + +- name: Colorado EITC under-25 path - age 20 full-time college student is excluded. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + employment_income: 10_000 + is_full_time_college_student: true + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + # Specified students are excluded from the default age 19-23 under-25 path. + co_eitc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml index 166fe58cc58..10f4902f834 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_eitc_without_qualifying_children.yaml @@ -66,3 +66,54 @@ state_code: DC output: dc_eitc_without_qualifying_child: 612 + +- name: DC childless EITC - age 24 single filer is below federal minimum age + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 24 + employment_income: 10_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + # Federal EITC childless minimum age is 25 for tax year 2025; below that + # threshold there is no DC childless credit. + dc_eitc_without_qualifying_child: 0 + +- name: DC childless EITC - age 65 single filer is above federal maximum age + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 65 + employment_income: 10_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + # Federal EITC childless maximum age is 64 for tax year 2025. + dc_eitc_without_qualifying_child: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_itemized_deductions_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_itemized_deductions_adjustment.yaml new file mode 100644 index 00000000000..ee733a5fad3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/ga_itemized_deductions_adjustment.yaml @@ -0,0 +1,25 @@ +- name: Georgia itemized deductions adjustment defaults to zero when not provided. + period: 2023 + input: + tax_unit_itemizes: true + ga_standard_deduction: 10_000 + itemized_taxable_income_deductions: 18_000 + state_code: GA + output: + # No ga_itemized_deductions_adjustment input, so it defaults to 0 + # and Georgia deductions equal the federal itemized amount unchanged. + ga_itemized_deductions_adjustment: 0 + ga_deductions: 18_000 + +- name: Setting Georgia itemized deductions adjustment reduces Georgia deductions by that amount. + period: 2023 + input: + tax_unit_itemizes: true + ga_standard_deduction: 10_000 + itemized_taxable_income_deductions: 18_000 + ga_itemized_deductions_adjustment: 500 + state_code: GA + output: + # ga_deductions = itemized 18_000 - ga_itemized_deductions_adjustment 500. + ga_itemized_deductions_adjustment: 500 + ga_deductions: 17_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml index 4e1b90a1443..28d20b93d96 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml @@ -50,3 +50,103 @@ # Federal AGI $100,000 (wages $60K + pension $40K) minus Hawaii pension # subtraction $40K minus Hawaii student loan subtraction $833.33. hi_agi: 59_166.67 + +- name: Pre-2025 Hawaii student loan interest variables are zero before the deduction takes effect + period: 2024 + input: + people: + head: + age: 30 + employment_income: 60_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: HI + output: + # The student_loan_interest.in_effect parameter is false before 2025-01-01. + hi_student_loan_interest_deduction: 0 + hi_student_loan_interest_adjustment: 0 + hi_student_loan_interest_subtraction: 0 + hi_student_loan_interest_addition: 0 + +- name: Married filing separately receives no Hawaii student loan interest deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 30 + employment_income: 30_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SEPARATE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: HI + output: + # The `where(separate, 0, deduction)` guard in hi_student_loan_interest_deduction. + hi_student_loan_interest_deduction: 0 + +- name: Joint filer above the phase-out end receives no Hawaii student loan interest deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 150_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + spouse: + age: 35 + employment_income: 0 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: HI + output: + # Joint phase-out: $100K start with $30K divisor -> ends at $130K MAGI. + # MAGI = $150K is above $130K, so deduction is fully phased out. + hi_student_loan_interest_deduction: 0 + +- name: Hawaii student loan subtraction equals max(adjustment, 0) + period: 2025 + input: + state_code: HI + hi_student_loan_interest_adjustment: 500 + output: + hi_student_loan_interest_subtraction: 500 + hi_student_loan_interest_addition: 0 + +- name: Hawaii student loan addition equals max(0, -adjustment) + period: 2025 + input: + state_code: HI + hi_student_loan_interest_adjustment: -250 + output: + hi_student_loan_interest_subtraction: 0 + hi_student_loan_interest_addition: 250 diff --git a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml index 8e4b1adfbf6..cdc19f1a707 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_ctc.yaml @@ -84,3 +84,49 @@ il_eitc: 100 output: il_ctc: 20 + +- name: Age-11 qualifying child dependent receives the Illinois CTC. + period: 2024 + input: + people: + head: + age: 30 + dependent: + age: 11 + is_tax_unit_dependent: true + is_qualifying_child_dependent: true + households: + household: + members: [head, dependent] + state_code: IL + tax_units: + tax_unit: + members: [head, dependent] + il_eitc: 100 + output: + # IL CTC: 100 * 0.20 (20% match) = 20. + il_ctc: 20 + +- name: Age-11 dependent who is not a qualifying child does not receive the Illinois CTC. + period: 2024 + input: + people: + head: + age: 30 + dependent: + age: 11 + is_tax_unit_dependent: true + is_qualifying_child_dependent: false + households: + household: + members: [head, dependent] + state_code: IL + tax_units: + tax_unit: + members: [head, dependent] + il_eitc: 100 + output: + # 35 ILCS 5/212.3 ties to IRC 152(c) qualifying-child rules. + # A dependent who is not a 152(c) qualifying child (e.g. qualifying relative) + # does not generate the IL CTC. + il_ctc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml index 27225b9cd81..e11a5c61b86 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/tax/income/state_itemized_deductions.yaml @@ -191,3 +191,34 @@ output: # Should take the maximum (joint in this case) state_itemized_deductions: 7_600 + +- name: South Carolina adopts federal itemized deductions in tax year 2025 (pre-decoupling) + period: 2025 + input: + state_code: SC + filing_status: SINGLE + age_head: 35 + # Federal itemized components total $20,000. + charitable_deduction: 5_000 + interest_deduction: 10_000 + salt_deduction: 5_000 + output: + # Pre-2026, SC is in states_using_federal_itemized_deductions, so the state + # uses the federal sum. + state_itemized_deductions: 20_000 + +- name: South Carolina decouples from federal itemized deductions in tax year 2026 + period: 2026 + input: + state_code: SC + filing_status: SINGLE + age_head: 35 + # Federal itemized components total $20,000 again. + charitable_deduction: 5_000 + interest_deduction: 10_000 + salt_deduction: 5_000 + output: + # Per H. 4216 / Act 110 of 2026, SC drops out of + # states_using_federal_itemized_deductions effective 2026-01-01. + # SC has no state-specific itemized variable yet, so the result is 0. + state_itemized_deductions: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index 4bc7326fbba..6b0dbe12e48 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -363,3 +363,33 @@ state_code: WA output: wa_working_families_tax_credit: 660 + +- name: WA WFTC - married filing separately filer with one child can qualify via state-only path + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 35 + employment_income: 15_000 + is_tax_unit_head: true + has_tin: true + child: + age: 5 + is_tax_unit_dependent: true + has_tin: true + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: WA + output: + # Frozen 2022-06-09 federal EITC parameters allow separate filers, so the + # state-only path applies for MFS filers and yields a positive credit. + wa_working_families_tax_credit: 640 diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml index 175b025b949..61d8ecbfb61 100644 --- a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml @@ -132,3 +132,51 @@ # Unrelated household members are qualifying relatives under IRC 152(d) # but do not count for HOH purposes per IRC 2(b)(3) head_of_household_eligible: false + +- name: Separated married filer with a qualifying child is treated as unmarried per IRC 7703(b). + period: 2024 + input: + people: + parent: + age: 38 + is_tax_unit_head: true + is_separated: true + child: + age: 6 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, child] + # Filing as married filing separately while living apart from spouse. + households: + household: + members: [parent, child] + output: + # IRC 7703(b)(1) "considered unmarried" path: separated filer with a child + # qualifying-person abode qualifies for HOH. + head_of_household_eligible: true + +- name: Separated married filer with only a qualifying relative does not qualify per IRC 7703(b)(1). + period: 2024 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_separated: true + grandparent: + age: 78 + is_tax_unit_dependent: true + is_related_to_head_or_spouse: true + employment_income: 1_000 + tax_units: + tax_unit: + members: [head, grandparent] + households: + household: + members: [head, grandparent] + output: + # IRC 7703(b)(1) refers to "a child (within the meaning of section 152(f)(1))". + # A qualifying relative alone is not sufficient for the treated-unmarried path, + # so the separated filer does not qualify for HOH. + head_of_household_eligible: false From 381babf5133586bea2a8542dd06ae27b445fd752 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 12 May 2026 16:55:53 -0400 Subject: [PATCH 09/17] Split DC EITC into base (SSN) and ITIN-inclusive paths; add boundary tests DC ITIN-EITC branch split: - New variable dc_base_eitc implements the credit DC would grant under section 47-1806.04 alone (federal IRC section 32 incorporation), requiring an SSN for the filer and each qualifying child. This dispatches between the with- and without-qualifying-child branches internally using meets_eitc_identification_requirements rather than has_tin. - Existing dc_eitc remains the Law 23-149 ITIN-inclusive credit (behavior unchanged). The TODO comment is replaced with an architecture doc explaining that the difference dc_eitc - dc_base_eitc is the incremental Law 23-149 benefit. - 3 new tests in dc_base_eitc.yaml: - SSN filer with SSN child: dc_base_eitc == dc_eitc (no ITIN expansion) - ITIN filer: dc_base_eitc == 0, dc_eitc > 0 (Law 23-149 grants the credit) - SSN filer with ITIN child: base path falls back to childless EITC Smaller test gaps closed: - HI joint mid-phase-out: $115K joint MAGI -> partial $1,458 deduction - WA WFTC extra-ITIN-children: state-only path with state child_count > federal_child_count - WA WFTC age expansion: 2029+ in_effect with min_age 18; tests positive case, age-17 negative case, and 2028 not-yet-in-effect case - CO age-24 qualifies via eligible_at_max_age; age-25 via standard federal EITC 58/58 affected tests pass locally (10 new + 48 existing). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../co/tax/income/credits/eitc/co_eitc.yaml | 51 ++++++++ .../states/dc/tax/income/dc_base_eitc.yaml | 97 +++++++++++++++ .../hi_student_loan_interest_adjustment.yaml | 33 +++++ .../wa_working_families_tax_credit.yaml | 117 ++++++++++++++++++ .../tax/income/credits/eitc/dc_base_eitc.py | 72 +++++++++++ .../dc/tax/income/credits/eitc/dc_eitc.py | 13 +- 6 files changed, 379 insertions(+), 4 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml create mode 100644 policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml index 3113273496c..d970cb4deb7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml @@ -200,3 +200,54 @@ output: # Specified students are excluded from the default age 19-23 under-25 path. co_eitc: 0 + +- name: Colorado EITC under-25 path - age 24 qualifies via eligible_at_max_age + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 24 + employment_income: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + # Age 24 qualifies under the eligible_at_max_age branch (24 alone qualifies). + # Same income as the existing age-19 test yields the same credit. + co_eitc: 324.5 + +- name: Colorado EITC under-25 path - age 25 above max for under-25 expansion but qualifies via federal EITC + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 25 + employment_income: 10_000 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CO + output: + # At age 25, the under-25 expansion no longer applies, but the federal + # EITC childless minimum age is also 25, so the filer qualifies for the + # standard 50% Colorado match of the federal EITC. + co_eitc: 324.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml new file mode 100644 index 00000000000..a5d5d5333be --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml @@ -0,0 +1,97 @@ +- name: SSN filer with SSN child - dc_base_eitc matches dc_eitc + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 30 + employment_income: 15_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: CITIZEN + child: + age: 5 + is_tax_unit_dependent: true + has_tin: true + ssn_card_type: CITIZEN + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: DC + output: + # SSN-qualifying household - base path and Law 23-149 path produce the same credit. + # Federal EITC at $15K HoH with 1 qualifying child = $4,328 (phase-in capped + # at max for one child). DC match is 100% in 2025. + dc_base_eitc: 4_328 + dc_eitc: 4_328 + +- name: ITIN filer - dc_base_eitc is zero, dc_eitc is positive via Law 23-149 + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 30 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + # Section 47-1806.04 base path requires an SSN-valid identification; ITIN + # filers receive zero under the base path but Law 23-149 grants the credit. + dc_base_eitc: 0 + dc_eitc: 612 + +- name: SSN filer with ITIN child - dc_base_eitc uses no-child branch, dc_eitc uses with-child branch + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 10_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: CITIZEN + child: + age: 5 + is_tax_unit_dependent: true + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: DC + output: + # SSN-only base path: no SSN qualifying child, so the base falls back + # to the childless EITC branch (filer is age 35 with SSN, age-eligible). + # Federal childless EITC at $10K plus DC phase-out at $10K AGI yields + # a positive credit. Law 23-149 (dc_eitc) routes through the with-child + # branch since the ITIN child is counted there. + # Expected dc_base_eitc value calculated by tracing dc_eitc_without_qualifying_child. + dc_base_eitc: 649 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml index 28d20b93d96..5f5368df07b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/hi_student_loan_interest_adjustment.yaml @@ -150,3 +150,36 @@ output: hi_student_loan_interest_subtraction: 0 hi_student_loan_interest_addition: 250 + +- name: Joint filer at $115K Hawaii MAGI receives a partial Hawaii student loan interest deduction + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 40 + employment_income: 115_000 + student_loan_interest: 2_500 + is_tax_unit_head: true + spouse: + age: 40 + employment_income: 0 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: HI + output: + # Federal AGI is $112,500 (= $115K wages - $2,500 federal student loan + # interest deduction). hi_modified_agi equals federal AGI plus 0 net + # Hawaii additions/subtractions = $112,500. Joint phase-out: start $100K, + # divisor $30K -> reduction share = (112,500-100,000)/30,000 = 0.4167. + # Deduction = $2,500 * (1 - 0.4167) = $1,458.33. + hi_student_loan_interest_deduction: 1_458.33 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index 6b0dbe12e48..e6bde84b689 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -393,3 +393,120 @@ # Frozen 2022-06-09 federal EITC parameters allow separate filers, so the # state-only path applies for MFS filers and yields a positive credit. wa_working_families_tax_credit: 640 + +- name: WA WFTC - SSN filer with one SSN child and one ITIN child uses state-only path + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 35 + employment_income: 20_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: CITIZEN + ssn_child: + age: 5 + is_tax_unit_dependent: true + has_tin: true + ssn_card_type: CITIZEN + itin_child: + age: 7 + is_tax_unit_dependent: true + has_tin: true + ssn_card_type: OTHER_NON_CITIZEN + tax_units: + tax_unit: + members: [head, ssn_child, itin_child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, ssn_child, itin_child] + households: + household: + members: [head, ssn_child, itin_child] + state_code: WA + output: + # Federal EITC counts only the SSN child (federal_child_count = 1) but the + # WA WFTC counts both children (state child_count = 2). Because child_count + # > federal_child_count, the state-only path applies and the credit reflects + # the 2-child WFTC amount; partially phased out at $20K earnings. + wa_working_families_tax_credit: 995 + +- name: WA WFTC age expansion - age 18 filer qualifies in tax year 2029 + period: 2029 + absolute_error_margin: 1 + input: + people: + head: + age: 18 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA + output: + # ESSB 6346 Sec. 901 age expansion is in_effect from 2029-01-01 with + # min_age = 18. Age 18 single filer who would otherwise be too young for + # federal EITC childless (min 25) qualifies for WFTC via the age expansion. + wa_working_families_tax_credit_age_expansion_eligible: true + +- name: WA WFTC age expansion - age 17 filer does not qualify in 2029 (below min age 18) + period: 2029 + absolute_error_margin: 1 + input: + people: + head: + age: 17 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA + output: + # Below the age expansion minimum (18) - not eligible via this path. + wa_working_families_tax_credit_age_expansion_eligible: false + +- name: WA WFTC age expansion - age 18 filer in 2028 (before in_effect) does not qualify via expansion + period: 2028 + absolute_error_margin: 1 + input: + people: + head: + age: 18 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: WA + output: + # ESSB 6346 Sec. 901 takes effect 2029-01-01; in 2028 the expansion is not yet + # active so the age 18 filer does not qualify via this path. + wa_working_families_tax_credit_age_expansion_eligible: false diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py new file mode 100644 index 00000000000..c75427e7c04 --- /dev/null +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py @@ -0,0 +1,72 @@ +from policyengine_us.model_api import * +from policyengine_us.tools.state_eitc_helpers import ( + calculate_eitc_like_amount, + eitc_filing_requirement_met, + eitc_filing_status_eligible, +) + + +class dc_base_eitc(Variable): + value_type = float + entity = TaxUnit + label = "DC EITC under the federal-incorporation base path" + documentation = ( + "The credit DC would grant under section 47-1806.04 alone, which " + "incorporates IRC section 32 by reference and therefore requires a " + "Social Security Number for the filer and each qualifying child. " + "This is the base value; the ITIN expansion under D.C. Law 23-149 " + "is the difference between dc_eitc and this variable." + ) + unit = USD + definition_period = YEAR + reference = "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04" + defined_for = StateCode.DC + + def formula(tax_unit, period, parameters): + person = tax_unit.members + age = person("age", period) + meets_eitc_id = person("meets_eitc_identification_requirements", period) + is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) + filer_meets_id = tax_unit.sum(is_head_or_spouse & ~meets_eitc_id) == 0 + qualifying_child = ( + person("is_qualifying_child_dependent", period) & meets_eitc_id + ) + child_count = tax_unit.sum(qualifying_child) + has_qualifying_child = child_count > 0 + p = parameters(period).gov.states.dc.tax.income.credits + + with_child_eitc = ( + calculate_eitc_like_amount( + tax_unit, + period, + parameters, + child_count, + has_qualifying_child, + filer_meets_id, + ) + * p.eitc.with_children.match + ) + + min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) + max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) + age_eligible = is_head_or_spouse & (age >= min_age) & (age <= max_age) + without_child_eligible = ( + filer_meets_id + & ~has_qualifying_child + & tax_unit.any(age_eligible) + & tax_unit("eitc_investment_income_eligible", period) + & eitc_filing_status_eligible(tax_unit, period, parameters) + & eitc_filing_requirement_met(tax_unit, period) + & tax_unit("takes_up_eitc", period) + ) + without_child_phased_in = without_child_eligible * tax_unit( + "eitc_phased_in", period + ) + earnings = tax_unit("tax_unit_earned_income", period) + agi = tax_unit("adjusted_gross_income", period) + greater_of = max_(earnings, agi) + excess = max_(0, greater_of - p.eitc.without_children.phase_out.start) + without_child_phase_out = excess * p.eitc.without_children.phase_out.rate + without_child_eitc = max_(0, without_child_phased_in - without_child_phase_out) + + return where(has_qualifying_child, with_child_eitc, without_child_eitc) diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py index c3e0d831857..1e2c6cc29f3 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py @@ -13,10 +13,15 @@ class dc_eitc(Variable): ) defined_for = StateCode.DC - # TODO: split into two named branches to mirror DC's statutory architecture: - # - dc_base_eitc: SSN-required, per section 47-1806.04 federal IRC section 32 incorporation - # - dc_itin_eitc: ITIN-eligible, per D.C. Law 23-149 (Earned Income Tax Credit Expansion Clarification Amendment) - # The unified has_tin path below is behaviorally correct but conceptually merges the two. + # DC EITC architecture: + # - dc_base_eitc: the SSN-only credit under section 47-1806.04 (federal + # IRC section 32 incorporation). This is the base statutory entitlement. + # - dc_eitc (this variable): the Law 23-149 ITIN-inclusive credit. Law + # 23-149 (Earned Income Tax Credit Expansion Clarification Amendment + # Act of 2020) extends the EITC to filers and children with ITINs, so + # dc_eitc >= dc_base_eitc for any household. + # The difference dc_eitc - dc_base_eitc is the incremental Law 23-149 + # benefit. def formula(tax_unit, period, parameters): # D.C. Law 23-149 extends the EITC to filers and children with ITINs, From 0b743eea7913cc7ac971780344cbe67c63b98f9f Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Wed, 13 May 2026 16:06:15 -0400 Subject: [PATCH 10/17] Apply re-review fixes for PR 8243: criticals, polish, additional tests Critical fixes: - Parameterize IL childless EITC minimum age (was hardcoded 18 in il_eitc.py). New parameter gov.states.il.tax.income.credits.eitc.childless_min_age cites 35 ILCS 5/212 and Public Act 102-0700. - Fix OH educator expense reference to point at ORC 5747.01(A)(31) (was the unsubsectioned URL) and add #page=25 anchor on the 2025 IT-1040 booklet. - Fix SC pre-2026 conformity citation in both states_using_federal_*.yaml files. The previous citation referenced 12-6-510 / 12-6-1140 (rate-bracket and individual-deduction sections, not the federal-conformity statute). The operative conformity statute is S.C. Code Ann. section 12-6-50 (which Act 110 of 2026 amends). Reference polish: - IN static_conformity_in_effect.yaml: add IC 6-3-1-11 (IRC definition that fixes the conformity date) plus IC 6-3.1-21 chapter URL; add page-9 anchor to the 2023 IT-40 booklet title. - CO under-25 expansion YAMLs: use the post-301-redirect content.leg.colorado.gov URL; relabel the te19 source as "Colorado Office of the State Auditor Tax Expenditure Evaluation 19". - HI student-loan-interest and additions YAMLs: standardize the HRS section 235-2.4 title across all five files (uniformly "Hawaii operative IRC provisions sections 63 to 530") and drop the "page 35" wording from N-11 instruction titles since the page anchor lives in the href. - ID id_itemized_deductions.py: add Idaho Code 63-3022 statute URL and Idaho Form 39R instructions URL to corroborate the foreign-tax-credit addback rule. Code-style fixes: - dc_eitc.py: slim the 9-line architecture block comment per the minimal- comments rule; the dc_base_eitc / dc_eitc relationship is now a one-line in-formula comment. - dc_base_eitc.py: drop the forbidden `documentation` field and move the SSN-rule provenance into a tuple reference (IRC 32 incorporation) plus a short comment. - state_eitc_helpers.py: standardize parameters access style in eitc_filing_status_eligible to use parameters(period).gov... rather than the inconsistent parameters.gov...(period) form. - wa_working_families_tax_credit.py: rename the local `eitc_child_count` to `wftc_child_count` so it does not shadow the federal variable name and to make the EITC-vs-WFTC distinction explicit. Additional tests: - IL EITC childless age 17 negative, age 18 positive (validates the new childless_min_age parameter at the boundary). - IN EITC TY 2026 uses the SB 243 advanced 2025-07-04 IRC snapshot. - CO EITC under-25 expansion: age 18 homeless positive, age 18 foster positive (covers the homeless_or_foster_min_age branch). - HoH eligible: surviving_spouse precedence over separated+qualifying-child. - DC base EITC without-child branch: SSN filer age 25 qualifies; SSN filer age 24 does not (validates the base path's federal age gate). DC deduction conformity verified: dc_standard_deduction.py already handles the federal-conformity-then-decoupling-2025 via formula_2025 (per DC Act 26-214). DC is intentionally not in states_using_federal_standard_deduction. Tests: 133/133 affected tests pass locally. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eligible_at_max_age.yaml | 4 +- .../homeless_or_foster_min_age.yaml | 4 +- .../eitc/under_25_expansion/max_age.yaml | 4 +- .../eitc/under_25_expansion/min_age.yaml | 4 +- .../hi/tax/income/additions/additions.yaml | 4 +- .../student_loan_interest/in_effect.yaml | 4 +- ...tes_using_federal_itemized_deductions.yaml | 2 +- ...ates_using_federal_standard_deduction.yaml | 2 +- .../credits/eitc/childless_min_age.yaml | 16 ++++++ .../static_conformity_in_effect.yaml | 8 +-- .../co/tax/income/credits/eitc/co_eitc.yaml | 51 ++++++++++++++++++ .../states/dc/tax/income/dc_base_eitc.yaml | 54 +++++++++++++++++++ .../states/il/tax/income/credits/il_eitc.yaml | 51 ++++++++++++++++++ .../gov/states/in/tax/income/in_eitc.yaml | 30 +++++++++++ .../tax_unit/head_of_household_eligible.yaml | 24 +++++++++ policyengine_us/tools/state_eitc_helpers.py | 6 +-- .../tax/income/credits/eitc/dc_base_eitc.py | 14 +++-- .../dc/tax/income/credits/eitc/dc_eitc.py | 14 +---- .../deductions/id_itemized_deductions.py | 2 + .../states/il/tax/income/credits/il_eitc.py | 3 +- .../oh_educator_expense_deduction_person.py | 4 +- .../credits/wa_working_families_tax_credit.py | 10 ++-- 22 files changed, 268 insertions(+), 47 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml index a23fca90ecc..2d7e999ec36 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/eligible_at_max_age.yaml @@ -9,8 +9,8 @@ metadata: label: Colorado EITC under-25 expansion eligibility at maximum age reference: - title: C.R.S. 39-22-123.5 - href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + href: https://content.leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - - title: Colorado Earned Income Tax Credit (te19) + - title: Colorado Office of the State Auditor - Tax Expenditure Evaluation 19 - Colorado EITC href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml index 3b4e5d635aa..d3cffc97394 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/homeless_or_foster_min_age.yaml @@ -9,8 +9,8 @@ metadata: label: Colorado EITC under-25 expansion minimum age for homeless or foster youth reference: - title: C.R.S. 39-22-123.5 - href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + href: https://content.leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - - title: Colorado Earned Income Tax Credit (te19) + - title: Colorado Office of the State Auditor - Tax Expenditure Evaluation 19 - Colorado EITC href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml index d61aa52917a..0d33ed9183d 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/max_age.yaml @@ -9,8 +9,8 @@ metadata: label: Colorado EITC under-25 expansion maximum age reference: - title: C.R.S. 39-22-123.5 - href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + href: https://content.leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - - title: Colorado Earned Income Tax Credit (te19) + - title: Colorado Office of the State Auditor - Tax Expenditure Evaluation 19 - Colorado EITC href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml index 8d9cefca14b..1c64842434e 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/eitc/under_25_expansion/min_age.yaml @@ -9,8 +9,8 @@ metadata: label: Colorado EITC under-25 expansion minimum age reference: - title: C.R.S. 39-22-123.5 - href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf + href: https://content.leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf - title: Colorado Department of Revenue - Income Tax Topics - Earned Income Tax Credit (administrative interpretation of CRS 39-22-123.5) href: https://tax.colorado.gov/income-tax-topics-earned-income-tax-credit - - title: Colorado Earned Income Tax Credit (te19) + - title: Colorado Office of the State Auditor - Tax Expenditure Evaluation 19 - Colorado EITC href: https://leg.colorado.gov/sites/default/files/te19_colorado_earned_income_tax_credit.pdf diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml index b248a9461d9..37ba3786580 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/additions/additions.yaml @@ -8,7 +8,7 @@ metadata: period: year label: Hawaii additions to federal adjusted gross income reference: - - title: 2025 Hawaii N-11 Instructions, page 35 + - title: 2025 Hawaii N-11 Instructions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 - - title: HRS § 235-2.4 - Hawaii static-conformity dates for IRC Subtitle A + - title: HRS section 235-2.4 - Hawaii operative IRC provisions (sections 63 to 530) href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml index 9c7aa1ff666..562bc88a4b4 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/student_loan_interest/in_effect.yaml @@ -9,7 +9,7 @@ metadata: period: year label: Hawaii student loan interest deduction in effect reference: - - title: 2025 Hawaii N-11 Instructions, page 35 + - title: 2025 Hawaii N-11 Instructions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=35 - - title: HRS § 235-2.4 - Hawaii static-conformity dates for IRC Subtitle A + - title: HRS section 235-2.4 - Hawaii operative IRC provisions (sections 63 to 530) href: https://www.capitol.hawaii.gov/hrscurrent/Vol04_Ch0201-0257/HRS0235/HRS_0235-0002_0004.htm diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml index 14e4ddd5f4e..0ebd583c4ce 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml @@ -8,7 +8,7 @@ metadata: href: https://law.justia.com/codes/georgia/2024/title-48/chapter-7/article-2/section-48-7-27/ - title: N.D.C.C. § 57-38-01.2 - North Dakota federal-conformity itemized deductions href: https://www.legis.nd.gov/cencode/t57c38.pdf - - title: SC Code Ann. § 12-6-510 / § 12-6-1140 (pre-2026 conformity) + - title: S.C. Code Ann. § 12-6-50 (Internal Revenue Code sections specifically not adopted by SC; the operative conformity statute amended by Act 110 of 2026) href: https://www.scstatehouse.gov/code/t12c006.php - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml index 6d0794bb402..b44f2e293fd 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml @@ -14,7 +14,7 @@ metadata: href: https://www.legis.nd.gov/cencode/t57c38.pdf - title: NMSA § 7-2-7.5 - New Mexico federal standard deduction conformity href: https://nmonesource.com/nmos/nmsa/en/item/4359/index.do - - title: SC Code Ann. § 12-6-510 / § 12-6-1140 (pre-2026 conformity) + - title: S.C. Code Ann. § 12-6-50 (Internal Revenue Code sections specifically not adopted by SC; the operative conformity statute amended by Act 110 of 2026) href: https://www.scstatehouse.gov/code/t12c006.php - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm diff --git a/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml b/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml new file mode 100644 index 00000000000..296b7eaf82d --- /dev/null +++ b/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml @@ -0,0 +1,16 @@ +description: Illinois sets this minimum age for filers without qualifying children to be eligible for the Earned Income Tax Credit under the under-25 expansion. + +values: + 2023-01-01: 18 + +metadata: + unit: year + period: year + label: Illinois EITC childless minimum age + reference: + - title: 35 ILCS 5/212 - Illinois Earned Income Tax Credit + href: https://www.ilga.gov/legislation/ilcs/fulltext.asp?DocName=003500050K212 + - title: Public Act 102-0700 - Illinois EITC expansion to age 18+ childless filers + href: https://ilga.gov/legislation/publicacts/fulltext.asp?Name=102-0700 + - title: Illinois Department of Revenue - EITC eligibility guidance + href: https://tax.illinois.gov/programs/eitc.html diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml index 4eb27dca364..f6893525913 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml @@ -9,7 +9,9 @@ metadata: period: year label: Indiana EITC static conformity in effect reference: - - title: IC 6-3.1-21-6 - href: https://iga.in.gov/laws/2021/ic/titles/6#6-3.1-21 - - title: 2023 IT-40 Income Tax Form Instruction Booklet + - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code, fixing the federal-law reference date + href: https://iga.in.gov/laws/2024/ic/titles/6 + - title: IC 6-3.1-21 - Indiana Earned Income Tax Credit chapter + href: https://iga.in.gov/laws/2024/ic/titles/6 + - title: 2023 IT-40 Individual Income Tax Booklet, page 9 (Indiana EITC discussion) href: https://forms.in.gov/Download.aspx?id=15792 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml index d970cb4deb7..67cbfd979ee 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/eitc/co_eitc.yaml @@ -251,3 +251,54 @@ # EITC childless minimum age is also 25, so the filer qualifies for the # standard 50% Colorado match of the federal EITC. co_eitc: 324.5 + +- name: Colorado EITC under-25 path - age 18 homeless filer qualifies + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 18 + employment_income: 10_000 + is_tax_unit_head: true + households: + household: + members: [head] + state_code: CO + is_homeless: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + output: + # CO under-25 expansion homeless_or_foster_min_age path (CRS 39-22-123.5): + # age 18 + homeless qualifies for the same credit as the age-19 default + # path at this income level. + co_eitc: 324.5 + +- name: Colorado EITC under-25 path - age 18 foster youth qualifies + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 18 + employment_income: 10_000 + is_tax_unit_head: true + was_in_foster_care: true + households: + household: + members: [head] + state_code: CO + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + output: + co_eitc: 324.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml index a5d5d5333be..1616490a191 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml @@ -95,3 +95,57 @@ # branch since the ITIN child is counted there. # Expected dc_base_eitc value calculated by tracing dc_eitc_without_qualifying_child. dc_base_eitc: 649 + +- name: DC base EITC without-child branch - SSN filer at age 25 with low income qualifies + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 25 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: CITIZEN + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + # SSN-eligible filer in the federal EITC childless age band (25-64); DC + # base EITC equals dc_eitc since the filer is SSN-qualifying. + dc_base_eitc: 612 + +- name: DC base EITC without-child branch - SSN filer below age 25 does not qualify + period: 2025 + absolute_error_margin: 1 + input: + people: + head: + age: 24 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + ssn_card_type: CITIZEN + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: DC + output: + # Below federal EITC childless minimum age 25 (2025); both base and full + # dc_eitc are zero for a non-ITIN, non-qualifying-child filer. + dc_base_eitc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml index 8ec41264958..86c8465196c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/il/tax/income/credits/il_eitc.yaml @@ -89,3 +89,54 @@ state_code: IL output: il_eitc: 680 + +- name: Illinois EITC childless filer age 18 qualifies for the state EITC + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 18 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: IL + output: + # 35 ILCS 5/212 (P.A. 102-0700) - IL EITC childless minimum age is 18 + # from 2023 onward; age 18 single filer with earned income qualifies. + il_eitc: 122.4 + +- name: Illinois EITC childless filer age 17 does not qualify + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 17 + employment_income: 8_000 + is_tax_unit_head: true + has_tin: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: IL + output: + # Below the IL childless minimum age of 18; not eligible. + il_eitc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml index 0b29a13315f..15f778bc3f3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml @@ -46,3 +46,33 @@ adjusted_gross_income: 10_000 output: in_eitc: 340 + +- name: Indiana EITC TY 2026 uses the SB 243 advanced 2025-07-04 IRC snapshot + period: 2026 + absolute_error_margin: 1 + input: + people: + head: + age: 30 + employment_income: 15_000 + is_tax_unit_head: true + child: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + filing_status: HEAD_OF_HOUSEHOLD + spm_units: + spm_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: IN + output: + # TY 2026 onwards Indiana SB 243 advances the IRC reference to 2025-07-04. + # The static_conformity_in_effect parameter is true; the year-conditional + # snapshot logic picks "2025-07-04" instead of "2023-01-01". A positive + # credit confirms the post-SB 243 frozen-IRC code path is exercised. + in_eitc: 432.80 diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml index 61d8ecbfb61..023e307e68e 100644 --- a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml @@ -180,3 +180,27 @@ # A qualifying relative alone is not sufficient for the treated-unmarried path, # so the separated filer does not qualify for HOH. head_of_household_eligible: false + +- name: Surviving spouse takes precedence over the separated-with-qualifying-child HoH path + period: 2024 + input: + people: + head: + age: 38 + is_tax_unit_head: true + is_separated: true + child: + age: 6 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, child] + surviving_spouse_eligible: true + households: + household: + members: [head, child] + output: + # head_of_household_eligible returns false when surviving_spouse_eligible + # is true, even when the separated-with-qualifying-child path would + # otherwise grant HoH. + head_of_household_eligible: false diff --git a/policyengine_us/tools/state_eitc_helpers.py b/policyengine_us/tools/state_eitc_helpers.py index 93265dae3ae..02f891b989c 100644 --- a/policyengine_us/tools/state_eitc_helpers.py +++ b/policyengine_us/tools/state_eitc_helpers.py @@ -20,9 +20,9 @@ def eitc_filing_status_eligible( """Apply the federal EITC separate-filer rule unless a state overrides it.""" if separate_filer_eligible is None: - separate_filer_eligible = ( - parameters.gov.irs.credits.eitc.eligibility.separate_filer(period) - ) + separate_filer_eligible = parameters( + period + ).gov.irs.credits.eitc.eligibility.separate_filer filing_status = tax_unit("filing_status", period) return separate_filer_eligible | ( filing_status != filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py index c75427e7c04..52011f2eb13 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py @@ -10,16 +10,14 @@ class dc_base_eitc(Variable): value_type = float entity = TaxUnit label = "DC EITC under the federal-incorporation base path" - documentation = ( - "The credit DC would grant under section 47-1806.04 alone, which " - "incorporates IRC section 32 by reference and therefore requires a " - "Social Security Number for the filer and each qualifying child. " - "This is the base value; the ITIN expansion under D.C. Law 23-149 " - "is the difference between dc_eitc and this variable." - ) unit = USD definition_period = YEAR - reference = "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04" + # SSN requirement flows from IRC section 32(c)(1)(F) and 32(m); D.C. Law + # 23-149 lifts that restriction for the ITIN-inclusive dc_eitc. + reference = ( + "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04", # DC EITC base statute + "https://www.law.cornell.edu/uscode/text/26/32", # IRC 32 SSN-rule incorporation + ) defined_for = StateCode.DC def formula(tax_unit, period, parameters): diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py index 1e2c6cc29f3..02dc122e75b 100644 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py +++ b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_eitc.py @@ -13,19 +13,9 @@ class dc_eitc(Variable): ) defined_for = StateCode.DC - # DC EITC architecture: - # - dc_base_eitc: the SSN-only credit under section 47-1806.04 (federal - # IRC section 32 incorporation). This is the base statutory entitlement. - # - dc_eitc (this variable): the Law 23-149 ITIN-inclusive credit. Law - # 23-149 (Earned Income Tax Credit Expansion Clarification Amendment - # Act of 2020) extends the EITC to filers and children with ITINs, so - # dc_eitc >= dc_base_eitc for any household. - # The difference dc_eitc - dc_base_eitc is the incremental Law 23-149 - # benefit. - def formula(tax_unit, period, parameters): - # D.C. Law 23-149 extends the EITC to filers and children with ITINs, - # overriding the federal IRC section 32 SSN-only identification rule. + # D.C. Law 23-149 extends EITC eligibility to filers and qualifying + # children with ITINs; dc_base_eitc holds the SSN-only baseline. person = tax_unit.members dc_qualifying_child = person("is_qualifying_child_dependent", period) & person( "has_tin", period 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 82325be5456..05838bc0c2e 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 @@ -10,6 +10,8 @@ class id_itemized_deductions(Variable): reference = ( "https://tax.idaho.gov/wp-content/uploads/forms/EIN00046/EIN00046_03-01-2023.pdf#page=8", "https://tax.idaho.gov/wp-content/uploads/forms/EFO00089/EFO00089_09-23-2021.pdf", + "https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3022/", # Idaho Code 63-3022 (subtractions/itemized framework) + "https://tax.idaho.gov/wp-content/uploads/forms/EFO00088/", # Idaho Form 39R - Additions and Subtractions instructions (foreign-tax addback rule) ) defined_for = StateCode.ID diff --git a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py index dca0da4da84..0fed0a4d988 100644 --- a/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py +++ b/policyengine_us/variables/gov/states/il/tax/income/credits/il_eitc.py @@ -21,8 +21,9 @@ def formula(tax_unit, period, parameters): qualifying_child = person("is_qualifying_child_dependent", period) & has_tin child_count = tax_unit.sum(qualifying_child) filer_has_tin = tax_unit.sum(is_head_or_spouse & ~has_tin) == 0 + p = parameters(period).gov.states.il.tax.income.credits.eitc demographic_eligible = (child_count > 0) | tax_unit.any( - is_head_or_spouse & (age >= 18) + is_head_or_spouse & (age >= p.childless_min_age) ) state_eitc = calculate_eitc_like_amount( tax_unit, diff --git a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py index 1cb9e92cecb..75a664a45c4 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py @@ -16,6 +16,6 @@ class oh_educator_expense_deduction_person(Variable): "licensure/teaching status or expenses above the federal deduction." ) reference = ( - "https://codes.ohio.gov/ohio-revised-code/section-5747.01", - "https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf", + "https://codes.ohio.gov/ohio-revised-code/section-5747.01#A_31", # ORC 5747.01(A)(31): Ohio educator deduction + "https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf#page=25", # 2025 IT-1040 booklet, Ohio educator expense deduction ) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 55776cfdfc8..1f68cc2677d 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -108,17 +108,19 @@ def formula(tax_unit, period, parameters): eligible = eitc_eligible | state_only_eitc_eligible | age_expansion_eligible # Parameters are based on EITC-eligible children. - eitc_child_count = max_(federal_child_count, child_count) - max_amount = p.amount.calc(eitc_child_count) + # WFTC child count is the larger of the federally-counted children + # (SSN-eligible) and Washington-counted children (TIN-eligible). + wftc_child_count = max_(federal_child_count, child_count) + max_amount = p.amount.calc(wftc_child_count) # WFTC phases out at a certain amount below the EITC maximum AGI. # NB: The Revised Code of Washington is ambiguous: # "below the federal phase-out income" # The legislative analysis clarifies that this refers to "federal maximum AGI" # https://lawfilesext.leg.wa.gov/biennium/2021-22/Pdf/Bill%20Reports/House/1297-S.E%20HBR%20FBR%2021.pdf?q=20220706071752 eitc_agi_limit = calculate_eitc_max_agi_limit( - tax_unit, period, frozen_eitc, eitc_child_count + tax_unit, period, frozen_eitc, wftc_child_count ) - phase_out_start_reduction = p.phase_out.start_below_eitc.calc(eitc_child_count) + phase_out_start_reduction = p.phase_out.start_below_eitc.calc(wftc_child_count) phase_out_start = eitc_agi_limit - phase_out_start_reduction # The phase-out rates are hard-coded in the legal code, but HB 1888 (2021-22) # instructs DOR to revise it to get to the minimum amount by the EITC AGI limit. From edb60e1c8e32e26162eee5e47f0ceb63a3ee51c4 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 19 May 2026 09:35:37 -0400 Subject: [PATCH 11/17] Address Pavel's review on PR #8243 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL fixes: - HoH MFJ mask: separated_qualifies now requires filing_status != JOINT, so a JOINT filer with is_separated=true no longer claims HoH (IRC 7703(b)(1) requires a separate return). - Remove dc_base_eitc.py and its YAML test (unreferenced dead code). - Parameterize IN EITC snapshot date as gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date (2023-01-01 for TY 2023-2025; 2025-07-04 for TY 2026+ per SB 243). - Parameterize WA WFTC snapshot date as gov.states.wa.tax.income.credits.working_families_tax_credit.federal_eitc_snapshot_date (2022-06-09 per RCW 82.08.0206). - Replace `if str(period) == "2021":` hard-coded gate in in_eitc with a new gov.states.in.tax.income.credits.earned_income.childless.in_effect boolean parameter (true only in 2021). - Rewrite decoupled.yaml: add period: metadata, dedupe the redundant 2023-01-01: true entry, restructure the description, and pull page-number text into href #page= anchors. - Drop `documentation =` field on ga_itemized_deductions_adjustment.py and oh_educator_expense_deduction_person.py (skill convention requires `reference =`; both already have one). Inline the previous explanatory text as code comments. - Replace IL childless_min_age.yaml's two dead URLs (HTTP 404) with current ILGA Chapter/Act/Section deep links and the IDOR EITC page. - Tighten SC § 12-6-50 reference titles to make clear that the operative amendment is in H. 4216 / Act 110 of 2026 (clicking the Code link today still shows the un-amended text). SHOULD ADDRESS: - Add a YAML test confirming a JOINT filer with is_separated=true is NOT HoH-eligible (regression guard for CRITICAL #1). - Add a divisor-safety comment to hi_student_loan_interest_adjustment.py explaining that the SEPARATE divisor is non-zero only to keep the vectorized arithmetic safe; the where(separate, 0, ...) guard masks the result. Changelog: expanded 8243.fixed.md with notes on the OH educator under-implementation (federal $300 cap no longer auto-applied to the OH deduction list; new oh_educator_expense_deduction_person is an explicit-input stub), CT silently dropped from federal-conformity lists, SC 2026 decoupling source, GA stub limitations, and the HI student loan interest deduction subtree. --- changelog.d/8243.fixed.md | 10 +- ...tes_using_federal_itemized_deductions.yaml | 12 +- ...ates_using_federal_standard_deduction.yaml | 18 +-- .../credits/eitc/childless_min_age.yaml | 14 +- .../earned_income/childless/in_effect.yaml | 12 ++ .../credits/earned_income/decoupled.yaml | 21 ++- .../static_conformity_snapshot_date.yaml | 13 ++ .../federal_eitc_snapshot_date.yaml | 10 ++ .../states/dc/tax/income/dc_base_eitc.yaml | 151 ------------------ .../tax_unit/head_of_household_eligible.yaml | 27 ++++ .../tax/income/credits/eitc/dc_base_eitc.py | 70 -------- .../ga_itemized_deductions_adjustment.py | 13 +- .../hi_student_loan_interest_adjustment.py | 4 + .../credits/earned_income_credit/in_eitc.py | 17 +- .../earned_income_credit/in_eitc_eligible.py | 11 +- .../oh_educator_expense_deduction_person.py | 13 +- .../credits/wa_working_families_tax_credit.py | 6 +- ...ilies_tax_credit_age_expansion_eligible.py | 9 +- .../tax_unit/head_of_household_eligible.py | 7 +- 19 files changed, 152 insertions(+), 286 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/childless/in_effect.yaml create mode 100644 policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml create mode 100644 policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml delete mode 100644 policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py diff --git a/changelog.d/8243.fixed.md b/changelog.d/8243.fixed.md index afae3988212..b14386fe056 100644 --- a/changelog.d/8243.fixed.md +++ b/changelog.d/8243.fixed.md @@ -1 +1,9 @@ -Fixed federal/state conformity across EITC, deduction, ALD, HoH, and CDCC linkages, including static-conformity handling and state-specific deduction hookups. +Fixed federal/state conformity across EITC, deduction, ALD, HoH, and CDCC linkages. + +- Federal HoH: tighten IRC 7703(b) "considered unmarried" to MFS/HoH/single filers only; a JOINT filer with `is_separated=true` no longer qualifies. +- IN/WA EITC: replace hard-coded IRC snapshot dates with new parameters (`gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date` and `gov.states.wa.tax.income.credits.working_families_tax_credit.federal_eitc_snapshot_date`). Replace the IN 2021-only branch with a `gov.states.in.tax.income.credits.earned_income.childless.in_effect` gate. +- CT dropped from `states_using_federal_itemized_deductions` and `states_using_federal_standard_deduction` (CT-1040 uses neither — only a personal exemption); SC drops out effective TY 2026 per H. 4216 / Act 110 of 2026. +- DC: `dc_eitc` continues to model Law 23-149 ITIN inclusivity; the previously-added `dc_base_eitc` diagnostic variable is removed (unreferenced in this PR's scope). +- OH educator expense: the federal `educator_expense` is no longer in OH's deductions list. ORC § 5747.01(A)(31) only allows the *excess* above the federal $300 cap, modeled via the new `oh_educator_expense_deduction_person` input variable. Filers without explicit input receive 0 for the OH-only excess; downstream microdata should populate it. The federal-cap interaction is no longer auto-applied to the OH deduction list — an under-implementation, documented as a known limitation. +- GA: `ga_itemized_deductions_adjustment` ships as an explicit-input stub for now (other-state taxes and exempt-investment interest are not separately observed in the baseline microdata). +- HI: new student loan interest deduction subtree (`gov.states.hi.tax.income.subtractions.student_loan_interest/*`) and supporting `hi_modified_agi`, `hi_student_loan_interest_*` variables modelling pre-current IRC § 221. diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml index 0ebd583c4ce..6174e9f6106 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml @@ -1,4 +1,4 @@ -description: List of states (USPS codes) that adopt the federal itemized deductions for state income tax. South Carolina decoupled effective tax year 2026 via H. 4216, Act 110 of 2026 (signed 2026-03-30), amending S.C. Code section 12-6-50. +description: List of USPS state codes whose income tax adopts the federal itemized deductions under the state income tax program. metadata: unit: list period: year @@ -6,13 +6,13 @@ metadata: reference: - title: O.C.G.A. § 48-7-27(a)(1) - Georgia itemized deductions href: https://law.justia.com/codes/georgia/2024/title-48/chapter-7/article-2/section-48-7-27/ - - title: N.D.C.C. § 57-38-01.2 - North Dakota federal-conformity itemized deductions - href: https://www.legis.nd.gov/cencode/t57c38.pdf - - title: S.C. Code Ann. § 12-6-50 (Internal Revenue Code sections specifically not adopted by SC; the operative conformity statute amended by Act 110 of 2026) + - title: N.D.C.C. § 57-38-01.2(1)(b) - North Dakota federal-conformity itemized deductions + href: https://www.legis.nd.gov/cencode/t57c38.pdf#page=2 + - title: S.C. Code Ann. § 12-6-50 - South Carolina conformity statute (Internal Revenue Code sections specifically not adopted; amended by H. 4216 / Act 110 of 2026 to add § 63) href: https://www.scstatehouse.gov/code/t12c006.php - - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 + - title: South Carolina H. 4216 / Act 110 of 2026 - decouples from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm - - title: Utah Code § 59-10-1018 - Utah itemized deductions + - title: Utah Code § 59-10-1018(1)(a) - Utah itemized deductions href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: 2014-01-01: diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml index b44f2e293fd..7a295828171 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml @@ -1,24 +1,24 @@ -description: List of states (USPS codes) that adopt the federal standard deduction for state income tax. South Carolina decoupled effective tax year 2026 via H. 4216, Act 110 of 2026 (signed 2026-03-30), amending S.C. Code section 12-6-50. +description: List of USPS state codes whose income tax adopts the federal standard deduction under the state income tax program. metadata: unit: list period: year label: States adopting federal standard deduction reference: - - title: CRS § 39-22-104 - Colorado conformity to federal taxable income (which incorporates federal standard deduction) + - title: CRS § 39-22-104(2) - Colorado conformity to federal taxable income href: https://leg.colorado.gov/sites/default/files/images/olls/crs2024-title-39.pdf - - title: Idaho Code § 63-3022 - Idaho federal standard deduction conformity + - title: Idaho Code § 63-3022(j)(1) - Idaho federal standard deduction conformity href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3022/ - - title: RSMo § 143.131 - Missouri federal standard deduction conformity + - title: RSMo § 143.131(1) - Missouri federal standard deduction conformity href: https://revisor.mo.gov/main/OneSection.aspx?section=143.131 - - title: N.D.C.C. § 57-38-30.3 - North Dakota federal taxable income conformity - href: https://www.legis.nd.gov/cencode/t57c38.pdf + - title: N.D.C.C. § 57-38-30.3(1)(b) - North Dakota federal taxable income conformity + href: https://www.legis.nd.gov/cencode/t57c38.pdf#page=2 - title: NMSA § 7-2-7.5 - New Mexico federal standard deduction conformity href: https://nmonesource.com/nmos/nmsa/en/item/4359/index.do - - title: S.C. Code Ann. § 12-6-50 (Internal Revenue Code sections specifically not adopted by SC; the operative conformity statute amended by Act 110 of 2026) + - title: S.C. Code Ann. § 12-6-50 - South Carolina conformity statute (Internal Revenue Code sections specifically not adopted; amended by H. 4216 / Act 110 of 2026 to add § 63) href: https://www.scstatehouse.gov/code/t12c006.php - - title: H. 4216 / Act 110 of 2026 — South Carolina decoupling from federal itemized and standard deductions effective tax year 2026 + - title: South Carolina H. 4216 / Act 110 of 2026 - decouples from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm - - title: Utah Code § 59-10-1018 - Utah federal standard deduction conformity + - title: Utah Code § 59-10-1018(1)(a) - Utah federal standard deduction conformity href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: 2014-01-01: diff --git a/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml b/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml index 296b7eaf82d..1e7020ae2f3 100644 --- a/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml +++ b/policyengine_us/parameters/gov/states/il/tax/income/credits/eitc/childless_min_age.yaml @@ -1,4 +1,4 @@ -description: Illinois sets this minimum age for filers without qualifying children to be eligible for the Earned Income Tax Credit under the under-25 expansion. +description: Illinois sets this minimum age for filers without qualifying children to be eligible for the Earned Income Tax Credit under the Illinois Earned Income Tax Credit program. values: 2023-01-01: 18 @@ -8,9 +8,9 @@ metadata: period: year label: Illinois EITC childless minimum age reference: - - title: 35 ILCS 5/212 - Illinois Earned Income Tax Credit - href: https://www.ilga.gov/legislation/ilcs/fulltext.asp?DocName=003500050K212 - - title: Public Act 102-0700 - Illinois EITC expansion to age 18+ childless filers - href: https://ilga.gov/legislation/publicacts/fulltext.asp?Name=102-0700 - - title: Illinois Department of Revenue - EITC eligibility guidance - href: https://tax.illinois.gov/programs/eitc.html + - title: 35 ILCS 5/212 (Illinois Earned Income Tax Credit, including the age-18 minimum for filers without qualifying children added by P.A. 102-0700) + href: https://www.ilga.gov/Legislation/ILCS/fulltext.asp?DocName=003500050K212&SeqStart=15400000&SeqEnd=16000000 + - title: Illinois Public Act 102-0700 (HB 4920) - Illinois EITC expansion to childless filers age 18+ + href: https://www.ilga.gov/Legislation/PublicActs/Fulltext.asp?Name=102-0700&GA=102 + - title: Illinois Department of Revenue - Illinois Earned Income Tax Credit eligibility + href: https://tax.illinois.gov/individuals/credits/earnedincomecredit.html diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/childless/in_effect.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/childless/in_effect.yaml new file mode 100644 index 00000000000..686542b71ed --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/childless/in_effect.yaml @@ -0,0 +1,12 @@ +description: Indiana applies its state-specific decoupled childless Earned Income Tax Credit parameters when this indicator is true. +values: + 2010-01-01: false + 2021-01-01: true + 2022-01-01: false +metadata: + unit: bool + period: year + label: Indiana EITC childless decoupled parameters in effect + reference: + - title: IC 6-3.1-21 - Indiana Earned Income Tax Credit (2021 childless override) + href: https://iga.in.gov/laws/2021/ic/titles/6#6-3.1-21 diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml index 68088df3611..f635db2cfd9 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml @@ -1,18 +1,17 @@ -description: Indiana has a state EITC that is decoupled from the federal EITC if this parameter is true. +description: Indiana decouples its Earned Income Tax Credit from the federal EITC under the Indiana Earned Income Tax Credit program when this indicator is true. metadata: unit: bool + period: year label: Whether IN EITC is decoupled from federal EITC reference: - - title: Indiana General Assembly Laws - href: https://iga.in.gov/laws/2021/ic/titles/6#6-3.1-21 - - title: 2023 IT-40 Tax Form Instructions Booklet, pg.3 - href: https://forms.in.gov/Download.aspx?id=15792 - - title: 2025 IT-40 Income Tax Form Instruction Booklet, pg. 30 - href: https://forms.in.gov/Download.aspx?id=16915 - - + - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code (fixes the federal-law reference date for the decoupled branch) + href: https://iga.in.gov/laws/2024/ic/titles/6 + - title: IC 6-3.1-21 - Indiana Earned Income Tax Credit chapter + href: https://iga.in.gov/laws/2024/ic/titles/6 + - title: 2023 IT-40 Individual Income Tax Booklet (Indiana EITC discussion) + href: https://forms.in.gov/Download.aspx?id=15792#page=9 + - title: 2025 IT-40 Income Tax Form Instruction Booklet + href: https://forms.in.gov/Download.aspx?id=16915#page=30 values: 2010-01-01: false 2011-01-01: true - 2023-01-01: true - diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml new file mode 100644 index 00000000000..77c5ae27bc6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml @@ -0,0 +1,13 @@ +description: Indiana freezes the federal Internal Revenue Code reference for the static-conformity Earned Income Tax Credit branch to the IRC as in effect on this date. +values: + 2023-01-01: 2023-01-01 + 2026-01-01: 2025-07-04 +metadata: + unit: date + period: year + label: Indiana EITC static conformity snapshot date + reference: + - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code (TY 2023-2025 freeze at 2023-01-01) + href: https://iga.in.gov/laws/2024/ic/titles/6 + - title: SB 243 of 2026 - Advances Indiana's IRC conformity to July 4, 2025 effective TY 2026 + href: https://iga.in.gov/legislative/2026/bills/senate/243 diff --git a/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml b/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml new file mode 100644 index 00000000000..1717a0ed8bb --- /dev/null +++ b/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml @@ -0,0 +1,10 @@ +description: Washington uses the federal EITC rules as in effect on this date for the Working Families Tax Credit under RCW 82.08.0206(2)(d). +values: + 2008-01-01: 2022-06-09 +metadata: + unit: date + period: year + label: Washington WFTC federal EITC snapshot date + reference: + - title: RCW 82.08.0206(2)(d) - definition of "qualifying child" pinning the federal EITC reference to June 9, 2022 + href: https://app.leg.wa.gov/RCW/default.aspx?cite=82.08.0206 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml deleted file mode 100644 index 1616490a191..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/dc/tax/income/dc_base_eitc.yaml +++ /dev/null @@ -1,151 +0,0 @@ -- name: SSN filer with SSN child - dc_base_eitc matches dc_eitc - period: 2025 - absolute_error_margin: 0.01 - input: - people: - head: - age: 30 - employment_income: 15_000 - is_tax_unit_head: true - has_tin: true - ssn_card_type: CITIZEN - child: - age: 5 - is_tax_unit_dependent: true - has_tin: true - ssn_card_type: CITIZEN - tax_units: - tax_unit: - members: [head, child] - filing_status: HEAD_OF_HOUSEHOLD - spm_units: - spm_unit: - members: [head, child] - households: - household: - members: [head, child] - state_code: DC - output: - # SSN-qualifying household - base path and Law 23-149 path produce the same credit. - # Federal EITC at $15K HoH with 1 qualifying child = $4,328 (phase-in capped - # at max for one child). DC match is 100% in 2025. - dc_base_eitc: 4_328 - dc_eitc: 4_328 - -- name: ITIN filer - dc_base_eitc is zero, dc_eitc is positive via Law 23-149 - period: 2025 - absolute_error_margin: 0.01 - input: - people: - head: - age: 30 - employment_income: 8_000 - is_tax_unit_head: true - has_tin: true - ssn_card_type: OTHER_NON_CITIZEN - tax_units: - tax_unit: - members: [head] - filing_status: SINGLE - spm_units: - spm_unit: - members: [head] - households: - household: - members: [head] - state_code: DC - output: - # Section 47-1806.04 base path requires an SSN-valid identification; ITIN - # filers receive zero under the base path but Law 23-149 grants the credit. - dc_base_eitc: 0 - dc_eitc: 612 - -- name: SSN filer with ITIN child - dc_base_eitc uses no-child branch, dc_eitc uses with-child branch - period: 2025 - absolute_error_margin: 0.01 - input: - people: - head: - age: 35 - employment_income: 10_000 - is_tax_unit_head: true - has_tin: true - ssn_card_type: CITIZEN - child: - age: 5 - is_tax_unit_dependent: true - has_tin: true - ssn_card_type: OTHER_NON_CITIZEN - tax_units: - tax_unit: - members: [head, child] - filing_status: HEAD_OF_HOUSEHOLD - spm_units: - spm_unit: - members: [head, child] - households: - household: - members: [head, child] - state_code: DC - output: - # SSN-only base path: no SSN qualifying child, so the base falls back - # to the childless EITC branch (filer is age 35 with SSN, age-eligible). - # Federal childless EITC at $10K plus DC phase-out at $10K AGI yields - # a positive credit. Law 23-149 (dc_eitc) routes through the with-child - # branch since the ITIN child is counted there. - # Expected dc_base_eitc value calculated by tracing dc_eitc_without_qualifying_child. - dc_base_eitc: 649 - -- name: DC base EITC without-child branch - SSN filer at age 25 with low income qualifies - period: 2025 - absolute_error_margin: 1 - input: - people: - head: - age: 25 - employment_income: 8_000 - is_tax_unit_head: true - has_tin: true - ssn_card_type: CITIZEN - tax_units: - tax_unit: - members: [head] - filing_status: SINGLE - spm_units: - spm_unit: - members: [head] - households: - household: - members: [head] - state_code: DC - output: - # SSN-eligible filer in the federal EITC childless age band (25-64); DC - # base EITC equals dc_eitc since the filer is SSN-qualifying. - dc_base_eitc: 612 - -- name: DC base EITC without-child branch - SSN filer below age 25 does not qualify - period: 2025 - absolute_error_margin: 1 - input: - people: - head: - age: 24 - employment_income: 8_000 - is_tax_unit_head: true - has_tin: true - ssn_card_type: CITIZEN - tax_units: - tax_unit: - members: [head] - filing_status: SINGLE - spm_units: - spm_unit: - members: [head] - households: - household: - members: [head] - state_code: DC - output: - # Below federal EITC childless minimum age 25 (2025); both base and full - # dc_eitc are zero for a non-ITIN, non-qualifying-child filer. - dc_base_eitc: 0 diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml index 023e307e68e..d7fe6115967 100644 --- a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_of_household_eligible.yaml @@ -204,3 +204,30 @@ # is true, even when the separated-with-qualifying-child path would # otherwise grant HoH. head_of_household_eligible: false + +- name: Separated JOINT filer with qualifying child is NOT eligible for HoH + # IRC 7703(b)(1) "considered unmarried" path requires filing a separate + # return. A JOINT filer who happens to be separated forfeits the + # treated-unmarried route by choosing joint filing status. + period: 2024 + input: + people: + parent: + age: 38 + is_tax_unit_head: true + is_separated: true + spouse: + age: 36 + is_tax_unit_spouse: true + child: + age: 6 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [parent, spouse, child] + filing_status: JOINT + households: + household: + members: [parent, spouse, child] + output: + head_of_household_eligible: false diff --git a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py b/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py deleted file mode 100644 index 52011f2eb13..00000000000 --- a/policyengine_us/variables/gov/states/dc/tax/income/credits/eitc/dc_base_eitc.py +++ /dev/null @@ -1,70 +0,0 @@ -from policyengine_us.model_api import * -from policyengine_us.tools.state_eitc_helpers import ( - calculate_eitc_like_amount, - eitc_filing_requirement_met, - eitc_filing_status_eligible, -) - - -class dc_base_eitc(Variable): - value_type = float - entity = TaxUnit - label = "DC EITC under the federal-incorporation base path" - unit = USD - definition_period = YEAR - # SSN requirement flows from IRC section 32(c)(1)(F) and 32(m); D.C. Law - # 23-149 lifts that restriction for the ITIN-inclusive dc_eitc. - reference = ( - "https://code.dccouncil.gov/us/dc/council/code/sections/47-1806.04", # DC EITC base statute - "https://www.law.cornell.edu/uscode/text/26/32", # IRC 32 SSN-rule incorporation - ) - defined_for = StateCode.DC - - def formula(tax_unit, period, parameters): - person = tax_unit.members - age = person("age", period) - meets_eitc_id = person("meets_eitc_identification_requirements", period) - is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) - filer_meets_id = tax_unit.sum(is_head_or_spouse & ~meets_eitc_id) == 0 - qualifying_child = ( - person("is_qualifying_child_dependent", period) & meets_eitc_id - ) - child_count = tax_unit.sum(qualifying_child) - has_qualifying_child = child_count > 0 - p = parameters(period).gov.states.dc.tax.income.credits - - with_child_eitc = ( - calculate_eitc_like_amount( - tax_unit, - period, - parameters, - child_count, - has_qualifying_child, - filer_meets_id, - ) - * p.eitc.with_children.match - ) - - min_age = parameters.gov.irs.credits.eitc.eligibility.age.min(period) - max_age = parameters.gov.irs.credits.eitc.eligibility.age.max(period) - age_eligible = is_head_or_spouse & (age >= min_age) & (age <= max_age) - without_child_eligible = ( - filer_meets_id - & ~has_qualifying_child - & tax_unit.any(age_eligible) - & tax_unit("eitc_investment_income_eligible", period) - & eitc_filing_status_eligible(tax_unit, period, parameters) - & eitc_filing_requirement_met(tax_unit, period) - & tax_unit("takes_up_eitc", period) - ) - without_child_phased_in = without_child_eligible * tax_unit( - "eitc_phased_in", period - ) - earnings = tax_unit("tax_unit_earned_income", period) - agi = tax_unit("adjusted_gross_income", period) - greater_of = max_(earnings, agi) - excess = max_(0, greater_of - p.eitc.without_children.phase_out.start) - without_child_phase_out = excess * p.eitc.without_children.phase_out.rate - without_child_eitc = max_(0, without_child_phased_in - without_child_phase_out) - - return where(has_qualifying_child, with_child_eitc, without_child_eitc) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py index 993cdfd255f..412512d142c 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/deductions/ga_itemized_deductions_adjustment.py @@ -9,10 +9,11 @@ class ga_itemized_deductions_adjustment(Variable): definition_period = YEAR default_value = 0 defined_for = StateCode.GA - documentation = ( - "Georgia-specific reduction from federal Schedule A before computing " - "Georgia itemized deductions. This remains an explicit input because " - "the model does not separately observe components such as other-state " - "income taxes and exempt-income investment interest." + # Georgia-specific reduction from federal Schedule A before computing + # Georgia itemized deductions. Remains an explicit input because the + # baseline microdata do not separately observe components such as + # other-state income taxes and exempt-income investment interest. + reference = ( + "https://law.justia.com/codes/georgia/2024/title-48/chapter-7/article-2/section-48-7-27/", + "https://dor.georgia.gov/document/document/2025-it-511-individual-income-tax-booklet/download#page=16", ) - reference = "https://dor.georgia.gov/document/document/2025-it-511-individual-income-tax-booklet/download" diff --git a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py index 524f1bc17c0..c4c7203eeff 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/subtractions/hi_student_loan_interest_adjustment.py @@ -29,6 +29,10 @@ def formula(tax_unit, period, parameters): status = filing_status.possible_values separate = filing_status == status.SEPARATE phase_out_start = p_sli.phase_out.start[filing_status] + # SEPARATE filers receive no Hawaii SLI deduction; we still need a + # non-zero divisor here so the vectorized phase-out arithmetic does + # not divide by zero. The where(separate, 0, ...) guard below masks + # the result. phase_out_divisor = p_sli.phase_out.divisor[filing_status] reduction_share = min_( 1, max_(0, (hi_magi - phase_out_start) / phase_out_divisor) diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index af0979acdd5..11c4eb1e0db 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -20,12 +20,12 @@ def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate if ip.earned_income.static_conformity_in_effect: - # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): - # - TY 2023 through 2025: IRC as in effect on January 1, 2023. - # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 - # of 2026 (signed by Governor Braun, advancing conformity to - # include sections of H.R. 1 / Public Law 119-21). - snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21) pins the + # federal-law reference date used to compute the EITC. The exact + # snapshot date lives in + # `gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date` + # (2023-01-01 for TY 2023-2025; 2025-07-04 for TY 2026+ per SB 243). + snapshot_date = ip.earned_income.static_conformity_snapshot_date frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( @@ -60,8 +60,9 @@ def formula(tax_unit, period, parameters): pi_rate = fp.eitc.phase_in_rate.calc(kids) po_start = fp.eitc.phase_out.start.calc(kids) # no JOINT bonus po_rate = fp.eitc.phase_out.rate.calc(kids) - if str(period) == "2021": - # ... additional decoupling of parameters for childless taxpayers + if ip.earned_income.childless.in_effect: + # 2021-only decoupled-childless-parameter branch, gated by + # gov.states.in.tax.income.credits.earned_income.childless.in_effect. maximum0 = ip.earned_income.childless.maximum pi_rate0 = ip.earned_income.childless.phase_in_rate po_start0 = ip.earned_income.childless.phase_out_start diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index df62d185a1b..a1b874f1794 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -21,11 +21,12 @@ def formula(tax_unit, period, parameters): if not p.credits.earned_income.decoupled: return gets_federal_eitc if p.credits.earned_income.static_conformity_in_effect: - # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): - # - TY 2023 through 2025: IRC as in effect on January 1, 2023. - # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 - # of 2026. - snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21) pins the + # federal-law reference date via the + # gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date + # parameter (2023-01-01 for TY 2023-2025; 2025-07-04 for TY 2026+ + # per SB 243 of 2026). + snapshot_date = p.credits.earned_income.static_conformity_snapshot_date frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py index 75a664a45c4..88a3c1c74c7 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py @@ -9,12 +9,13 @@ class oh_educator_expense_deduction_person(Variable): definition_period = YEAR default_value = 0 defined_for = StateCode.OH - documentation = ( - "Ohio-specific educator deduction allowed in excess of the federal " - "educator deduction for a qualifying Ohio educator. This remains an " - "explicit input because the baseline data do not identify Ohio " - "licensure/teaching status or expenses above the federal deduction." - ) + # Ohio-specific educator deduction allowed in excess of the federal + # educator deduction for a qualifying Ohio educator. This remains an + # explicit input because the baseline data do not identify Ohio + # licensure/teaching status or expenses above the federal deduction. + # Replacing the federal educator_expense in oh/.../deductions.yaml with + # this Ohio-specific stub means filers without explicit input receive 0 + # for Ohio purposes; downstream microdata should populate it. reference = ( "https://codes.ohio.gov/ohio-revised-code/section-5747.01#A_31", # ORC 5747.01(A)(31): Ohio educator deduction "https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf#page=25", # 2025 IT-1040 booklet, Ohio educator expense deduction diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index 1f68cc2677d..cc214a0b0f9 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -24,8 +24,10 @@ def formula(tax_unit, period, parameters): period ).gov.states.wa.tax.income.credits.working_families_tax_credit # RCW 82.08.0206 pins Washington's WFTC to the federal EITC rules as - # in effect on June 9, 2022 (the date the statute references). - frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") + # in effect on the date stored in + # gov.states.wa.tax.income.credits.working_families_tax_credit.federal_eitc_snapshot_date + # (currently 2022-06-09, the date the statute references). + frozen_eitc = parameters.gov.irs.credits.eitc(p.federal_eitc_snapshot_date) person = tax_unit.members has_tin = person("has_tin", period) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index d34e0a1ff21..b1540a2fedd 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -15,9 +15,10 @@ class wa_working_families_tax_credit_age_expansion_eligible(Variable): def formula(tax_unit, period, parameters): # ESSB 6346 Sec. 901(2)(a)(ii)(D): individuals who would otherwise # qualify for EITC except for age can qualify if at least age 18. - p = parameters( + wftc = parameters( period - ).gov.states.wa.tax.income.credits.working_families_tax_credit.age_expansion + ).gov.states.wa.tax.income.credits.working_families_tax_credit + p = wftc.age_expansion expansion_in_effect = p.in_effect person = tax_unit.members @@ -30,7 +31,9 @@ def formula(tax_unit, period, parameters): child_count = tax_unit.sum( person("is_qualifying_child_dependent", period) & has_tin ) - frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") + # RCW 82.08.0206(2)(d) freezes WFTC to the federal EITC rules as of + # wftc.federal_eitc_snapshot_date (currently 2022-06-09). + frozen_eitc = parameters.gov.irs.credits.eitc(wftc.federal_eitc_snapshot_date) frozen_investment_income_eligible = ( tax_unit("eitc_relevant_investment_income", period) <= frozen_eitc.phase_out.max_investment_income diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py index ee33504574f..0cd9a754347 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py @@ -10,6 +10,8 @@ class head_of_household_eligible(Variable): def formula(tax_unit, period, parameters): married = tax_unit("tax_unit_married", period) + filing_status = tax_unit("filing_status", period) + joint = filing_status == filing_status.possible_values.JOINT person = tax_unit.members # IRC 7703(b) "considered unmarried" applies to the taxpayer (head or # spouse), not to dependents. A separated dependent must not trigger @@ -32,8 +34,11 @@ def formula(tax_unit, period, parameters): has_qualifying_person = tax_unit.sum(is_hoh_qualifying) > 0 # IRC 7703(b) treated-unmarried status supports HoH only through the # child-abode path, not the broader qualifying-relative route. + # IRC 7703(b)(1) further requires the taxpayer to file a separate + # return: a JOINT filer who is separated cannot claim HoH because + # the joint return forecloses the "considered unmarried" path. treated_unmarried_qualifies = tax_unit.sum(is_qualifying_child) > 0 surviving_spouse = tax_unit("surviving_spouse_eligible", period) unmarried_qualifies = has_qualifying_person & ~married & ~is_separated - separated_qualifies = treated_unmarried_qualifies & is_separated + separated_qualifies = treated_unmarried_qualifies & is_separated & ~joint return (unmarried_qualifies | separated_qualifies) & ~surviving_spouse From 6cc4fc92575591846b8f2c53f277ed1aca7a6f0a Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 19 May 2026 09:41:27 -0400 Subject: [PATCH 12/17] Quote date parameter values for YAML parser YAML auto-parses date-formatted strings as datetime.date objects, which policyengine-core's parameter_at_instant rejects ('str' has no .get). Quote the values to keep them as strings; parameters.gov.irs.credits.eitc() accepts a string instant-spec internally. --- .../earned_income/static_conformity_snapshot_date.yaml | 5 ++--- .../federal_eitc_snapshot_date.yaml | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml index 77c5ae27bc6..ebe91abf155 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml @@ -1,9 +1,8 @@ description: Indiana freezes the federal Internal Revenue Code reference for the static-conformity Earned Income Tax Credit branch to the IRC as in effect on this date. values: - 2023-01-01: 2023-01-01 - 2026-01-01: 2025-07-04 + 2023-01-01: "2023-01-01" + 2026-01-01: "2025-07-04" metadata: - unit: date period: year label: Indiana EITC static conformity snapshot date reference: diff --git a/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml b/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml index 1717a0ed8bb..2fcaee77752 100644 --- a/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml +++ b/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml @@ -1,8 +1,7 @@ description: Washington uses the federal EITC rules as in effect on this date for the Working Families Tax Credit under RCW 82.08.0206(2)(d). values: - 2008-01-01: 2022-06-09 + 2008-01-01: "2022-06-09" metadata: - unit: date period: year label: Washington WFTC federal EITC snapshot date reference: From 16c7a000184254b7d1de8d405d1aa2dfaecae8b2 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 19 May 2026 09:47:12 -0400 Subject: [PATCH 13/17] Revert snapshot-date parameterization (unsupported by policyengine-core) policyengine-core's parameter system rejects string-valued parameters ('str' object has no attribute 'get'). The IN and WA snapshot-date parameters introduced in the previous commit caused every import to fail at startup. Revert to statutory date literals in Python with explicit comments citing the operative statute and noting why the date lives in code (date-valued parameters are not supported in policyengine-core today). This addresses Pavel's concern in spirit by making the literals discoverable and self-documenting, but leaves the parameterization question for a future policyengine-core enhancement. Keep the parameterization that does work: gov.states.in.tax.income.credits.earned_income.childless.in_effect (boolean gate for the 2021-only branch). --- .../static_conformity_snapshot_date.yaml | 12 ------------ .../federal_eitc_snapshot_date.yaml | 9 --------- .../credits/earned_income_credit/in_eitc.py | 15 +++++++++------ .../earned_income_credit/in_eitc_eligible.py | 13 +++++++------ .../credits/wa_working_families_tax_credit.py | 11 ++++++----- ..._families_tax_credit_age_expansion_eligible.py | 11 +++++------ 6 files changed, 27 insertions(+), 44 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml delete mode 100644 policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml deleted file mode 100644 index ebe91abf155..00000000000 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_snapshot_date.yaml +++ /dev/null @@ -1,12 +0,0 @@ -description: Indiana freezes the federal Internal Revenue Code reference for the static-conformity Earned Income Tax Credit branch to the IRC as in effect on this date. -values: - 2023-01-01: "2023-01-01" - 2026-01-01: "2025-07-04" -metadata: - period: year - label: Indiana EITC static conformity snapshot date - reference: - - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code (TY 2023-2025 freeze at 2023-01-01) - href: https://iga.in.gov/laws/2024/ic/titles/6 - - title: SB 243 of 2026 - Advances Indiana's IRC conformity to July 4, 2025 effective TY 2026 - href: https://iga.in.gov/legislative/2026/bills/senate/243 diff --git a/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml b/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml deleted file mode 100644 index 2fcaee77752..00000000000 --- a/policyengine_us/parameters/gov/states/wa/tax/income/credits/working_families_tax_credit/federal_eitc_snapshot_date.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: Washington uses the federal EITC rules as in effect on this date for the Working Families Tax Credit under RCW 82.08.0206(2)(d). -values: - 2008-01-01: "2022-06-09" -metadata: - period: year - label: Washington WFTC federal EITC snapshot date - reference: - - title: RCW 82.08.0206(2)(d) - definition of "qualifying child" pinning the federal EITC reference to June 9, 2022 - href: https://app.leg.wa.gov/RCW/default.aspx?cite=82.08.0206 diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index 11c4eb1e0db..2d3eb8e1dca 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -20,12 +20,15 @@ def formula(tax_unit, period, parameters): federal_eitc = tax_unit("eitc", period) return federal_eitc * ip.earned_income.match_rate if ip.earned_income.static_conformity_in_effect: - # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21) pins the - # federal-law reference date used to compute the EITC. The exact - # snapshot date lives in - # `gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date` - # (2023-01-01 for TY 2023-2025; 2025-07-04 for TY 2026+ per SB 243). - snapshot_date = ip.earned_income.static_conformity_snapshot_date + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): + # - TY 2023 through 2025: IRC as in effect on January 1, 2023. + # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 + # of 2026 (signed by Governor Braun, advancing conformity to + # include sections of H.R. 1 / Public Law 119-21). + # The snapshot dates are statutory literals; policyengine-core + # parameters do not support date-valued types, so they appear + # here rather than in the parameter tree. + snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index a1b874f1794..1f5724526f0 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -21,12 +21,13 @@ def formula(tax_unit, period, parameters): if not p.credits.earned_income.decoupled: return gets_federal_eitc if p.credits.earned_income.static_conformity_in_effect: - # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21) pins the - # federal-law reference date via the - # gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date - # parameter (2023-01-01 for TY 2023-2025; 2025-07-04 for TY 2026+ - # per SB 243 of 2026). - snapshot_date = p.credits.earned_income.static_conformity_snapshot_date + # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): + # - TY 2023 through 2025: IRC as in effect on January 1, 2023. + # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243. + # The snapshot dates are statutory literals; policyengine-core + # parameters do not support date-valued types, so they appear + # here rather than in the parameter tree. + snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py index cc214a0b0f9..c499bafa4e5 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.py @@ -23,11 +23,12 @@ def formula(tax_unit, period, parameters): p = parameters( period ).gov.states.wa.tax.income.credits.working_families_tax_credit - # RCW 82.08.0206 pins Washington's WFTC to the federal EITC rules as - # in effect on the date stored in - # gov.states.wa.tax.income.credits.working_families_tax_credit.federal_eitc_snapshot_date - # (currently 2022-06-09, the date the statute references). - frozen_eitc = parameters.gov.irs.credits.eitc(p.federal_eitc_snapshot_date) + # RCW 82.08.0206(2)(d) pins Washington's WFTC to the federal EITC + # rules as in effect on June 9, 2022. The snapshot date is a + # statutory literal; policyengine-core parameters do not support + # date-valued types, so the date appears here rather than in the + # parameter tree. + frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") person = tax_unit.members has_tin = person("has_tin", period) is_head_or_spouse = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index b1540a2fedd..6af838b7034 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -15,10 +15,9 @@ class wa_working_families_tax_credit_age_expansion_eligible(Variable): def formula(tax_unit, period, parameters): # ESSB 6346 Sec. 901(2)(a)(ii)(D): individuals who would otherwise # qualify for EITC except for age can qualify if at least age 18. - wftc = parameters( + p = parameters( period - ).gov.states.wa.tax.income.credits.working_families_tax_credit - p = wftc.age_expansion + ).gov.states.wa.tax.income.credits.working_families_tax_credit.age_expansion expansion_in_effect = p.in_effect person = tax_unit.members @@ -31,9 +30,9 @@ def formula(tax_unit, period, parameters): child_count = tax_unit.sum( person("is_qualifying_child_dependent", period) & has_tin ) - # RCW 82.08.0206(2)(d) freezes WFTC to the federal EITC rules as of - # wftc.federal_eitc_snapshot_date (currently 2022-06-09). - frozen_eitc = parameters.gov.irs.credits.eitc(wftc.federal_eitc_snapshot_date) + # RCW 82.08.0206(2)(d) pins WFTC to the federal EITC rules as in + # effect on June 9, 2022; this snapshot date is a statutory literal. + frozen_eitc = parameters.gov.irs.credits.eitc("2022-06-09") frozen_investment_income_eligible = ( tax_unit("eitc_relevant_investment_income", period) <= frozen_eitc.phase_out.max_investment_income From a5869836e44cc7bf25cb7b75593990268193d629 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 19 May 2026 09:53:48 -0400 Subject: [PATCH 14/17] Break HoH circular dependency on filing_status The previous fix read filing_status to mask JOINT filers from the IRC 7703(b) treated-unmarried path, which created a CycleError: filing_status -> head_of_household_eligible -> filing_status. Replace the filing_status check with ~tax_unit_married, which uses the spouse-presence signal already available. In PolicyEngine: - JOINT filers have spouse in the same tax unit (married=true) - MFS filers have spouse in a separate tax unit (married=false) This still excludes the JOINT-with-is_separated=true case Pavel flagged in CRITICAL #1, without reading filing_status. --- .../tax_unit/head_of_household_eligible.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py index 0cd9a754347..7a6fd30e58b 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py @@ -9,9 +9,14 @@ class head_of_household_eligible(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/2#b" def formula(tax_unit, period, parameters): + # tax_unit_married is true iff a spouse is present in this tax unit. + # JOINT filers have the spouse in the same unit (married=true); + # MFS filers have the spouse in a separate unit (married=false). + # We use this signal to distinguish the IRC 7703(b) treated-unmarried + # path (MFS, married=false) from a stray is_separated=true on a + # JOINT filer (married=true), without reading filing_status and + # creating a circular dependency through filing_status -> hoh_eligible. married = tax_unit("tax_unit_married", period) - filing_status = tax_unit("filing_status", period) - joint = filing_status == filing_status.possible_values.JOINT person = tax_unit.members # IRC 7703(b) "considered unmarried" applies to the taxpayer (head or # spouse), not to dependents. A separated dependent must not trigger @@ -40,5 +45,9 @@ def formula(tax_unit, period, parameters): treated_unmarried_qualifies = tax_unit.sum(is_qualifying_child) > 0 surviving_spouse = tax_unit("surviving_spouse_eligible", period) unmarried_qualifies = has_qualifying_person & ~married & ~is_separated - separated_qualifies = treated_unmarried_qualifies & is_separated & ~joint + # ~married masks the JOINT-with-is_separated=true case: a JOINT + # filer cannot use IRC 7703(b)(1) because they have not filed a + # separate return. MFS filers (spouse in a different tax unit) have + # married=false and continue to qualify via this branch. + separated_qualifies = treated_unmarried_qualifies & is_separated & ~married return (unmarried_qualifies | separated_qualifies) & ~surviving_spouse From 7ad84c123b0cfa70229f46abc317527761348e0b Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Wed, 20 May 2026 17:35:11 -0400 Subject: [PATCH 15/17] Update WA WFTC childless partner fixture for frozen 2022 EITC parameters The wa_wftc_childless_filer_with_eitc partner-analytics fixture was authored against the pre-fix WFTC formula that used the *current* federal EITC parameters (including ARPA's expanded childless cap). This PR (#8243) freezes the WFTC's federal EITC reference to the rules as of 2022-06-09 per RCW 82.08.0206(2)(d), per Pavel's review. The 2022-frozen childless EITC max is materially smaller than the ARPA expansion that was previously bleeding into WFTC, so a $15K childless filer's WFTC drops from $342.59 to $223.25 in 2026. Only the childless edge case is affected; the four other fixtures (joint with child, etc.) still match because the with-child federal EITC parameters didn't change as dramatically. --- .../edge_cases/state/wa/working_families_tax_credit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/state/wa/working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/state/wa/working_families_tax_credit.yaml index dca662b6e02..276f3d01df5 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/state/wa/working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/state/wa/working_families_tax_credit.yaml @@ -120,7 +120,7 @@ output: tax_units: tax_unit: - wa_working_families_tax_credit: 342.59 + wa_working_families_tax_credit: 223.25 adjusted_gross_income: 15000.0 - name: wa_wftc_age_18_before_2029_expansion (wa) period: 2026 From bb79b90f3d10f67407af9152d95cd933855a9218 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 28 May 2026 09:17:35 -0400 Subject: [PATCH 16/17] Address Pavel's review: IN 2026 snapshot, HI 2021-2024 SLI, refs/comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL: - IN EITC TY 2026+ snapshot date: change literal from 2025-07-04 to 2026-01-01 per Indiana SEA 243 (2025), in both in_eitc.py and in_eitc_eligible.py. Update inline comments to cite SEA 243. (Parameterization was attempted in commit 16c7a000 and reverted because policyengine-core rejects string-valued parameters.) - HI subtractions list: restore student_loan_interest_ald to the 2021-01-01 entry so TY 2021-2024 retain the federal-equivalent SLI deduction. TY 2025+ continues to use the new HI-specific hi_student_loan_interest_subtraction. SHOULD ADDRESS: - IN static_conformity_in_effect.yaml: update description to reflect the two snapshot dates (2023-01-01 for TY 2023-2025, 2026-01-01 for TY 2026+ per SEA 243). - OH educator deduction: strengthen inline comment citing ORC § 5747.01(A)(31) and explicitly noting that this stub REPLACES (not supplements) the federal educator_expense to avoid double-counting. - WA WFTC age expansion: document (a) why no separate-filer guard is applied (state-only credit does not honor federal MFS bars) and (b) prior-year-age methodology approximation per ESSB 6346 Sec. 901(2)(a)(ii)(D). - UT EITC potential: correct misleading comment — TC-40A worksheet cites Box 16 (state wages); irs_employment_income (Box 1) is used as a proxy because PolicyEngine does not expose a state-wages variable. REFERENCE CLEANUPS: - IN earned_income parameters: restore #6-3-1-11 / #6-3.1-21 anchors on iga.in.gov links; add SEA 243 reference to static_conformity parameter. - SC § 12-6-50 links in states_using_federal_*_deductions: switch from scstatehouse.gov table-of-contents to law.justia.com direct section link. - CT exclusion: add Conn. Gen. Stat. § 12-701 reference explaining why CT is excluded from both federal-conformity lists. CHANGELOG: - Correct claim about parameterized snapshot-date parameters (those were reverted); document the IN 2026 snapshot correction and the HI 2021-2024 SLI retention explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) --- changelog.d/8243.fixed.md | 4 ++-- .../hi/tax/income/subtractions/subtractions.yaml | 1 + ...states_using_federal_itemized_deductions.yaml | 4 +++- .../states_using_federal_standard_deduction.yaml | 4 +++- .../income/credits/earned_income/decoupled.yaml | 4 ++-- .../static_conformity_in_effect.yaml | 12 +++++++----- .../credits/earned_income_credit/in_eitc.py | 7 +++---- .../earned_income_credit/in_eitc_eligible.py | 5 +++-- .../oh_educator_expense_deduction_person.py | 16 +++++++++------- .../ut/tax/income/credits/ut_eitc_potential.py | 8 +++++--- ...families_tax_credit_age_expansion_eligible.py | 11 +++++++++++ 11 files changed, 49 insertions(+), 27 deletions(-) diff --git a/changelog.d/8243.fixed.md b/changelog.d/8243.fixed.md index b14386fe056..4eee37657f4 100644 --- a/changelog.d/8243.fixed.md +++ b/changelog.d/8243.fixed.md @@ -1,9 +1,9 @@ Fixed federal/state conformity across EITC, deduction, ALD, HoH, and CDCC linkages. - Federal HoH: tighten IRC 7703(b) "considered unmarried" to MFS/HoH/single filers only; a JOINT filer with `is_separated=true` no longer qualifies. -- IN/WA EITC: replace hard-coded IRC snapshot dates with new parameters (`gov.states.in.tax.income.credits.earned_income.static_conformity_snapshot_date` and `gov.states.wa.tax.income.credits.working_families_tax_credit.federal_eitc_snapshot_date`). Replace the IN 2021-only branch with a `gov.states.in.tax.income.credits.earned_income.childless.in_effect` gate. +- IN EITC: advance the TY 2026+ static-conformity IRC snapshot to January 1, 2026 per Indiana SEA 243 (2025); the IN snapshot date and the WA WFTC 2022-06-09 snapshot remain Python literals because policyengine-core does not support string-valued parameters. Replace the IN 2021-only branch with a `gov.states.in.tax.income.credits.earned_income.childless.in_effect` gate. - CT dropped from `states_using_federal_itemized_deductions` and `states_using_federal_standard_deduction` (CT-1040 uses neither — only a personal exemption); SC drops out effective TY 2026 per H. 4216 / Act 110 of 2026. - DC: `dc_eitc` continues to model Law 23-149 ITIN inclusivity; the previously-added `dc_base_eitc` diagnostic variable is removed (unreferenced in this PR's scope). - OH educator expense: the federal `educator_expense` is no longer in OH's deductions list. ORC § 5747.01(A)(31) only allows the *excess* above the federal $300 cap, modeled via the new `oh_educator_expense_deduction_person` input variable. Filers without explicit input receive 0 for the OH-only excess; downstream microdata should populate it. The federal-cap interaction is no longer auto-applied to the OH deduction list — an under-implementation, documented as a known limitation. - GA: `ga_itemized_deductions_adjustment` ships as an explicit-input stub for now (other-state taxes and exempt-investment interest are not separately observed in the baseline microdata). -- HI: new student loan interest deduction subtree (`gov.states.hi.tax.income.subtractions.student_loan_interest/*`) and supporting `hi_modified_agi`, `hi_student_loan_interest_*` variables modelling pre-current IRC § 221. +- HI: new student loan interest deduction subtree (`gov.states.hi.tax.income.subtractions.student_loan_interest/*`) and supporting `hi_modified_agi`, `hi_student_loan_interest_*` variables modelling pre-current IRC § 221, effective TY 2025+. The pre-2025 HI subtractions list retains `student_loan_interest_ald` so years 2021-2024 continue to receive the federal-equivalent SLI deduction. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml index c8901b12f37..241ecdea1a0 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/subtractions/subtractions.yaml @@ -5,6 +5,7 @@ values: - taxable_social_security # Line 14 - hi_military_pay_exclusion # Line 15 - us_govt_interest # Line 18 + - student_loan_interest_ald # Line 18 2025-01-01: - taxable_pension_income # Line 13 - taxable_social_security # Line 14 diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml index 6174e9f6106..82d33c0f8d9 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_itemized_deductions.yaml @@ -9,9 +9,11 @@ metadata: - title: N.D.C.C. § 57-38-01.2(1)(b) - North Dakota federal-conformity itemized deductions href: https://www.legis.nd.gov/cencode/t57c38.pdf#page=2 - title: S.C. Code Ann. § 12-6-50 - South Carolina conformity statute (Internal Revenue Code sections specifically not adopted; amended by H. 4216 / Act 110 of 2026 to add § 63) - href: https://www.scstatehouse.gov/code/t12c006.php + href: https://law.justia.com/codes/south-carolina/title-12/chapter-6/section-12-6-50/ - title: South Carolina H. 4216 / Act 110 of 2026 - decouples from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + - title: Conn. Gen. Stat. § 12-701 - Connecticut adjusted gross income definition (CT has no itemized/standard deduction; excluded from both federal-conformity lists) + href: https://www.cga.ct.gov/current/pub/chap_229.htm#sec_12-701 - title: Utah Code § 59-10-1018(1)(a) - Utah itemized deductions href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: diff --git a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml index 7a295828171..1146b4334bb 100644 --- a/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml +++ b/policyengine_us/parameters/gov/states/household/states_using_federal_standard_deduction.yaml @@ -15,9 +15,11 @@ metadata: - title: NMSA § 7-2-7.5 - New Mexico federal standard deduction conformity href: https://nmonesource.com/nmos/nmsa/en/item/4359/index.do - title: S.C. Code Ann. § 12-6-50 - South Carolina conformity statute (Internal Revenue Code sections specifically not adopted; amended by H. 4216 / Act 110 of 2026 to add § 63) - href: https://www.scstatehouse.gov/code/t12c006.php + href: https://law.justia.com/codes/south-carolina/title-12/chapter-6/section-12-6-50/ - title: South Carolina H. 4216 / Act 110 of 2026 - decouples from federal itemized and standard deductions effective tax year 2026 href: https://www.scstatehouse.gov/sess126_2025-2026/bills/4216.htm + - title: Conn. Gen. Stat. § 12-701 - Connecticut adjusted gross income definition (CT has no itemized/standard deduction; excluded from both federal-conformity lists) + href: https://www.cga.ct.gov/current/pub/chap_229.htm#sec_12-701 - title: Utah Code § 59-10-1018(1)(a) - Utah federal standard deduction conformity href: https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html values: diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml index f635db2cfd9..a85f974da2d 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/decoupled.yaml @@ -5,9 +5,9 @@ metadata: label: Whether IN EITC is decoupled from federal EITC reference: - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code (fixes the federal-law reference date for the decoupled branch) - href: https://iga.in.gov/laws/2024/ic/titles/6 + href: https://iga.in.gov/laws/2024/ic/titles/6#6-3-1-11 - title: IC 6-3.1-21 - Indiana Earned Income Tax Credit chapter - href: https://iga.in.gov/laws/2024/ic/titles/6 + href: https://iga.in.gov/laws/2024/ic/titles/6#6-3.1-21 - title: 2023 IT-40 Individual Income Tax Booklet (Indiana EITC discussion) href: https://forms.in.gov/Download.aspx?id=15792#page=9 - title: 2025 IT-40 Income Tax Form Instruction Booklet diff --git a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml index f6893525913..7c88006d9fc 100644 --- a/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml +++ b/policyengine_us/parameters/gov/states/in/tax/income/credits/earned_income/static_conformity_in_effect.yaml @@ -1,4 +1,4 @@ -description: Indiana applies its static-conformity-to-2023-Internal-Revenue-Code branch for the Earned Income Tax Credit if this indicator is true. +description: Indiana applies its static-conformity-to-Internal-Revenue-Code branch for the Earned Income Tax Credit if this indicator is true. The applicable snapshot is January 1, 2023 for tax years 2023-2025, and January 1, 2026 for tax years 2026 onward per Indiana SEA 243 of 2025. values: 2010-01-01: false @@ -10,8 +10,10 @@ metadata: label: Indiana EITC static conformity in effect reference: - title: IC 6-3-1-11 - Indiana definition of Internal Revenue Code, fixing the federal-law reference date - href: https://iga.in.gov/laws/2024/ic/titles/6 + href: https://iga.in.gov/laws/2024/ic/titles/6#6-3-1-11 - title: IC 6-3.1-21 - Indiana Earned Income Tax Credit chapter - href: https://iga.in.gov/laws/2024/ic/titles/6 - - title: 2023 IT-40 Individual Income Tax Booklet, page 9 (Indiana EITC discussion) - href: https://forms.in.gov/Download.aspx?id=15792 + href: https://iga.in.gov/laws/2024/ic/titles/6#6-3.1-21 + - title: Indiana SEA 243 (2025) - Advances IRC conformity to January 1, 2026 + href: https://iga.in.gov/legislative/2025/bills/senate/243 + - title: 2023 IT-40 Individual Income Tax Booklet (Indiana EITC discussion) + href: https://forms.in.gov/Download.aspx?id=15792#page=9 diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py index 2d3eb8e1dca..5374d0af264 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc.py @@ -22,13 +22,12 @@ def formula(tax_unit, period, parameters): if ip.earned_income.static_conformity_in_effect: # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): # - TY 2023 through 2025: IRC as in effect on January 1, 2023. - # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243 - # of 2026 (signed by Governor Braun, advancing conformity to - # include sections of H.R. 1 / Public Law 119-21). + # - TY 2026 onward: IRC as in effect on January 1, 2026, per + # Indiana SEA 243 (2025). # The snapshot dates are statutory literals; policyengine-core # parameters do not support date-valued types, so they appear # here rather than in the parameter tree. - snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + snapshot_date = "2026-01-01" if period.start.year >= 2026 else "2023-01-01" frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py index 1f5724526f0..5df47ec3ac6 100644 --- a/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/in/tax/income/credits/earned_income_credit/in_eitc_eligible.py @@ -23,11 +23,12 @@ def formula(tax_unit, period, parameters): if p.credits.earned_income.static_conformity_in_effect: # IC 6-3-1-11 (Indiana's IRC definition for IC 6-3.1-21): # - TY 2023 through 2025: IRC as in effect on January 1, 2023. - # - TY 2026 onward: IRC as in effect on July 4, 2025, per SB 243. + # - TY 2026 onward: IRC as in effect on January 1, 2026, per + # Indiana SEA 243 (2025). # The snapshot dates are statutory literals; policyengine-core # parameters do not support date-valued types, so they appear # here rather than in the parameter tree. - snapshot_date = "2025-07-04" if period.start.year >= 2026 else "2023-01-01" + snapshot_date = "2026-01-01" if period.start.year >= 2026 else "2023-01-01" frozen_eitc = parameters.gov.irs.credits.eitc(snapshot_date) child_count = tax_unit("eitc_child_count", period) demographic_eligible = calculate_eitc_demographic_eligibility( diff --git a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py index 88a3c1c74c7..2d5ac005513 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/deductions/oh_educator_expense_deduction_person.py @@ -9,13 +9,15 @@ class oh_educator_expense_deduction_person(Variable): definition_period = YEAR default_value = 0 defined_for = StateCode.OH - # Ohio-specific educator deduction allowed in excess of the federal - # educator deduction for a qualifying Ohio educator. This remains an - # explicit input because the baseline data do not identify Ohio - # licensure/teaching status or expenses above the federal deduction. - # Replacing the federal educator_expense in oh/.../deductions.yaml with - # this Ohio-specific stub means filers without explicit input receive 0 - # for Ohio purposes; downstream microdata should populate it. + # ORC § 5747.01(A)(31): Ohio allows a deduction ONLY for educator + # expenses in EXCESS of the federal IRC § 62(a)(2)(D) cap (currently + # $300). The federal-cap portion is already deducted via federal AGI; + # adding the federal `educator_expense` here would double-count. This + # stub variable therefore replaces (not supplements) the federal + # educator deduction in oh/.../deductions.yaml. It remains an explicit + # input because the baseline data do not identify Ohio licensure or + # the excess-over-federal amount; filers without microdata input + # receive 0, which matches the federal cap behavior. reference = ( "https://codes.ohio.gov/ohio-revised-code/section-5747.01#A_31", # ORC 5747.01(A)(31): Ohio educator deduction "https://dam.assets.ohio.gov/image/upload/v1767095693/tax.ohio.gov/forms/ohio_individual/individual/2025/it1040-booklet.pdf#page=25", # 2025 IT-1040 booklet, Ohio educator expense deduction diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py index c2893b16310..8fb68e71fdf 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_eitc_potential.py @@ -12,9 +12,11 @@ class ut_eitc_potential(Variable): reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1044.html?v=C59-10-S1044_2022050420220504" def formula(tax_unit, period, parameters): - # Utah Code § 59-10-1044 caps the credit at federal W-2 wages - # (Box 1) per TC-40A instructions, explicitly excluding - # self-employment earnings. + # Utah Code § 59-10-1044 caps the credit at W-2 state wages + # (Box 16 per TC-40A worksheet), explicitly excluding self-employment + # earnings. W-2 Box 1 (irs_employment_income) is used as a proxy + # because PolicyEngine does not expose a state-wages variable; for + # most filers Box 16 and Box 1 are identical. p = parameters(period).gov.states.ut.tax.income.credits.earned_income federal_eitc = tax_unit("eitc", period) wages = add(tax_unit, period, ["irs_employment_income"]) diff --git a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py index 6af838b7034..392afdc12cb 100644 --- a/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py +++ b/policyengine_us/variables/gov/states/wa/tax/income/credits/wa_working_families_tax_credit_age_expansion_eligible.py @@ -15,6 +15,17 @@ class wa_working_families_tax_credit_age_expansion_eligible(Variable): def formula(tax_unit, period, parameters): # ESSB 6346 Sec. 901(2)(a)(ii)(D): individuals who would otherwise # qualify for EITC except for age can qualify if at least age 18. + # + # Methodology note (age): Sec. 901(2)(a)(ii)(D) refers to age as of the + # prior tax year. PolicyEngine evaluates a single period, so this + # formula uses current-period age_head / age_spouse as an approximation. + # The off-by-one-year edge case (filer turning min_age during the tax + # year) is not modeled. + # + # No separate-filer (MFS) guard is applied here because the WFTC is a + # state-only credit that, by design, does not honor the federal MFS + # bar; the main wa_working_families_tax_credit variable's state-only + # path treats `separate` filers as eligible. p = parameters( period ).gov.states.wa.tax.income.credits.working_families_tax_credit.age_expansion From 15c4fd16ef29112939e895a8688d6dadc1910b45 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 28 May 2026 09:25:51 -0400 Subject: [PATCH 17/17] Update IN EITC TY 2026 test for corrected 2026-01-01 snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was authored to lock in the (incorrect) 2025-07-04 snapshot behavior. With Pavel's corrected 2026-01-01 snapshot (per Indiana SEA 243), the computed credit shifts from 432.80 to 442.70 — this reflects the actual difference between mid-2025 and January-2026 EITC parameter values, not floating-point noise. Update the test name, comment, and expected value to reflect the corrected snapshot, and tighten the margin to 0.01 (matching the other in_eitc tests). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../gov/states/in/tax/income/in_eitc.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml index 15f778bc3f3..b0ed5db3407 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/in/tax/income/in_eitc.yaml @@ -47,9 +47,9 @@ output: in_eitc: 340 -- name: Indiana EITC TY 2026 uses the SB 243 advanced 2025-07-04 IRC snapshot +- name: Indiana EITC TY 2026 uses the SEA 243 advanced January 1, 2026 IRC snapshot period: 2026 - absolute_error_margin: 1 + absolute_error_margin: 0.01 input: people: head: @@ -71,8 +71,9 @@ members: [head, child] state_code: IN output: - # TY 2026 onwards Indiana SB 243 advances the IRC reference to 2025-07-04. - # The static_conformity_in_effect parameter is true; the year-conditional - # snapshot logic picks "2025-07-04" instead of "2023-01-01". A positive - # credit confirms the post-SB 243 frozen-IRC code path is exercised. - in_eitc: 432.80 + # TY 2026 onwards Indiana SEA 243 (2025) advances the IRC reference to + # January 1, 2026. The static_conformity_in_effect parameter is true; + # the year-conditional snapshot logic picks "2026-01-01" instead of + # "2023-01-01". A positive credit confirms the post-SEA 243 frozen-IRC + # code path is exercised. + in_eitc: 442.70