From 1f0cd1b0f918fc3e51441ae08711e65a9ca8d181 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 8 Oct 2026 11:03:59 -0400 Subject: [PATCH 01/42] Make head_is_dependent_elsewhere consumers independent of the head label The federal standard deduction and 17 state formulas read only whether the tax-unit head is claimed elsewhere, so a joint return where one spouse can be claimed was taxed differently depending on which spouse was labelled head. Each formula now applies its jurisdiction's rule for that case (either spouse, single filers only, per spouse, or only when every filer is claimed), using two new label-independent helpers. Adds swap-pair YAML cases and a Hypothesis property test. Co-Authored-By: Claude Opus 5.5 --- .../head-dependent-elsewhere-swap.fixed.md | 1 + .../itemized/threshold/dependent.yaml | 2 +- .../exemptions/dependent_on_other_return.yaml | 17 +- .../standard/dependent_elsewhere.yaml | 4 +- ...t_elsewhere_head_spouse_swap_invariance.py | 355 ++++++++++++++++++ .../standard/basic_standard_deduction.yaml | 56 +++ .../hi/tax/income/credits/act_163_sunset.yaml | 4 - .../food_excise/hi_food_excise_credit.yaml | 89 ++++- .../tax/income/credits/hi_act_115_rebate.yaml | 90 +++++ ...w_income_household_renters_tax_credit.yaml | 64 ++++ ...low_income_household_renters_eligible.yaml | 91 ++++- .../tax/income/deductions/hi_deductions.yaml | 134 ++++++- .../hi_salt_deduction_integration.yaml | 6 +- ...me_sales_tax_fairness_credit_eligible.yaml | 85 ++++- .../tax/income/exemptions/mi_exemptions.yaml | 148 +++++++- .../income/credits/wftc/mo_wftc_eligible.yaml | 56 +++ .../income/credits/cdcc/nm_cdcc_eligible.yaml | 105 +++++- .../credits/nm_medical_expense_credit.yaml | 92 ++++- ...ction_for_certain_dependents_eligible.yaml | 86 ++++- .../2021_income/nm_2021_income_rebate.yaml | 90 ++++- .../nm_additional_2021_income_rebate.yaml | 89 ++++- .../nm_supplemental_2021_income_rebate.yaml | 89 ++++- .../deductions/ny_standard_deduction.yaml | 94 +++++ .../standard/or_standard_deduction.yaml | 60 ++- .../va_low_income_tax_credit_eligible.yaml | 56 ++- ...head_or_spouse_is_dependent_elsewhere.yaml | 114 ++++++ ..._spouse_count_not_dependent_elsewhere.yaml | 114 ++++++ .../basic_standard_deduction.py | 14 +- .../food_excise/hi_food_excise_credit.py | 25 +- .../hi_food_excise_exemption_amount.py | 18 +- .../tax/income/credits/hi_act_115_rebate.py | 16 +- ...low_income_household_renters_tax_credit.py | 30 +- ...r_low_income_household_renters_eligible.py | 16 +- .../hi/tax/income/deductions/hi_deductions.py | 27 +- .../me_sales_tax_fairness_credit_eligible.py | 16 +- .../mi/tax/income/exemptions/mi_exemptions.py | 29 +- .../mo/tax/income/credits/mo_wftc_eligible.py | 18 +- .../income/credits/cdcc/nm_cdcc_eligible.py | 20 +- .../credits/nm_medical_expense_credit.py | 16 +- ...duction_for_certain_dependents_eligible.py | 16 +- .../2021_rebate/nm_2021_income_rebate.py | 17 +- .../nm_additional_2021_income_rebate.py | 17 +- .../nm_supplemental_2021_income_rebate.py | 17 +- .../deductions/ny_standard_deduction.py | 20 +- .../deductions/or_standard_deduction.py | 11 +- .../va_low_income_tax_credit_eligible.py | 17 +- .../head_or_spouse_is_dependent_elsewhere.py | 21 ++ ...ad_spouse_count_not_dependent_elsewhere.py | 20 + 48 files changed, 2413 insertions(+), 179 deletions(-) create mode 100644 changelog.d/head-dependent-elsewhere-swap.fixed.md create mode 100644 policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml create mode 100644 policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py create mode 100644 policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py diff --git a/changelog.d/head-dependent-elsewhere-swap.fixed.md b/changelog.d/head-dependent-elsewhere-swap.fixed.md new file mode 100644 index 00000000000..4b4d878f15f --- /dev/null +++ b/changelog.d/head-dependent-elsewhere-swap.fixed.md @@ -0,0 +1 @@ +The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers, and Hawaii now limits the standard deduction of a filer who can be claimed as a dependent. diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml index 0a894e4a148..3ff2ac9a451 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml @@ -1,4 +1,4 @@ -description: Hawaii allows for itemized deductions for dependent filers with deductions above this amount. +description: Hawaii limits the standard deduction of a filer who can be claimed as a dependent to the greater of this amount or earned income, and lets such a filer itemize deductions above that limit. values: 2018-01-01: 500 metadata: diff --git a/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml b/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml index 44fbabc57c8..46ca0dabbff 100644 --- a/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml +++ b/policyengine_us/parameters/gov/states/mi/tax/income/exemptions/dependent_on_other_return.yaml @@ -1,21 +1,10 @@ -description: Michigan provides this exemption amount for a taxpayer who can be claimed as a dependent on someone else’s return. -SINGLE: - 2021-01-01: 1_500 -JOINT: - 2021-01-01: 3_000 -HEAD_OF_HOUSEHOLD: - 2021-01-01: 1_500 -SURVIVING_SPOUSE: - 2021-01-01: 1_500 -SEPARATE: +description: Michigan provides this exemption amount to each individual who can be claimed as a dependent on another taxpayer's return, in place of that individual's personal exemption. +values: 2021-01-01: 1_500 metadata: - propagate_metadata_to_children: true unit: currency-USD period: year - breakdown: - - filing_status - label: Michigan dependent on other return exemption amount + label: Michigan exemption amount per individual claimable as a dependent elsewhere reference: - title: Michigan Legal Code Section 206.30 - (4) href: https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30 diff --git a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml index 4744cadf89b..efe6e74704b 100644 --- a/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml +++ b/policyengine_us/parameters/gov/states/ny/tax/income/deductions/standard/dependent_elsewhere.yaml @@ -1,10 +1,10 @@ -description: New York standard deduction for filers who can be claimed as a dependent elsewhere. +description: New York provides this standard deduction to single filers who can be claimed as a dependent on another taxpayer's federal return. values: 2020-01-01: 3_100 metadata: unit: currency-USD period: year - label: New York standard deduction for dependent filers + label: New York standard deduction for single dependent filers reference: - title: 2021 NY Form IT-201 Instructions Line 34 href: https://www.tax.ny.gov/pdf/2021/inc/it201i_2021.pdf#page=19 diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py new file mode 100644 index 00000000000..8c3e6fc2c48 --- /dev/null +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -0,0 +1,355 @@ +"""Rules for a filer claimed as a dependent elsewhere do not depend on which +spouse is labelled head. + +A married couple may list either spouse first, so exchanging the two adults' +`is_tax_unit_head` and `is_tax_unit_spouse` labels, with every other input +kept, must not change their taxes. On main, the federal standard deduction and +seventeen state formulas read only `head_is_dependent_elsewhere`, so a joint +return where one spouse can be claimed on another return (a joint return filed +only to recover withholding can meet the federal exception, IRS Publication +501) was taxed differently depending on which spouse was labelled head. Each +formula now applies its jurisdiction's rule for that case: + +- Either spouse limits the return (the IRC 63(c)(5) worksheet): the federal + standard deduction, Oregon's and Hawaii's standard deductions, and + Virginia's credit for low-income individuals. +- Only single filers: New York's dependent standard deduction. +- Each spouse separately: Michigan's exemptions and Hawaii's food/excise, + renters and Act 115 credits count only the filers who cannot be claimed. +- Only when every filer is a dependent: New Mexico's rebates, credits and + dependents deduction, and Maine's sales tax fairness credit. +- Never on a joint return: Missouri's working family credit, since such a + couple files as married filing combined. + +Hypothesis draws batches of married couples, with and without dependents, in +the eight states with such a rule and one without, for 2021-2026, with either, +both or neither spouse claimed elsewhere. A seeded population and crafted +cases add breadth in every year, and two seeded couples plus the review case +in every state guard 2026. Each batch is one vectorized simulation. For each couple: + +1. Swap invariance: federal income tax, state income tax before and after + refundable credits, and each of the eighteen formulas are the same to the + cent (or the same boolean) under either labelling. +2. Monotonicity: marking another filer as claimed elsewhere never raises any + of the eighteen amounts or turns an eligibility on. +3. The helper identities: `head_or_spouse_is_dependent_elsewhere` equals + `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, and + `head_spouse_count_not_dependent_elsewhere` plus the claimed filers equals + the number of filers. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.model_api import STATES + +TOLERANCE = 0.01 # dollars: the same to the cent +YEARS = [2021, 2022, 2023, 2024, 2025, 2026] +AFFECTED = ["HI", "ME", "MI", "MO", "NM", "NY", "OR", "VA", "CA"] +ALL_STATES = [s for s in STATES if s not in ("PR", "VI")] +TAX_UNIT_OUTPUTS = [ + "income_tax", + "state_income_tax", + "state_income_tax_before_refundable_credits", +] +# The formulas that read whether a filer is claimed elsewhere. +CONSUMERS = [ + "basic_standard_deduction", + "ny_standard_deduction", + "mi_exemptions", + "or_standard_deduction", + "va_low_income_tax_credit_eligible", + "mo_wftc_eligible", + "nm_2021_income_rebate", + "nm_additional_2021_income_rebate", + "nm_supplemental_2021_income_rebate", + "nm_deduction_for_certain_dependents_eligible", + "nm_medical_expense_credit", + "nm_cdcc_eligible", + "me_sales_tax_fairness_credit_eligible", + "hi_deductions", + "hi_act_115_rebate", + "hi_food_excise_credit", + "hi_tax_credit_for_low_income_household_renters_eligible", + "hi_tax_credit_for_low_income_household_renters", +] +CLAIM_PATTERNS = [(True, False), (False, True), (True, True), (False, False)] + + +def money(high): + return st.one_of(st.just(0.0), st.integers(1, high).map(float)) + + +@st.composite +def adults(draw): + # Low incomes reach the credits' limits; ages reach the 65+ rules. + return { + "age": draw(st.integers(18, 90)), + "is_blind": draw(st.booleans()), + "is_disabled": draw(st.booleans()), + "employment_income": draw(money(60_000)), + "self_employment_income": draw(money(20_000)), + "taxable_interest_income": draw(money(3_000)), + "social_security_retirement": draw(money(25_000)), + "real_estate_taxes": draw(money(6_000)), + "rent": draw(money(18_000)), + "charitable_cash_donations": draw(money(6_000)), + "other_medical_expenses": draw(money(40_000)), + } + + +@st.composite +def dependents(draw): + age = draw(st.integers(0, 23)) + return { + "age": age, + "is_full_time_student": age >= 18, + "employment_income": draw(money(10_000)), + "pre_subsidy_childcare_expenses": draw(money(8_000)), + } + + +@st.composite +def couples(draw): + return { + "state": draw(st.sampled_from(AFFECTED)), + "adults": [draw(adults()), draw(adults())], + "dependents": draw(st.lists(dependents(), max_size=2)), + "claimed": draw(st.sampled_from(CLAIM_PATTERNS)), + } + + +SEED = 20261008 + + +def _seeded_adult(rng): + def some(high, p): + return float(round(rng.uniform(1, high))) if rng.random() < p else 0.0 + + age = int(rng.integers(18, 91)) + return { + "age": age, + "is_blind": bool(rng.random() < 0.1), + "is_disabled": bool(rng.random() < 0.1), + "employment_income": some(60_000, 0.7), + "self_employment_income": some(20_000, 0.15), + "taxable_interest_income": some(3_000, 0.3), + "social_security_retirement": some(25_000, 0.5 if age >= 62 else 0.03), + "real_estate_taxes": some(6_000, 0.3), + "rent": some(18_000, 0.5), + "charitable_cash_donations": some(6_000, 0.3), + "other_medical_expenses": some(40_000, 0.15), + } + + +def _seeded_couples(states, per_state): + rng = np.random.default_rng(SEED) + return [ + { + "state": state, + "adults": [_seeded_adult(rng), _seeded_adult(rng)], + "dependents": [ + {"age": int(rng.integers(0, 18))} + for _ in range(int(rng.choice([0, 0, 1, 2]))) + ], + "claimed": CLAIM_PATTERNS[int(rng.integers(0, 3))], + } + for state in states + for _ in range(per_state) + ] + + +def _crafted_cases(states, review_case_only=False): + # The review case (married adults aged 20 earning $8,000 each, one + # claimed by a parent), with and without a child, and older couples who + # reach the 65+ and renter rules. + young = {"age": 20, "employment_income": 8_000.0, "rent": 6_000.0} + old = { + "age": 70, + "social_security_retirement": 12_000.0, + "rent": 9_000.0, + "other_medical_expenses": 30_000.0, + } + cases = [] + for state in states: + for claimed in CLAIM_PATTERNS[:3]: + cases += [ + { + "state": state, + "adults": [young, young], + "dependents": [], + "claimed": claimed, + }, + ] + if review_case_only: + continue + cases += [ + { + "state": state, + "adults": [young, {"age": 22, "employment_income": 3_000.0}], + "dependents": [{"age": 2}], + "claimed": claimed, + }, + { + "state": state, + "adults": [old, {**old, "age": 66}], + "dependents": [], + "claimed": claimed, + }, + ] + return cases + + +def _situation(units, year, variants): + # Each unit appears once per variant: (head is the first adult, first + # adult claimed, second adult claimed). People keep their order and every + # other input in every copy. + people = {} + groups = { + "tax_units": {}, + "spm_units": {}, + "families": {}, + "marital_units": {}, + "households": {}, + } + for i, unit in enumerate(units): + for copy, (first_is_head, claim_a, claim_b) in enumerate(variants(unit)): + a, b = f"a_{i}_{copy}", f"b_{i}_{copy}" + for name, amounts, is_head, claimed in ( + (a, unit["adults"][0], first_is_head, claim_a), + (b, unit["adults"][1], not first_is_head, claim_b), + ): + people[name] = { + **amounts, + "is_tax_unit_head": is_head, + "is_tax_unit_spouse": not is_head, + "is_tax_unit_dependent": False, + "claimed_as_dependent_on_another_return": claimed, + } + members = [a, b] + groups["marital_units"][f"couple_{i}_{copy}"] = {"members": [a, b]} + for j, amounts in enumerate(unit["dependents"]): + child = f"dependent_{i}_{copy}_{j}" + people[child] = { + **amounts, + "is_tax_unit_head": False, + "is_tax_unit_spouse": False, + "is_tax_unit_dependent": True, + } + members.append(child) + groups["marital_units"][f"single_{i}_{copy}_{j}"] = {"members": [child]} + for group, prefix in ( + ("tax_units", "tax_unit"), + ("spm_units", "spm_unit"), + ("families", "family"), + ): + groups[group][f"{prefix}_{i}_{copy}"] = {"members": members} + groups["households"][f"household_{i}_{copy}"] = { + "members": members, + "state_code": {year: unit["state"]}, + } + people = { + name: {k: {year: v} for k, v in values.items()} + for name, values in people.items() + } + return {"people": people, **groups} + + +def _swap_variants(unit): + claim_a, claim_b = unit["claimed"] + return [(True, claim_a, claim_b), (False, claim_a, claim_b)] + + +def _calc(sim, name, year): + return np.asarray(sim.calculate(name, year), dtype=float) + + +def _check_swap(units, year, outputs): + sim = Simulation(situation=_situation(units, year, _swap_variants)) + states = np.array([u["state"] for u in units]) + for name in outputs: + values = _calc(sim, name, year) + assert np.isfinite(values).all(), f"{name} in {year} is not finite" + drawn, swapped = values[0::2], values[1::2] + differs = ~(np.abs(swapped - drawn) <= TOLERANCE) + assert not differs.any(), ( + f"{name} in {year} changes when the head and spouse labels are " + f"exchanged: " + + ", ".join( + f"{states[i]} claimed={units[i]['claimed']} " + f"{drawn[i]:.2f} -> {swapped[i]:.2f}" + for i in np.flatnonzero(differs)[:10] + ) + ) + # 3. The helper identities. + either = _calc(sim, "head_or_spouse_is_dependent_elsewhere", year) + head = _calc(sim, "head_is_dependent_elsewhere", year) + spouse = _calc(sim, "spouse_is_dependent_elsewhere", year) + np.testing.assert_array_equal(either, np.maximum(head, spouse)) + independent = _calc(sim, "head_spouse_count_not_dependent_elsewhere", year) + np.testing.assert_array_equal(independent + head + spouse, 2) + + +def _monotone_variants(unit): + # Nobody, either adult, then both adults claimed; labels fixed. + return [ + (True, False, False), + (True, True, False), + (True, False, True), + (True, True, True), + ] + + +def _check_monotone(units, year): + sim = Simulation(situation=_situation(units, year, _monotone_variants)) + states = np.array([u["state"] for u in units]) + for name in CONSUMERS: + values = _calc(sim, name, year).reshape(-1, 4) + nobody, first, second, both = values.T + for more, fewer, label in ( + (first, nobody, "first adult"), + (second, nobody, "second adult"), + (both, first, "both after first"), + (both, second, "both after second"), + ): + rises = more > fewer + TOLERANCE + assert not rises.any(), ( + f"{name} in {year} rises when the {label} is claimed " + f"elsewhere: " + + ", ".join( + f"{states[i]} {fewer[i]:.2f} -> {more[i]:.2f}" + for i in np.flatnonzero(rises)[:10] + ) + ) + + +# A batch's cost is mostly per-variable overhead, so each example is a large +# batch and there are few examples. +@settings( + max_examples=6, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.sampled_from(YEARS), st.lists(couples(), min_size=8, max_size=20)) +def test_claimed_spouse_does_not_depend_on_head_label(year, units): + _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) + + +@pytest.mark.parametrize("year", YEARS) +def test_seeded_population(year): + units = _seeded_couples(AFFECTED, per_state=4) + _crafted_cases(AFFECTED) + _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) + _check_monotone(units, year) + + +def test_every_state_with_a_claimed_spouse(): + # Other states' rules for a claimed filer read both spouses already; + # this guards their state income tax in one year. + units = _seeded_couples(ALL_STATES, per_state=2) + _crafted_cases( + ALL_STATES, review_case_only=True + ) + _check_swap(units, 2026, TAX_UNIT_OUTPUTS) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml index 483f073599a..b9a821293a5 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml @@ -207,3 +207,59 @@ members: [head] output: basic_standard_deduction: 3_650 + +- name: 2025 joint return uses the dependent worksheet when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: CA + output: + # Joint earned income $16,000 + $450 = $16,450, below the $31,500 joint amount. + basic_standard_deduction: 16_450 + +- name: 2025 joint return uses the dependent worksheet when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: CA + output: + # Joint earned income $16,000 + $450 = $16,450, below the $31,500 joint amount. + basic_standard_deduction: 16_450 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml index 4116a0a6dac..778252cac05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/act_163_sunset.yaml @@ -34,7 +34,6 @@ adjusted_gross_income: 10_000 filing_status: JOINT exemptions_count: 2 - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -58,7 +57,6 @@ adjusted_gross_income: 4_000 filing_status: JOINT exemptions_count: 2 - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -80,7 +78,6 @@ adjusted_gross_income: 10_000 filing_status: SINGLE exemptions_count: 1 - head_is_dependent_elsewhere: false households: household: members: [person1] @@ -102,7 +99,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 1 - head_is_dependent_elsewhere: false households: household: members: [person1] diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml index 9ad568e7fee..e340975fe5c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml @@ -20,7 +20,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -50,7 +49,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -71,6 +69,7 @@ person2: age: 39 is_child: false + claimed_as_dependent_on_another_return: true spm_units: spm_unit: members: [person1, person2] @@ -81,7 +80,6 @@ adjusted_gross_income: 4_000 filing_status: SINGLE exemptions_count: 2 - head_is_dependent_elsewhere: true households: household: members: [person1, person2] @@ -111,7 +109,6 @@ adjusted_gross_income: 35_000 filing_status: SINGLE exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -141,7 +138,6 @@ adjusted_gross_income: 35_000 filing_status: JOINT exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -171,7 +167,6 @@ adjusted_gross_income: 55_000 filing_status: JOINT exemptions_count: 3 - head_is_dependent_elsewhere: false households: household: members: [person1, person2, person3] @@ -197,7 +192,6 @@ members: [person1, person2] adjusted_gross_income: 6_000 filing_status: SINGLE - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -223,7 +217,6 @@ members: [person1, person2] adjusted_gross_income: 6_000 filing_status: SINGLE - head_is_dependent_elsewhere: false households: household: members: [person1, person2] @@ -258,3 +251,83 @@ exemptions_count: 2 hi_food_excise_credit: 440 hi_income_tax: -440 + +- name: 2027 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2027 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # Form N-311 counts only the spouse who cannot be claimed: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2027 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2027 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # Form N-311 counts only the spouse who cannot be claimed: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2027 joint return where both spouses can be claimed + period: 2027 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_food_excise_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml index 13ebfd8d94a..7c4030e8e4c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml @@ -162,3 +162,93 @@ state_code: HI output: hi_act_115_rebate: 0 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed has a qualified exemption; a joint + # return on which a spouse can be claimed claims no dependents. 1 x $300. + hi_act_115_rebate: 300 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed has a qualified exemption; a joint + # return on which a spouse can be claimed claims no dependents. 1 x $300. + hi_act_115_rebate: 300 + +- name: Joint return where both spouses can be claimed + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 5_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_act_115_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml index cddb436375d..2dd4cd51e42 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml @@ -37,3 +37,67 @@ hi_tax_credit_for_low_income_household_renters_eligible: false output: hi_tax_credit_for_low_income_household_renters: 0 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Schedule X lines 8-10: the spouse who cannot be claimed, plus their age + # 65 exemption. 2 x $50. + hi_tax_credit_for_low_income_household_renters: 100 + +- name: Joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Schedule X lines 8-10: the spouse who cannot be claimed, plus their age + # 65 exemption. 2 x $50. + hi_tax_credit_for_low_income_household_renters: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml index 4916c7667b8..629efc27e05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.yaml @@ -2,7 +2,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 29_000 rent: 1_100 output: @@ -12,7 +12,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 31_000 rent: 1_100 output: @@ -22,7 +22,7 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 29_000 rent: 900 output: @@ -32,8 +32,91 @@ period: 2021 input: state_code: HI - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false hi_agi: 32_000 rent: 900 output: hi_tax_credit_for_low_income_household_renters_eligible: false + +- name: Joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # HRS 235-55.7(b): the spouse who cannot be claimed may claim the credit. + hi_tax_credit_for_low_income_household_renters_eligible: true + +- name: Joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # HRS 235-55.7(b): the spouse who cannot be claimed may claim the credit. + hi_tax_credit_for_low_income_household_renters_eligible: true + +- name: Joint return where both spouses can be claimed + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + rent: 1_100 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_agi: 29_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml index fee63c3b063..377d8edd769 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml @@ -7,7 +7,7 @@ hi_agi: 1_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 5_000 @@ -20,7 +20,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_400 @@ -33,7 +33,7 @@ hi_agi: 1_000 hi_total_itemized_deductions: 1_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_400 @@ -46,7 +46,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 1_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 2_200 @@ -59,7 +59,7 @@ hi_agi: 1_000_000 hi_total_itemized_deductions: 5_000 hi_reduced_itemized_deductions: 4_500 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: hi_deductions: 4_500 @@ -71,3 +71,127 @@ employment_income: 62_000 output: hi_deductions: 8_800 + +- name: 2025 joint standard deduction limited when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 0 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($3,000), up to $8,800. + hi_deductions: 3_000 + +- name: 2025 joint standard deduction limited when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 0 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($3,000), up to $8,800. + hi_deductions: 3_000 + +- name: 2025 joint filers with a claimable spouse itemize above the limited standard deduction (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 4_000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # Itemized deductions of $4,000 exceed the limited $3,000 standard deduction. + hi_deductions: 4_000 + +- name: 2025 joint filers with a claimable spouse itemize above the limited standard deduction (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + hi_itemized_deductions: 4_000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # Itemized deductions of $4,000 exceed the limited $3,000 standard deduction. + hi_deductions: 4_000 + +- name: 2025 single dependent filer with little earned income gets the $500 minimum + period: 2025 + input: + state_code: HI + filing_status: SINGLE + claimed_as_dependent_on_another_return: true + tax_unit_earned_income: 300 + hi_itemized_deductions: 0 + output: + # HRS 235-2.4(a)(3): greater of $500 or earned income ($300). + hi_deductions: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml index 74db648797c..7efb22ba1f0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/itemized/hi_salt_deduction_integration.yaml @@ -4,7 +4,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 99_999 hi_agi: 99_999 state_withheld_income_tax: 6_000 @@ -32,7 +32,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 100_000 hi_agi: 100_000 state_withheld_income_tax: 6_000 @@ -60,7 +60,7 @@ state_code: HI filing_status: SINGLE age: 40 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false adjusted_gross_income: 99_999 hi_agi: 99_999 state_withheld_income_tax: 2_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml index b3afa273542..b81ac606775 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/me/tax/income/credits/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.yaml @@ -2,7 +2,7 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: JOINT output: me_sales_tax_fairness_credit_eligible: false @@ -11,7 +11,7 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false filing_status: SINGLE output: me_sales_tax_fairness_credit_eligible: true @@ -20,7 +20,86 @@ period: 2022 input: state_code: ME - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: SEPARATE output: me_sales_tax_fairness_credit_eligible: false + +- name: Joint return where only one spouse can be claimed as a dependent (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: ME + output: + # 36 MRSA 5213-A(6)(C) excludes the claimable individual; the other spouse + # still qualifies for the joint credit. + me_sales_tax_fairness_credit_eligible: true + +- name: Joint return where only one spouse can be claimed as a dependent (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: ME + output: + # 36 MRSA 5213-A(6)(C) excludes the claimable individual; the other spouse + # still qualifies for the joint credit. + me_sales_tax_fairness_credit_eligible: true + +- name: Joint return where both spouses can be claimed as dependents + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: ME + output: + me_sales_tax_fairness_credit_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml index f916a47b51d..eee9da8f35d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml @@ -55,20 +55,36 @@ period: 2021 input: mi_personal_exemptions: 5_000 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true filing_status: SINGLE state_code: MI output: mi_exemptions: 1_500 -- name: not disabled, is a dependent, married +- name: not disabled, both spouses can be claimed as dependents, married period: 2021 input: - mi_personal_exemptions: 10_000 - head_is_dependent_elsewhere: true - filing_status: JOINT - state_code: MI + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MI output: + # $1,500 for each spouse. mi_exemptions: 3_000 # 2025 test cases - personal exemption $5,800, disabled exemption $3,400 @@ -301,15 +317,131 @@ person1: mi_disabled_exemption_eligible_person: true is_fully_disabled_service_connected_veteran: false + claimed_as_dependent_on_another_return: true tax_units: tax_unit: mi_personal_exemptions: 5_800 - head_is_dependent_elsewhere: true filing_status: SINGLE members: [person1] household: members: [person1] state_code: MI output: - # Dependent exemption: $1,500 (for single) + $3,400 disabled = $4,900 + # Dependent exemption: $1,500 + $3,400 disabled = $4,900 mi_exemptions: 4_900 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MI + output: + # MCL 206.30(4): $1,500 for the claimable spouse, plus the other + # spouse's own $5,800 personal exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: MI + output: + # MCL 206.30(4): $1,500 for the claimable spouse, plus the other + # spouse's own $5,800 personal exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return with a child where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child_1] + state_code: MI + output: + # A joint return on which a spouse can be claimed claims no dependents + # (IRS Publication 501), so the child adds no exemption. + mi_exemptions: 7_300 + +- name: 2025 joint return with a child where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child_1] + state_code: MI + output: + # A joint return on which a spouse can be claimed claims no dependents + # (IRS Publication 501), so the child adds no exemption. + mi_exemptions: 7_300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml index dba14ed3fb0..6cb8a3e48c6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml @@ -133,3 +133,59 @@ # $4,401 is below the federal EITC investment income limit ($11,950 in # 2025) but above Missouri's $4,400 limit. mo_wftc_eligible: false + +- name: 2025 married couple filing combined is not stopped by a claimable spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + eitc: 500 + households: + household: + members: [person_a, person_b] + state_code: MO + output: + # A joint federal filer whose spouse can be claimed still files Form MO-1040 + # as married filing combined, not as claimed as a dependent. + mo_wftc_eligible: true + +- name: 2025 married couple filing combined is not stopped by a claimable spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + eitc: 500 + households: + household: + members: [person_b, person_a] + state_code: MO + output: + # A joint federal filer whose spouse can be claimed still files Form MO-1040 + # as married filing combined, not as claimed as a dependent. + mo_wftc_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml index df774c30456..de3db5f2233 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.yaml @@ -1,7 +1,7 @@ - name: Is dependent on another return period: 2022 input: - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true state_code: NM earned_income: 1 is_disabled: false @@ -15,7 +15,7 @@ - name: No earned income period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -29,7 +29,7 @@ - name: No earned income but disabled - eligible period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: true @@ -43,7 +43,7 @@ - name: Over income threshold period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -57,7 +57,7 @@ - name: Receives tanf period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: false @@ -71,7 +71,7 @@ - name: Eligible period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 1 is_disabled: false @@ -101,7 +101,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -130,7 +129,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -159,7 +157,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -188,7 +185,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -217,7 +213,6 @@ tax_units: tax_unit: members: [head, spouse] - head_is_dependent_elsewhere: false filing_status: JOINT nm_modified_gross_income: 30_159 households: @@ -230,7 +225,7 @@ - name: Reported TANF receipt disqualifies despite zero computed TANF period: 2022 input: - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false state_code: NM earned_income: 0 is_disabled: true @@ -241,3 +236,89 @@ nm_modified_gross_income: 30_159 output: nm_cdcc_eligible: false + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_cdcc_eligible: true + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_cdcc_eligible: true + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2022 + input: + people: + person_a: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + earned_income: 1 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + nm_modified_gross_income: 30_159 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + nm_cdcc_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml index df55277d73a..518854ff24e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml @@ -4,7 +4,7 @@ state_code: NM age: 64 other_medical_expenses: 30_000 - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 0 @@ -16,7 +16,7 @@ medicare_enrolled: false other_medical_expenses: 27_999 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 0 @@ -27,7 +27,7 @@ age: 65 other_medical_expenses: 28_000 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 2_800 @@ -38,7 +38,7 @@ age: 65 other_medical_expenses: 28_000 filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 1_400 @@ -49,7 +49,7 @@ age: 65 other_medical_expenses: 40_000 filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_medical_expense_credit: 2_800 @@ -60,6 +60,86 @@ age: 65 other_medical_expenses: 40_000 filing_status: SINGLE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_medical_expense_credit: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_medical_expense_credit: 2_800 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 66 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_medical_expense_credit: 2_800 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2023 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_medical_expense_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml index 6f65f043918..bcb29cf3062 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.yaml @@ -3,7 +3,7 @@ input: exemptions: 0 state_code: NM - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_deduction_for_certain_dependents_eligible: true @@ -12,7 +12,7 @@ input: exemptions: 1 state_code: NM - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_deduction_for_certain_dependents_eligible: false @@ -22,6 +22,86 @@ input: exemptions: 0 state_code: NM - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_deduction_for_certain_dependents_eligible: false + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_deduction_for_certain_dependents_eligible: true + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_deduction_for_certain_dependents_eligible: true + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + exemptions: 0 + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_deduction_for_certain_dependents_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml index 72c2bb91a67..3a1c22772bc 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_2021_income_rebate.yaml @@ -4,7 +4,7 @@ state_code: NM adjusted_gross_income: 74_999 filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 250 @@ -14,7 +14,7 @@ state_code: NM adjusted_gross_income: 75_000 filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 0 @@ -24,7 +24,7 @@ state_code: NM adjusted_gross_income: 149_999 filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 500 @@ -34,7 +34,7 @@ state_code: NM adjusted_gross_income: 150_000 filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_2021_income_rebate: 0 @@ -44,6 +44,86 @@ state_code: NM adjusted_gross_income: 149_999 filing_status: JOINT - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_2021_income_rebate: 500 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_2021_income_rebate: 500 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 50_000 + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml index ddf2f6150d1..91a64599414 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_additional_2021_income_rebate.yaml @@ -3,7 +3,7 @@ input: state_code: NM filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -12,7 +12,7 @@ input: state_code: NM filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 500 @@ -21,7 +21,7 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 500 @@ -30,7 +30,7 @@ input: state_code: NM filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -39,7 +39,7 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_additional_2021_income_rebate: 1_000 @@ -48,6 +48,83 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_additional_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_additional_2021_income_rebate: 1_000 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_additional_2021_income_rebate: 1_000 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_additional_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml index ac64cc9f434..0b1b8565f7a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/2021_income/nm_supplemental_2021_income_rebate.yaml @@ -3,7 +3,7 @@ input: state_code: NM filing_status: JOINT - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -12,7 +12,7 @@ input: state_code: NM filing_status: SINGLE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 500 @@ -21,7 +21,7 @@ input: state_code: NM filing_status: SEPARATE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 500 @@ -30,7 +30,7 @@ input: state_code: NM filing_status: SURVIVING_SPOUSE - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -39,7 +39,7 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false output: nm_supplemental_2021_income_rebate: 1_000 @@ -48,6 +48,83 @@ input: state_code: NM filing_status: HEAD_OF_HOUSEHOLD - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true + output: + nm_supplemental_2021_income_rebate: 0 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_supplemental_2021_income_rebate: 1_000 + +- name: Joint return where only one spouse is a dependent of another taxpayer (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # A spouse who is not a dependent may still qualify (PIT-RC instructions). + nm_supplemental_2021_income_rebate: 1_000 + +- name: Joint return where both spouses are a dependent of another taxpayer + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NM output: nm_supplemental_2021_income_rebate: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml new file mode 100644 index 00000000000..cd98af8b0dc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.yaml @@ -0,0 +1,94 @@ +- name: 2025 single filer who can be claimed as a dependent + period: 2025 + input: + state_code: NY + filing_status: SINGLE + claimed_as_dependent_on_another_return: true + output: + ny_standard_deduction: 3_100 + +- name: 2025 single filer who cannot be claimed as a dependent + period: 2025 + input: + state_code: NY + filing_status: SINGLE + claimed_as_dependent_on_another_return: false + output: + ny_standard_deduction: 8_000 + +- name: 2025 head of household who can be claimed as a dependent keeps the regular amount + period: 2025 + input: + state_code: NY + filing_status: HEAD_OF_HOUSEHOLD + claimed_as_dependent_on_another_return: true + output: + # The IT-201 table gives the dependent amount only to filing status 1. + ny_standard_deduction: 11_200 + +- name: 2025 separate filer who can be claimed as a dependent keeps the regular amount + period: 2025 + input: + state_code: NY + filing_status: SEPARATE + claimed_as_dependent_on_another_return: true + output: + ny_standard_deduction: 8_000 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: NY + output: + # The IT-201 table gives married filing jointly $16,050 whether or not + # item C is marked. + ny_standard_deduction: 16_050 + +- name: 2025 joint return where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: NY + output: + # The IT-201 table gives married filing jointly $16,050 whether or not + # item C is marked. + ny_standard_deduction: 16_050 diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml index 6c2182a4783..7818ce120d2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml @@ -68,7 +68,7 @@ state_code: OR filing_status: JOINT age_head: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 600 output: # $1100 base @@ -80,7 +80,7 @@ state_code: OR filing_status: SINGLE age_head: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 1_000 output: # $1000 income + $350 addition, below the $2,350 normal standard deduction cap. @@ -92,7 +92,7 @@ state_code: OR filing_status: SINGLE age: 35 - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true tax_unit_earned_income: 1_000 blind_head: true output: @@ -137,3 +137,57 @@ output: # $2,835 base + $1,200 aged or_standard_deduction: 4_035 + +- name: 2025 joint return limited to the dependent amount when either spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 35 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 35 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + tax_unit_earned_income: 3_000 + households: + household: + members: [person_a, person_b] + state_code: OR + output: + # max($1,350, $3,000 + $450) = $3,450, below the $5,670 joint amount. + or_standard_deduction: 3_450 + +- name: 2025 joint return limited to the dependent amount when either spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 35 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 35 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + tax_unit_earned_income: 3_000 + households: + household: + members: [person_b, person_a] + state_code: OR + output: + # max($1,350, $3,000 + $450) = $3,450, below the $5,670 joint amount. + or_standard_deduction: 3_450 diff --git a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml index 5584f3c0288..b5cf4a21af1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.yaml @@ -2,7 +2,7 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true va_aged_blind_exemption: 0 va_age_deduction: 0 va_military_benefit_subtraction: 0 @@ -14,7 +14,7 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false va_aged_blind_exemption: 1_600 va_age_deduction: 12_000 va_military_benefit_subtraction: 0 @@ -26,10 +26,60 @@ period: 2021 input: state_code: VA - head_is_dependent_elsewhere: false + claimed_as_dependent_on_another_return: false va_aged_blind_exemption: 0 va_age_deduction: 0 va_military_benefit_subtraction: 0 va_federal_state_employees_subtraction: 0 output: va_low_income_tax_credit_eligible: true + +- name: Joint return is ineligible when either spouse is claimed as a dependent (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VA + output: + va_low_income_tax_credit_eligible: false + +- name: Joint return is ineligible when either spouse is claimed as a dependent (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: VA + output: + va_low_income_tax_credit_eligible: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml new file mode 100644 index 00000000000..80029885451 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.yaml @@ -0,0 +1,114 @@ +- name: Head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: true + +- name: Neither spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere: false + +- name: A dependent claimed elsewhere is not a filer + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_or_spouse_is_dependent_elsewhere: false + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + head_or_spouse_is_dependent_elsewhere: true diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml new file mode 100644 index 00000000000..ac5381a1b4b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.yaml @@ -0,0 +1,114 @@ +- name: Head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 0 + +- name: Neither spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_spouse_count_not_dependent_elsewhere: 2 + +- name: A dependent claimed elsewhere is not a filer + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_spouse_count_not_dependent_elsewhere: 1 + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + head_spouse_count_not_dependent_elsewhere: 0 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py index e3d3745bdc5..e8575cdcc7e 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py @@ -7,13 +7,23 @@ class basic_standard_deduction(Variable): label = "Basic standard deduction" definition_period = YEAR unit = USD - reference = "https://www.law.cornell.edu/uscode/text/26/63#c_2" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/63#c_2", + "https://www.law.cornell.edu/uscode/text/26/63#c_5", + # Standard Deduction Worksheet for Dependents: "Use this worksheet + # only if someone can claim you, or your spouse if filing jointly, + # as a dependent." + "https://www.irs.gov/pub/irs-prior/i1040gi--2025.pdf#page=35", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.irs.deductions.standard filing_status = tax_unit("filing_status", period) separate_filer_itemizes = tax_unit("separate_filer_itemizes", period) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # A joint return uses the dependent worksheet, with the couple's + # earned income and the joint amount as the cap, when either spouse + # can be claimed. + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) standard_deduction = p.amount[filing_status] standard_deduction_if_dependent = min_( standard_deduction, diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py index c4f098d02f1..a98403539f7 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py @@ -8,17 +8,20 @@ class hi_food_excise_credit(Variable): defined_for = StateCode.HI unit = USD definition_period = YEAR - reference = "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=44" + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=50", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + ) def formula(tax_unit, period, parameters): - # filer can not be a dependent on another return - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - total_amount = add( - tax_unit, - period, - [ - "hi_food_excise_credit_minor_child_amount", - "hi_food_excise_exemption_amount", - ], + # HRS 235-55.85(a): only a taxpayer who cannot be claimed as a + # dependent may claim the credit, for each qualified exemption. The + # exemption amount counts only filers who cannot be claimed when + # either can; such a return claims no dependents (IRS Publication + # 501), so it gets no minor child amount. + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + exemption_amount = tax_unit("hi_food_excise_exemption_amount", period) + minor_child_amount = tax_unit( + "hi_food_excise_credit_minor_child_amount", period ) - return ~dependent_elsewhere * total_amount + return exemption_amount + where(dependent_filer, 0, minor_child_amount) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py index 1b4e22e61bc..d11bb0a6cac 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py @@ -7,6 +7,11 @@ class hi_food_excise_exemption_amount(Variable): label = "Exemption amount for Hawaii Food/Excise Tax Credit" definition_period = YEAR defined_for = StateCode.HI + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=50", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.food_excise_tax @@ -45,4 +50,15 @@ def formula(tax_unit, period, parameters): claimable_exemptions = exemptions - minor_children else: claimable_exemptions = exemptions - return claimable_exemptions * amount_per_exemption + # Form N-311 counts only people who cannot be claimed as a dependent + # by another taxpayer. A return on which a filer can be claimed claims + # no dependents (IRS Publication 501), so it counts only the filers + # who cannot be claimed. + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + qualified_exemptions = where( + dependent_filer, independent_filers, claimable_exemptions + ) + return qualified_exemptions * amount_per_exemption diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py index 8fd79179391..3a229d50ebb 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py @@ -12,6 +12,7 @@ class hi_act_115_rebate(Variable): "https://tax.hawaii.gov/act-115-ref/", "https://data.capitol.hawaii.gov/sessions/session2022/bills/SB514_CD2_.HTM", "https://files.hawaii.gov/tax/news/announce/ann22-03.pdf#page=2", + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=45", ) def formula(tax_unit, period, parameters): @@ -39,6 +40,15 @@ def formula(tax_unit, period, parameters): # person in the tax unit is one exemption. exemptions = tax_unit("exemptions_count", period) # A person who can be claimed as a dependent by another taxpayer is - # not a qualifying resident taxpayer. - head_is_dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - return where(head_is_dependent_elsewhere, 0, amount_per_exemption * exemptions) + # not a qualifying resident taxpayer, and each qualifying resident + # taxpayer claims the refund for the exemptions they are entitled to. + # A filer who can be claimed has no exemption (HRS 235-54(a)), and a + # return on which a filer can be claimed claims no dependents (IRS + # Publication 501), so such a return counts only the filers who + # cannot be claimed. + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + qualified_exemptions = where(dependent_filer, independent_filers, exemptions) + return amount_per_exemption * qualified_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py index e0a69bef748..81a87e2876e 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py @@ -8,18 +8,36 @@ class hi_tax_credit_for_low_income_household_renters(Variable): unit = USD definition_period = YEAR defined_for = "hi_tax_credit_for_low_income_household_renters_eligible" + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=48", + "https://files.hawaii.gov/tax/forms/2025/schx_i.pdf#page=1", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.lihrtc - # Aged extra exemptions - aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold).astype(int) - aged_spouse = (tax_unit("age_spouse", period) >= p.aged_age_threshold).astype( - int + # Schedule X lines 8-10 count only people who cannot be claimed as a + # dependent by another taxpayer, with an extra exemption for each + # such filer aged 65 or over. A return on which a filer can be + # claimed claims no dependents (IRS Publication 501), so it counts + # only the filers who cannot be claimed. + aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold) & ~tax_unit( + "head_is_dependent_elsewhere", period ) + aged_spouse = ( + tax_unit("age_spouse", period) >= p.aged_age_threshold + ) & ~tax_unit("spouse_is_dependent_elsewhere", period) + aged_exemptions = aged_head.astype(int) + aged_spouse.astype(int) - aged_exemptions = aged_head + aged_spouse - exemptions = tax_unit("exemptions_count", period) + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + exemptions = where( + dependent_filer, + independent_filers, + tax_unit("exemptions_count", period), + ) total_exemptions = exemptions + aged_exemptions return p.amount * total_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py index 3c99b935916..db97f6a626f 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py @@ -6,16 +6,26 @@ class hi_tax_credit_for_low_income_household_renters_eligible(Variable): entity = TaxUnit label = "Eligible for the Hawaii low income household renters tax credit" definition_period = YEAR - reference = "https://files.hawaii.gov/tax/legal/har/har_235.pdf#page=105" # §18-235-55.7 (b) + reference = ( + "https://files.hawaii.gov/tax/legal/har/har_235.pdf#page=105", # §18-235-55.7 (b) + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=48", + "https://files.hawaii.gov/tax/forms/2025/schx_i.pdf#page=1", + ) defined_for = StateCode.HI def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # HRS 235-55.7(b) allows the credit to each resident taxpayer who + # cannot be claimed as a dependent. Schedule X stops at line 3 only + # for the first-listed filer; since a couple may list either spouse + # first, a joint return qualifies unless both spouses can be claimed. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) p = parameters(period).gov.states.hi.tax.income.credits.lihrtc agi = tax_unit("hi_agi", period) rent = add(tax_unit, period, ["rent"]) return ( (rent > p.eligibility.rent_threshold) & (agi < p.eligibility.agi_limit) - & ~dependent_elsewhere + & independent_filer ) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py index 5e84412ac6c..70a783508dd 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py @@ -10,6 +10,11 @@ class hi_deductions(Variable): "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15\n" # Itemized Deduction "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=20" # Standard Deduction ) + reference = ( + "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11", + # PDF pages 15, 20 + "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=15", + ) definition_period = YEAR defined_for = StateCode.HI @@ -25,14 +30,26 @@ def formula(tax_unit, period, parameters): filing_status_eligible = ( total_itemized_deduction > p.threshold.deductions[filing_status] ) - is_dependent_on_another_return = tax_unit("head_is_dependent_elsewhere", period) - standard_cap = min_(tax_unit_earned_income, standard_deduction) - dependent_floor = max_(p.threshold.dependent, standard_cap) + # HRS 235-2.4(a)(3) applies IRC 63(c)(5): the standard deduction of a + # filer who can be claimed as a dependent is the greater of $500 or + # earned income, up to the regular amount. As federally, a joint + # return is limited when either spouse can be claimed. + is_dependent_on_another_return = tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + dependent_standard_deduction = min_( + max_(p.threshold.dependent, tax_unit_earned_income), standard_deduction + ) + applicable_standard_deduction = where( + is_dependent_on_another_return, + dependent_standard_deduction, + standard_deduction, + ) dependent_eligible = is_dependent_on_another_return & ( - total_itemized_deduction > dependent_floor + total_itemized_deduction > dependent_standard_deduction ) itemized_deductions_eligible = filing_status_eligible | dependent_eligible itemized_deduction = where( itemized_deductions_eligible, total_itemized_deduction, 0 ) - return max_(itemized_deduction, standard_deduction) + return max_(itemized_deduction, applicable_standard_deduction) diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py index c0ea36df12a..d761c2d8ecf 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py @@ -6,11 +6,21 @@ class me_sales_tax_fairness_credit_eligible(Variable): entity = TaxUnit label = "Eligible for the Maine sales tax fairness credit" definition_period = YEAR - reference = "https://legislature.maine.gov/statutes/36/title36sec5213-A.html" + reference = ( + "https://legislature.maine.gov/statutes/36/title36sec5213-A.html", + "https://www.maine.gov/revenue/sites/maine.gov.revenue/files/inline-files/25_1040me_sch_ptfc_fillable.pdf#page=3", + ) defined_for = StateCode.ME def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # 36 MRSA 5213-A(6)(C) excludes individuals who may be claimed as a + # dependent on another return, and subsection 2 allows each resident + # individual the base credit for the return's filing status. On a + # joint return where only one spouse can be claimed, the other spouse + # still qualifies for the joint credit. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - return ~dependent_elsewhere & ~separate + return independent_filer & ~separate diff --git a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py index 5974fd46303..1c77fb504ce 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py @@ -11,6 +11,8 @@ class mi_exemptions(Variable): reference = ( "https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-206-30", "https://www.legislature.mi.gov/Publications/TaxpayerGuide.pdf", + "https://www.michigan.gov/taxes/-/media/Project/Websites/taxes/Forms/IIT/TY2025/MI-1040-Book.pdf#page=10", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) def formula(tax_unit, period, parameters): @@ -31,16 +33,31 @@ def formula(tax_unit, period, parameters): ) disabled_veteran_exemption = disabled_veterans * p.disabled.amount.veteran - # Dependent-on-other-return exemptions - filing_status = tax_unit("filing_status", period) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - dependent_exemption = ( - dependent_elsewhere * p.dependent_on_other_return[filing_status] + # MCL 206.30(4): an individual who can be claimed as a dependent on + # another return gets no personal exemption but subtracts $1,500. + # This applies to each spouse separately, so on a joint return where + # only one spouse can be claimed, the other keeps their own personal + # exemption (and any stillbirth exemption). A joint return on which + # either spouse can be claimed claims no dependents (IRS Publication + # 501, "Dependent Taxpayer Test"). + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period ) + dependent_filers = filers - independent_filers + stillborn = tax_unit("tax_unit_stillborn_children", period) + independent_filer_exemptions = ( + independent_filers + where(independent_filers > 0, stillborn, 0) + ) * p.personal + dependent_filer_exemptions = dependent_filers * p.dependent_on_other_return # Total exemptions return ( - where(dependent_elsewhere, dependent_exemption, personal_exemption) + where( + dependent_filers > 0, + independent_filer_exemptions + dependent_filer_exemptions, + personal_exemption, + ) + disabled_exemption + disabled_veteran_exemption ) diff --git a/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py b/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py index d1b71e8d93e..582fdb3ed2a 100644 --- a/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py +++ b/policyengine_us/variables/gov/states/mo/tax/income/credits/mo_wftc_eligible.py @@ -8,7 +8,8 @@ class mo_wftc_eligible(Variable): definition_period = YEAR reference = ( "https://revisor.mo.gov/main/OneSection.aspx?section=143.177&bid=49978&hl=", - "https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=43", + # PDF pages 6, 43 + "https://dor.mo.gov/forms/MO-1040%20Instructions_2025.pdf#page=6", ) defined_for = StateCode.MO @@ -23,10 +24,17 @@ def formula(tax_unit, period, parameters): # married filing separately. filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - # The 2024 and 2025 Form MO-WFTC checklists also stop filers - # claimed as a dependent on another return; the 2023 form does not. - excluded_dependent = p.dependent_filers_excluded & tax_unit( - "head_is_dependent_elsewhere", period + # The 2024 and 2025 Form MO-WFTC checklists also stop filers with a + # filing status of "claimed as a dependent"; the 2023 form does not. + # A couple filing a joint federal return files Form MO-1040 as + # married filing combined even when a spouse can be claimed (2025 + # MO-1040 instructions, "Filing status"), so the stop never applies + # to a joint return. + joint = filing_status == filing_status.possible_values.JOINT + excluded_dependent = ( + p.dependent_filers_excluded + & ~joint + & tax_unit("head_or_spouse_is_dependent_elsewhere", period) ) # Form MO-WFTC applies Missouri's own investment income limit, # reflecting IRC Section 32(i) as of January 1, 2021 per RSMo diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py index ea1c0e9fd46..48198e1b904 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py @@ -6,13 +6,22 @@ class nm_cdcc_eligible(Variable): entity = TaxUnit label = "Eligible household for the New Mexico dependent child day care credit" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503752/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsfAEwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503752/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsfAEwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): person = tax_unit.members - # Filer can not be a dependent on another tax return - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # Filer can not be a dependent on another tax return, but "If you are + # a dependent with a spouse who was not a dependent of another + # taxpayer, your spouse may still qualify to claim rebates or + # credits" (PIT-RC instructions). So only a return on which every + # filer is a dependent elsewhere is barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) p = parameters(period).gov.states.nm.tax.income.credits.cdcc # Filer has to be be gainfully employed to receive credit has_earnings = person("earned_income", period) > 0 @@ -43,8 +52,5 @@ def formula(tax_unit, period, parameters): ) income_eligible = nm_modified_gross_income <= income_limit return ( - ~dependent_elsewhere - & employment_eligible - & ~receives_tanf - & income_eligible + independent_filer & employment_eligible & ~receives_tanf & income_eligible ) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py index e516884f45e..205f3b1b0e4 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py @@ -6,7 +6,10 @@ class nm_medical_expense_credit(Variable): entity = TaxUnit label = "New Mexico unreimbursed medical expense care credit" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503776/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDswgGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503776/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDswgGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): @@ -17,8 +20,15 @@ def formula(tax_unit, period, parameters): medical_expense = tax_unit("itemized_medical_expenses", period) age_eligible = tax_unit.any(age >= p.age_eligibility) expense_eligible = medical_expense >= p.min_expenses - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - eligible = age_eligible & expense_eligible & ~dependent_elsewhere + # The rebate and credit schedule bars a filer who is a dependent of + # another taxpayer, but "If you are a dependent with a spouse who was + # not a dependent of another taxpayer, your spouse may still qualify + # to claim rebates or credits" (PIT-RC instructions). So only a return + # on which every filer is a dependent elsewhere is barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) + eligible = age_eligible & expense_eligible & independent_filer # exemption is halved for married filing separately filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py index d0415b60595..d66ef23f1a6 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py @@ -6,12 +6,22 @@ class nm_deduction_for_certain_dependents_eligible(Variable): entity = TaxUnit label = "Eligibility for New Mexico deduction for certain dependents" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503892/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgAcATgBMASgA0ybKUIQAiokK4AntADkW6REJhcCFWs069BoyADKeUgCFNAJQCiAGRcA1AIIA5AMIu0qRgAEbQpOySkkA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503892/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgAcATgBMASgA0ybKUIQAiokK4AntADkW6REJhcCFWs069BoyADKeUgCFNAJQCiAGRcA1AIIA5AMIu0qRgAEbQpOySkkA", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The deduction uses the same "not a dependent of another individual" + # test as the rebates and credits on Schedule PIT-RC, whose + # instructions let a spouse who is not a dependent still qualify. So + # only a return on which every filer is a dependent elsewhere is + # barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) # deduction does not apply if an exemption under IRS 151 is claimed; # IRC 151 refers to the federal personal exemption federal_exemption_amount = tax_unit("exemptions", period) - return ~dependent_elsewhere & (federal_exemption_amount == 0) + return independent_filer & (federal_exemption_amount == 0) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py index 1f37e1c7c21..8ce48277203 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py @@ -6,15 +6,26 @@ class nm_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503708/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPABwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503708/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPABwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate and credit schedule bars a filer who is a dependent of + # another taxpayer, but "If you are a dependent with a spouse who was + # not a dependent of another taxpayer, your spouse may still qualify + # to claim rebates or credits" (PIT-RC instructions). So only a return + # on which every filer is a dependent elsewhere is barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) agi = tax_unit("adjusted_gross_income", period) filing_status = tax_unit("filing_status", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] income_eligible = agi < p.main.income_limit[filing_status] - eligible = income_eligible & ~dependent_elsewhere + eligible = income_eligible & independent_filer amount_if_eligible = p.main.amount[filing_status] return eligible * amount_if_eligible diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py index 891ce07d54e..dd68e862de4 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py @@ -6,11 +6,22 @@ class nm_additional_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico additional 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503710/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsHHgEoANMmylCEAIqJCuAJ7QA5BskRCYXAiUr1WnXoMgAynlIAhdQCUAogBknANQCCAOQDCTyVIwACNoUnZxcSA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503710/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsHHgEoANMmylCEAIqJCuAJ7QA5BskRCYXAiUr1WnXoMgAynlIAhdQCUAogBknANQCCAOQDCTyVIwACNoUnZxcSA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate and credit schedule bars a filer who is a dependent of + # another taxpayer, but "If you are a dependent with a spouse who was + # not a dependent of another taxpayer, your spouse may still qualify + # to claim rebates or credits" (PIT-RC instructions). So only a return + # on which every filer is a dependent elsewhere is barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return ~dependent_elsewhere * p.additional.amount[filing_status] + return independent_filer * p.additional.amount[filing_status] diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py index 7839b75873d..e98e3be36dd 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py @@ -6,11 +6,22 @@ class nm_supplemental_2021_income_rebate(Variable): entity = TaxUnit label = "New Mexico supplemental 2021 income tax rebate" definition_period = YEAR - reference = "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503706/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPAGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA" + reference = ( + "https://nmonesource.com/nmos/nmsa/en/item/4340/index.do#!fragment/zoupio-_Toc140503706/BQCwhgziBcwMYgK4DsDWszIQewE4BUBTADwBdoAvbRABwEtsBaAfX2zgEYAWABgFYeAZgDsPAGwBKADTJspQhACKiQrgCe0AOSapEQmFwJlqjdt37DIAMp5SAIQ0AlAKIAZZwDUAggDkAws5SpGAARtCk7BISQA", + # PDF pages 2-3 + "https://realfile.tax.newmexico.gov/2021pit-rc-ins.pdf#page=2", + ) defined_for = StateCode.NM def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # The rebate and credit schedule bars a filer who is a dependent of + # another taxpayer, but "If you are a dependent with a spouse who was + # not a dependent of another taxpayer, your spouse may still qualify + # to claim rebates or credits" (PIT-RC instructions). So only a return + # on which every filer is a dependent elsewhere is barred. + independent_filer = ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 + ) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return ~dependent_elsewhere * p.supplemental.amount[filing_status] + return independent_filer * p.supplemental.amount[filing_status] diff --git a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py index d98250ebddb..1720bb49cd2 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/ny_standard_deduction.py @@ -7,17 +7,21 @@ class ny_standard_deduction(Variable): label = "NY standard deduction" unit = USD definition_period = YEAR - reference = "https://www.nysenate.gov/legislation/laws/TAX/613" + reference = ( + "https://www.nysenate.gov/legislation/laws/TAX/614", + # Standard deduction table: the dependent amount applies only to + # filing status 1, "Single and you marked item C Yes". + "https://www.tax.ny.gov/pdf/2025/inc/it201i_2025.pdf#page=11", + ) defined_for = StateCode.NY def formula(tax_unit, period, parameters): - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) - standard_deduction = parameters( - period - ).gov.states.ny.tax.income.deductions.standard + p = parameters(period).gov.states.ny.tax.income.deductions.standard filing_status = tax_unit("filing_status", period) + single = filing_status == filing_status.possible_values.SINGLE + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) return where( - dependent_elsewhere, - standard_deduction.dependent_elsewhere, - standard_deduction.amount[filing_status], + single & dependent_elsewhere, + p.dependent_elsewhere, + p.amount[filing_status], ) diff --git a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py index 6d8dde3554d..eb3c543e6fa 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py +++ b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py @@ -8,9 +8,10 @@ class or_standard_deduction(Variable): unit = USD definition_period = YEAR documentation = "Oregon standard deduction, including bonus for aged or blind and special rules for filers who are claimable as dependents." - reference = ( # TODO: update + reference = ( "https://www.oregon.gov/dor/forms/FormsPubs/form-or-40-inst_101-040-1_2021.pdf#page=18", - "https://www.oregonlegislature.gov/bills_laws/ors/ors316.html", # Subsection 316.695 (7) + "https://www.oregonlegislature.gov/bills_laws/ors/ors316.html", # ORS 316.695(7)-(8) + "https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=96", ) defined_for = StateCode.OR @@ -25,7 +26,11 @@ def formula(tax_unit, period, parameters): claimable_dep_earned_amount = ( earned_income + p.claimable_as_dependent.earned_income_addition ) - dependent_elsewhere = tax_unit("head_is_dependent_elsewhere", period) + # ORS 316.695(8) limits the deduction as IRC 63(c)(5) does, so a joint + # return uses the couple's earned income and the joint amount when + # either spouse can be claimed (Publication OR-17, "Standard deduction + # worksheet for married filing jointly (MFJ) dependents"). + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) # Set floor and ceiling around earned income plus additional amount. floored_claimable_dep_amount = max_( claimable_dep_earned_amount, claimable_dep_floor diff --git a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py index 82b96fe10d0..7b746711ad1 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py @@ -7,16 +7,23 @@ class va_low_income_tax_credit_eligible(Variable): label = "Eligible for the Virginia Low Income Tax Credit" definition_period = YEAR defined_for = StateCode.VA + reference = ( + "https://law.lis.virginia.gov/vacode/title58.1/chapter3/section58.1-339.8/", + "https://www.tax.virginia.gov/sites/default/files/vatax-pdf/2025-760-instructions.pdf#page=30", + ) def formula(tax_unit, period, parameters): # Criteria 1: Not qualified to claim EITC if anyone in the tax unit claimed any of the following: p = parameters(period).gov.states.va.tax.income.credits.eitc.low_income_tax program_eligible = add(tax_unit, period, p.ineligible_programs) == 0 - # Criteria 2: Not qualified to claim EITC if the filier is claimed as a dependent on another taxpayer's return - - head_is_not_dependent_elsewhere = ~tax_unit( - "head_is_dependent_elsewhere", period + # Criteria 2: Not qualified if a filer is claimed as a dependent on + # another taxpayer's return. Va. Code 58.1-339.8 bars the credit + # against the tax of a dependent of another individual or couple; a + # joint return carries both spouses' tax, so either spouse being + # claimed bars it. + filer_is_dependent_elsewhere = tax_unit( + "head_or_spouse_is_dependent_elsewhere", period ) - return program_eligible & head_is_not_dependent_elsewhere + return program_eligible & ~filer_is_dependent_elsewhere diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py new file mode 100644 index 00000000000..493bf92c3c4 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class head_or_spouse_is_dependent_elsewhere(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Head or spouse is a dependent elsewhere" + documentation = ( + "Whether the head or the spouse of this tax unit is claimed as a " + "dependent in another tax unit. Unlike head_is_dependent_elsewhere, " + "this does not depend on which spouse is labelled head." + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + claimed_on_another_return = person( + "claimed_as_dependent_on_another_return", period + ) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.any(claimed_on_another_return & filer) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py new file mode 100644 index 00000000000..348b7a993a2 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class head_spouse_count_not_dependent_elsewhere(Variable): + value_type = int + entity = TaxUnit + definition_period = YEAR + label = "Head and spouse count not dependent elsewhere" + documentation = ( + "Number of tax unit heads and spouses who are not claimed as a " + "dependent in another tax unit." + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + claimed_on_another_return = person( + "claimed_as_dependent_on_another_return", period + ) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.sum(filer & ~claimed_on_another_return) From 3ecc675fe669409f59f1a756cf30103598e59598 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 08:35:58 -0400 Subject: [PATCH 02/42] Keep tax units without a filer eligible where only claimed filers are barred The New Mexico, Maine and Hawaii renters gates counted filers who are not claimed elsewhere, so a tax unit with neither a head nor a spouse (252 in the default dataset) looked like one whose every filer was claimed. A new helper, every_filer_is_dependent_elsewhere, requires a claimed filer and no unclaimed one. Co-Authored-By: Claude Opus 5.5 --- ...t_elsewhere_head_spouse_swap_invariance.py | 19 ++-- .../every_filer_is_dependent_elsewhere.yaml | 89 +++++++++++++++++++ ...r_low_income_household_renters_eligible.py | 6 +- .../me_sales_tax_fairness_credit_eligible.py | 6 +- .../income/credits/cdcc/nm_cdcc_eligible.py | 9 +- .../credits/nm_medical_expense_credit.py | 6 +- ...duction_for_certain_dependents_eligible.py | 6 +- .../2021_rebate/nm_2021_income_rebate.py | 6 +- .../nm_additional_2021_income_rebate.py | 6 +- .../nm_supplemental_2021_income_rebate.py | 6 +- .../every_filer_is_dependent_elsewhere.py | 18 ++++ 11 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml create mode 100644 policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py index 8c3e6fc2c48..721d38dff05 100644 --- a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -33,9 +33,10 @@ 2. Monotonicity: marking another filer as claimed elsewhere never raises any of the eighteen amounts or turns an eligibility on. 3. The helper identities: `head_or_spouse_is_dependent_elsewhere` equals - `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, and - `head_spouse_count_not_dependent_elsewhere` plus the claimed filers equals - the number of filers. + `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, + `every_filer_is_dependent_elsewhere` equals their conjunction for a + couple, and `head_spouse_count_not_dependent_elsewhere` plus the claimed + filers equals the number of filers. """ import numpy as np @@ -173,9 +174,11 @@ def _crafted_cases(states, review_case_only=False): "rent": 9_000.0, "other_medical_expenses": 30_000.0, } + # One claimed adult covers both labellings of a claimed spouse, since each + # case runs with the labels exchanged. cases = [] for state in states: - for claimed in CLAIM_PATTERNS[:3]: + for claimed in [(True, False), (True, True)]: cases += [ { "state": state, @@ -291,6 +294,8 @@ def _check_swap(units, year, outputs): np.testing.assert_array_equal(either, np.maximum(head, spouse)) independent = _calc(sim, "head_spouse_count_not_dependent_elsewhere", year) np.testing.assert_array_equal(independent + head + spouse, 2) + every = _calc(sim, "every_filer_is_dependent_elsewhere", year) + np.testing.assert_array_equal(every, np.minimum(head, spouse)) def _monotone_variants(unit): @@ -329,19 +334,19 @@ def _check_monotone(units, year): # A batch's cost is mostly per-variable overhead, so each example is a large # batch and there are few examples. @settings( - max_examples=6, + max_examples=4, deadline=None, derandomize=True, suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], ) -@given(st.sampled_from(YEARS), st.lists(couples(), min_size=8, max_size=20)) +@given(st.sampled_from(YEARS), st.lists(couples(), min_size=6, max_size=15)) def test_claimed_spouse_does_not_depend_on_head_label(year, units): _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) @pytest.mark.parametrize("year", YEARS) def test_seeded_population(year): - units = _seeded_couples(AFFECTED, per_state=4) + _crafted_cases(AFFECTED) + units = _seeded_couples(AFFECTED, per_state=2) + _crafted_cases(AFFECTED) _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) _check_monotone(units, year) diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml new file mode 100644 index 00000000000..2b934fda74a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.yaml @@ -0,0 +1,89 @@ +- name: Only the head is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: false + +- name: Only the spouse is claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: false + +- name: Both spouses are claimed elsewhere + period: 2025 + input: + people: + head: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + spouse: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + output: + every_filer_is_dependent_elsewhere: true + +- name: Single filer claimed elsewhere + period: 2025 + input: + people: + head: + age: 19 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + every_filer_is_dependent_elsewhere: true + +- name: A tax unit with neither a head nor a spouse has no filer claimed elsewhere + period: 2025 + input: + people: + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [child] + output: + every_filer_is_dependent_elsewhere: false diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py index db97f6a626f..61f4c81eaf2 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py @@ -18,14 +18,12 @@ def formula(tax_unit, period, parameters): # cannot be claimed as a dependent. Schedule X stops at line 3 only # for the first-listed filer; since a couple may list either spouse # first, a joint return qualifies unless both spouses can be claimed. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.hi.tax.income.credits.lihrtc agi = tax_unit("hi_agi", period) rent = add(tax_unit, period, ["rent"]) return ( (rent > p.eligibility.rent_threshold) & (agi < p.eligibility.agi_limit) - & independent_filer + & ~every_filer_dependent ) diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py index d761c2d8ecf..6d8e7aac8dd 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py @@ -18,9 +18,7 @@ def formula(tax_unit, period, parameters): # individual the base credit for the return's filing status. On a # joint return where only one spouse can be claimed, the other spouse # still qualifies for the joint credit. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - return independent_filer & ~separate + return ~every_filer_dependent & ~separate diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py index 48198e1b904..3bdf5fa4fed 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/cdcc/nm_cdcc_eligible.py @@ -19,9 +19,7 @@ def formula(tax_unit, period, parameters): # taxpayer, your spouse may still qualify to claim rebates or # credits" (PIT-RC instructions). So only a return on which every # filer is a dependent elsewhere is barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.credits.cdcc # Filer has to be be gainfully employed to receive credit has_earnings = person("earned_income", period) > 0 @@ -52,5 +50,8 @@ def formula(tax_unit, period, parameters): ) income_eligible = nm_modified_gross_income <= income_limit return ( - independent_filer & employment_eligible & ~receives_tanf & income_eligible + ~every_filer_dependent + & employment_eligible + & ~receives_tanf + & income_eligible ) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py index 205f3b1b0e4..27585ed2568 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py @@ -25,10 +25,8 @@ def formula(tax_unit, period, parameters): # not a dependent of another taxpayer, your spouse may still qualify # to claim rebates or credits" (PIT-RC instructions). So only a return # on which every filer is a dependent elsewhere is barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) - eligible = age_eligible & expense_eligible & independent_filer + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) + eligible = age_eligible & expense_eligible & ~every_filer_dependent # exemption is halved for married filing separately filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py index d66ef23f1a6..389227abe70 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py @@ -18,10 +18,8 @@ def formula(tax_unit, period, parameters): # instructions let a spouse who is not a dependent still qualify. So # only a return on which every filer is a dependent elsewhere is # barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) # deduction does not apply if an exemption under IRS 151 is claimed; # IRC 151 refers to the federal personal exemption federal_exemption_amount = tax_unit("exemptions", period) - return independent_filer & (federal_exemption_amount == 0) + return ~every_filer_dependent & (federal_exemption_amount == 0) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py index 8ce48277203..c49035340e2 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py @@ -19,13 +19,11 @@ def formula(tax_unit, period, parameters): # not a dependent of another taxpayer, your spouse may still qualify # to claim rebates or credits" (PIT-RC instructions). So only a return # on which every filer is a dependent elsewhere is barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) agi = tax_unit("adjusted_gross_income", period) filing_status = tax_unit("filing_status", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] income_eligible = agi < p.main.income_limit[filing_status] - eligible = income_eligible & independent_filer + eligible = income_eligible & ~every_filer_dependent amount_if_eligible = p.main.amount[filing_status] return eligible * amount_if_eligible diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py index dd68e862de4..f574c92dbab 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py @@ -19,9 +19,7 @@ def formula(tax_unit, period, parameters): # not a dependent of another taxpayer, your spouse may still qualify # to claim rebates or credits" (PIT-RC instructions). So only a return # on which every filer is a dependent elsewhere is barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return independent_filer * p.additional.amount[filing_status] + return ~every_filer_dependent * p.additional.amount[filing_status] diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py index e98e3be36dd..bf8f1a88d52 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py @@ -19,9 +19,7 @@ def formula(tax_unit, period, parameters): # not a dependent of another taxpayer, your spouse may still qualify # to claim rebates or credits" (PIT-RC instructions). So only a return # on which every filer is a dependent elsewhere is barred. - independent_filer = ( - tax_unit("head_spouse_count_not_dependent_elsewhere", period) > 0 - ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) - return independent_filer * p.supplemental.amount[filing_status] + return ~every_filer_dependent * p.supplemental.amount[filing_status] diff --git a/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py new file mode 100644 index 00000000000..161339f0358 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class every_filer_is_dependent_elsewhere(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Every head and spouse is a dependent elsewhere" + documentation = ( + "Whether the tax unit has a head or spouse claimed as a dependent in " + "another tax unit and none who is not. False for a tax unit with " + "neither a head nor a spouse." + ) + + def formula(tax_unit, period, parameters): + return tax_unit("head_or_spouse_is_dependent_elsewhere", period) & ( + tax_unit("head_spouse_count_not_dependent_elsewhere", period) == 0 + ) From 8da831289dabf23c4f36e06fa567f4a32a7293d4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 09:14:47 -0400 Subject: [PATCH 03/42] Address review: Hawaii worksheet earnings, NM same-filer age, childless EITC - Hawaii's dependent standard deduction uses earned income net of the deductible part of self-employment tax, as the N-11 worksheet does. - New Mexico's medical care credit needs a filer who is both 65 or older and not a dependent when a filer is a dependent elsewhere (NMSA 7-2-18.13). - The federal EITC without a qualifying child requires that no filer can be claimed as a dependent (IRC 32(c)(1)(A)(ii)(III), Publication 596), so Missouri's working family credit no longer reaches such a couple. - Hawaii's food/excise credit keeps minor children receiving public support (HRS 235-55.85(c)) on a joint return where one spouse can be claimed. - Comments label the readings the law does not settle, and the property test covers the EITC and the food/excise exemption amount. Co-Authored-By: Claude Opus 5.5 --- .../head-dependent-elsewhere-swap.fixed.md | 2 +- ...t_elsewhere_head_spouse_swap_invariance.py | 36 +++-- .../eitc_demographic_eligible.yaml | 130 ++++++++++++++++++ .../food_excise/hi_food_excise_credit.yaml | 130 ++++++++++++++++++ .../tax/income/deductions/hi_deductions.yaml | 79 +++++++++++ .../income/credits/wftc/mo_wftc_eligible.yaml | 62 +++++++++ .../credits/nm_medical_expense_credit.yaml | 106 ++++++++++++++ .../eligibility/eitc_demographic_eligible.py | 16 ++- .../food_excise/hi_food_excise_credit.py | 11 +- .../hi_food_excise_exemption_amount.py | 20 ++- .../tax/income/credits/hi_act_115_rebate.py | 7 +- ...low_income_household_renters_tax_credit.py | 5 +- .../hi/tax/income/deductions/hi_deductions.py | 7 +- .../me_sales_tax_fairness_credit_eligible.py | 6 +- .../mi/tax/income/exemptions/mi_exemptions.py | 12 +- .../credits/nm_medical_expense_credit.py | 22 +-- ...duction_for_certain_dependents_eligible.py | 9 +- .../2021_rebate/nm_2021_income_rebate.py | 13 +- .../nm_additional_2021_income_rebate.py | 13 +- .../nm_supplemental_2021_income_rebate.py | 13 +- .../va_low_income_tax_credit_eligible.py | 9 +- 21 files changed, 635 insertions(+), 73 deletions(-) diff --git a/changelog.d/head-dependent-elsewhere-swap.fixed.md b/changelog.d/head-dependent-elsewhere-swap.fixed.md index 4b4d878f15f..42a32945776 100644 --- a/changelog.d/head-dependent-elsewhere-swap.fixed.md +++ b/changelog.d/head-dependent-elsewhere-swap.fixed.md @@ -1 +1 @@ -The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers, and Hawaii now limits the standard deduction of a filer who can be claimed as a dependent. +The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers; Hawaii now limits the standard deduction of a filer who can be claimed as a dependent; the federal EITC without a qualifying child now requires that no filer can be claimed as a dependent; and New Mexico's medical care credit requires a filer who is both 65 or older and not a dependent when a filer is a dependent elsewhere. diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py index 721d38dff05..d64c56ff5ba 100644 --- a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -4,34 +4,40 @@ A married couple may list either spouse first, so exchanging the two adults' `is_tax_unit_head` and `is_tax_unit_spouse` labels, with every other input kept, must not change their taxes. On main, the federal standard deduction and -seventeen state formulas read only `head_is_dependent_elsewhere`, so a joint -return where one spouse can be claimed on another return (a joint return filed -only to recover withholding can meet the federal exception, IRS Publication -501) was taxed differently depending on which spouse was labelled head. Each +16 state formulas read only `head_is_dependent_elsewhere`, so a joint return +where one spouse can be claimed on another return (a joint return filed only +to recover withholding can meet the federal exception, IRS Publication 501) +was taxed differently depending on which spouse was labelled head. Each formula now applies its jurisdiction's rule for that case: - Either spouse limits the return (the IRC 63(c)(5) worksheet): the federal standard deduction, Oregon's and Hawaii's standard deductions, and - Virginia's credit for low-income individuals. + Virginia's credit for low-income individuals. The federal EITC without a + qualifying child now also requires that neither spouse can be claimed. - Only single filers: New York's dependent standard deduction. - Each spouse separately: Michigan's exemptions and Hawaii's food/excise, renters and Act 115 credits count only the filers who cannot be claimed. - Only when every filer is a dependent: New Mexico's rebates, credits and - dependents deduction, and Maine's sales tax fairness credit. -- Never on a joint return: Missouri's working family credit, since such a - couple files as married filing combined. + dependents deduction, and Maine's sales tax fairness credit. New Mexico's + medical care credit also needs a filer who is both 65 or older and not a + dependent. +- Never on a joint return: Missouri's own stop in its working family credit, + since such a couple files as married filing combined. Hypothesis draws batches of married couples, with and without dependents, in the eight states with such a rule and one without, for 2021-2026, with either, -both or neither spouse claimed elsewhere. A seeded population and crafted -cases add breadth in every year, and two seeded couples plus the review case -in every state guard 2026. Each batch is one vectorized simulation. For each couple: +both or neither spouse claimed elsewhere, and checks 1 and 3. A seeded +population and crafted cases check 1, 2 and 3 in every year, and two seeded +couples plus the review case in every state check 1 for federal and state +income tax in 2026. Each batch is one vectorized simulation. For each couple: 1. Swap invariance: federal income tax, state income tax before and after - refundable credits, and each of the eighteen formulas are the same to the - cent (or the same boolean) under either labelling. + refundable credits, and each formula in CONSUMERS are the same to the cent + (or the same boolean) under either labelling. 2. Monotonicity: marking another filer as claimed elsewhere never raises any - of the eighteen amounts or turns an eligibility on. + amount in CONSUMERS or turns an eligibility on. This holds for current + parameters and inputs the model derives; for example, a Hawaii standard + deduction input below the itemization threshold could break it. 3. The helper identities: `head_or_spouse_is_dependent_elsewhere` equals `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, `every_filer_is_dependent_elsewhere` equals their conjunction for a @@ -59,6 +65,7 @@ # The formulas that read whether a filer is claimed elsewhere. CONSUMERS = [ "basic_standard_deduction", + "eitc", "ny_standard_deduction", "mi_exemptions", "or_standard_deduction", @@ -73,6 +80,7 @@ "me_sales_tax_fairness_credit_eligible", "hi_deductions", "hi_act_115_rebate", + "hi_food_excise_exemption_amount", "hi_food_excise_credit", "hi_tax_credit_for_low_income_household_renters_eligible", "hi_tax_credit_for_low_income_household_renters", diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml index ae065070ccc..80b379d010b 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml @@ -127,3 +127,133 @@ is_full_time_student: true output: eitc_demographic_eligible: false + +- name: 2025 childless single filer who can be claimed as a dependent is ineligible + period: 2025 + input: + people: + head: + age: 30 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + output: + # IRC 32(c)(1)(A)(ii)(III). + eitc_demographic_eligible: false + +- name: 2025 childless joint return where one spouse can be claimed is ineligible (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: CA + output: + # Publication 596 (2025, p.18): "neither you nor your spouse can be claimed as a + # dependent by another person if you claim the EIC on your joint return." + eitc_demographic_eligible: false + +- name: 2025 childless joint return where one spouse can be claimed is ineligible (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: CA + output: + # Publication 596 (2025, p.18): "neither you nor your spouse can be claimed as a + # dependent by another person if you claim the EIC on your joint return." + eitc_demographic_eligible: false + +- name: 2025 joint return with a child where one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child_1] + state_code: CA + output: + # The dependent condition in IRC 32(c)(1)(A)(ii)(III) applies only without a + # qualifying child. + eitc_demographic_eligible: true + +- name: 2025 joint return with a child where one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child_1] + state_code: CA + output: + # The dependent condition in IRC 32(c)(1)(A)(ii)(III) applies only without a + # qualifying child. + eitc_demographic_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml index e340975fe5c..eaf2ffad6cd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml @@ -331,3 +331,133 @@ state_code: HI output: hi_food_excise_credit: 0 + +- name: 2022 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # The spouse who cannot be claimed: $110; HRS 235-55.85(c) lets the minor + # receiving public support count for this credit: $110. + hi_food_excise_credit: 220 + +- name: 2022 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # The spouse who cannot be claimed: $110; HRS 235-55.85(c) lets the minor + # receiving public support count for this credit: $110. + hi_food_excise_credit: 220 + +- name: 2025 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # From 2023 the minor counts as a qualified exemption: 2 x $220. + hi_food_excise_credit: 440 + +- name: 2025 joint return with a publicly supported minor where only one spouse can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # From 2023 the minor counts as a qualified exemption: 2 x $220. + hi_food_excise_credit: 440 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml index 377d8edd769..584d987cbe7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml @@ -195,3 +195,82 @@ output: # HRS 235-2.4(a)(3): greater of $500 or earned income ($300). hi_deductions: 500 + +- name: 2025 single dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + hi_itemized_deductions: 0 + households: + household: + members: [head] + state_code: HI + output: + # N-11 worksheet earned income subtracts Schedule 1 line 15: + # $3,000 x 0.9235 x 15.3% / 2 = $211.94, so $3,000 - $211.94 = $2,788.06. + hi_deductions: 2_788.06 + +- name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + hi_itemized_deductions: 0 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06, below the $8,800 joint amount. + hi_deductions: 2788.06 + +- name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + hi_itemized_deductions: 0 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06, below the $8,800 joint amount. + hi_deductions: 2788.06 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml index 6cb8a3e48c6..0cea1d97f45 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc_eligible.yaml @@ -189,3 +189,65 @@ # A joint federal filer whose spouse can be claimed still files Form MO-1040 # as married filing combined, not as claimed as a dependent. mo_wftc_eligible: true + +- name: 2025 childless couple with a claimable student spouse gets no federal EITC and so no WFTC (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + is_full_time_student: true + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: MO + output: + # Without a qualifying child, neither spouse may be claimable for the federal + # EITC (Publication 596), which RSMo 143.177 requires. + eitc: 0 + mo_wftc_eligible: false + +- name: 2025 childless couple with a claimable student spouse gets no federal EITC and so no WFTC (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 8_000 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + is_full_time_student: true + employment_income: 8_000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: MO + output: + # Without a qualifying child, neither spouse may be claimable for the federal + # EITC (Publication 596), which RSMo 143.177 requires. + eitc: 0 + mo_wftc_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml index 518854ff24e..f8563d24439 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/credits/nm_medical_expense_credit.yaml @@ -143,3 +143,109 @@ state_code: NM output: nm_medical_expense_credit: 0 + +- name: Joint return where only the claimable spouse is 65 or older (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 60 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # NMSA 7-2-18.13(A): the taxpayer who is 65 or older must also not be a dependent. + nm_medical_expense_credit: 0 + +- name: Joint return where only the claimable spouse is 65 or older (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 60 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + # NMSA 7-2-18.13(A): the taxpayer who is 65 or older must also not be a dependent. + nm_medical_expense_credit: 0 + +- name: Joint return where the spouse who is not claimable is 65 or older (the claimable spouse is the head) + period: 2023 + input: + people: + person_a: + age: 60 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 70 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + nm_medical_expense_credit: 2_800 + +- name: Joint return where the spouse who is not claimable is 65 or older (the claimable spouse is the spouse) + period: 2023 + input: + people: + person_b: + age: 70 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 60 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + itemized_medical_expenses: 28_000 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + nm_medical_expense_credit: 2_800 diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py index 2338356763e..053ca2799c0 100644 --- a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py @@ -6,7 +6,10 @@ class eitc_demographic_eligible(Variable): entity = TaxUnit label = "Meets demographic eligibility for EITC" definition_period = YEAR - reference = "https://www.law.cornell.edu/uscode/text/26/32#c_1_A_ii_II" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/32#c_1_A_ii", + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + ) def formula(tax_unit, period, parameters): person = tax_unit.members @@ -27,4 +30,13 @@ def formula(tax_unit, period, parameters): # IRC § 32(c)(1)(A)(ii)(II) applies the age test to the filer or, # on a joint return, either spouse — never to dependents. is_filer_or_spouse = ~person("is_tax_unit_dependent", period) - return has_child | tax_unit.any(meets_age_requirements & is_filer_or_spouse) + # IRC § 32(c)(1)(A)(ii)(III): without a qualifying child, the filer + # must not be a dependent of another taxpayer; on a joint return, + # "neither you nor your spouse can be claimed as a dependent by another + # person" (IRS Publication 596). + dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + childless_eligible = ( + tax_unit.any(meets_age_requirements & is_filer_or_spouse) + & ~dependent_elsewhere + ) + return has_child | childless_eligible diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py index a98403539f7..220561807b8 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py @@ -15,13 +15,14 @@ class hi_food_excise_credit(Variable): def formula(tax_unit, period, parameters): # HRS 235-55.85(a): only a taxpayer who cannot be claimed as a - # dependent may claim the credit, for each qualified exemption. The + # dependent may claim the credit, for each qualified exemption; the # exemption amount counts only filers who cannot be claimed when - # either can; such a return claims no dependents (IRS Publication - # 501), so it gets no minor child amount. - dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # either can. Minor children receiving public support count for this + # credit under 235-55.85(c), so they keep their amount unless every + # filer can be claimed. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) exemption_amount = tax_unit("hi_food_excise_exemption_amount", period) minor_child_amount = tax_unit( "hi_food_excise_credit_minor_child_amount", period ) - return exemption_amount + where(dependent_filer, 0, minor_child_amount) + return exemption_amount + where(every_filer_dependent, 0, minor_child_amount) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py index d11bb0a6cac..c597ba1352b 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py @@ -44,21 +44,29 @@ def formula(tax_unit, period, parameters): ], ) exemptions = tax_unit("exemptions_count", period) + minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) if p.minor_child.in_effect: - # Reduce number of exemptions by the number of minor children - minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) + # Reduce number of exemptions by the number of minor children, + # who get a fixed amount instead. claimable_exemptions = exemptions - minor_children + minor_child_exemptions = 0 else: claimable_exemptions = exemptions + minor_child_exemptions = minor_children # Form N-311 counts only people who cannot be claimed as a dependent - # by another taxpayer. A return on which a filer can be claimed claims - # no dependents (IRS Publication 501), so it counts only the filers - # who cannot be claimed. + # by another taxpayer. A return on which a filer can be claimed + # generally claims no dependents (IRS Publication 501; its exception + # for a claimer who files only for a refund is not modeled), so it + # counts the filers who cannot be claimed and, through HRS + # 235-55.85(c), their minor children receiving public support. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) + mixed_return_exemptions = independent_filers + where( + independent_filers > 0, minor_child_exemptions, 0 + ) qualified_exemptions = where( - dependent_filer, independent_filers, claimable_exemptions + dependent_filer, mixed_return_exemptions, claimable_exemptions ) return qualified_exemptions * amount_per_exemption diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py index 3a229d50ebb..734e6305b38 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py @@ -43,9 +43,10 @@ def formula(tax_unit, period, parameters): # not a qualifying resident taxpayer, and each qualifying resident # taxpayer claims the refund for the exemptions they are entitled to. # A filer who can be claimed has no exemption (HRS 235-54(a)), and a - # return on which a filer can be claimed claims no dependents (IRS - # Publication 501), so such a return counts only the filers who - # cannot be claimed. + # return on which a filer can be claimed generally claims no dependents + # (IRS Publication 501; its exception for a claimer who files only for + # a refund is not modeled), so such a return counts only the filers + # who cannot be claimed. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py index 81a87e2876e..f60088206d8 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py @@ -19,8 +19,9 @@ def formula(tax_unit, period, parameters): # Schedule X lines 8-10 count only people who cannot be claimed as a # dependent by another taxpayer, with an extra exemption for each # such filer aged 65 or over. A return on which a filer can be - # claimed claims no dependents (IRS Publication 501), so it counts - # only the filers who cannot be claimed. + # claimed generally claims no dependents (IRS Publication 501; its + # exception for a claimer who files only for a refund is not modeled), + # so it counts only the filers who cannot be claimed. aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold) & ~tax_unit( "head_is_dependent_elsewhere", period ) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py index 70a783508dd..ee104646ba4 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py @@ -37,8 +37,13 @@ def formula(tax_unit, period, parameters): is_dependent_on_another_return = tax_unit( "head_or_spouse_is_dependent_elsewhere", period ) + # Worksheet earned income: wages and self-employment income minus the + # deductible part of self-employment tax (Schedule 1, line 15). + worksheet_earned_income = max_( + tax_unit_earned_income - tax_unit("self_employment_tax_ald", period), 0 + ) dependent_standard_deduction = min_( - max_(p.threshold.dependent, tax_unit_earned_income), standard_deduction + max_(p.threshold.dependent, worksheet_earned_income), standard_deduction ) applicable_standard_deduction = where( is_dependent_on_another_return, diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py index 6d8e7aac8dd..cf13c6de8ea 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/fairness/sales_tax_fairness_credit/me_sales_tax_fairness_credit_eligible.py @@ -15,9 +15,9 @@ class me_sales_tax_fairness_credit_eligible(Variable): def formula(tax_unit, period, parameters): # 36 MRSA 5213-A(6)(C) excludes individuals who may be claimed as a # dependent on another return, and subsection 2 allows each resident - # individual the base credit for the return's filing status. On a - # joint return where only one spouse can be claimed, the other spouse - # still qualifies for the joint credit. + # individual the base credit for the return's filing status. Neither + # settles a joint return where only one spouse can be claimed; we read + # them as letting the other spouse qualify for the joint credit. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py index 1c77fb504ce..1940d6d1af8 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py @@ -35,11 +35,13 @@ def formula(tax_unit, period, parameters): # MCL 206.30(4): an individual who can be claimed as a dependent on # another return gets no personal exemption but subtracts $1,500. - # This applies to each spouse separately, so on a joint return where - # only one spouse can be claimed, the other keeps their own personal - # exemption (and any stillbirth exemption). A joint return on which - # either spouse can be claimed claims no dependents (IRS Publication - # 501, "Dependent Taxpayer Test"). + # Treasury's instructions do not address a joint return where only + # one spouse can be claimed; since subsection (4) speaks of an + # individual, we apply it to each spouse, so the other keeps their own + # personal exemption (and any stillbirth exemption). A joint return on + # which either spouse can be claimed generally claims no dependents + # (IRS Publication 501, "Dependent Taxpayer Test"; its exception for a + # claimer who files only for a refund is not modeled). filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py index 27585ed2568..1ee85c8bfae 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py @@ -18,15 +18,21 @@ def formula(tax_unit, period, parameters): p = pcredits.unreimbursed_medical_care_expense age = person("age", period) medical_expense = tax_unit("itemized_medical_expenses", period) - age_eligible = tax_unit.any(age >= p.age_eligibility) + aged = age >= p.age_eligibility expense_eligible = medical_expense >= p.min_expenses - # The rebate and credit schedule bars a filer who is a dependent of - # another taxpayer, but "If you are a dependent with a spouse who was - # not a dependent of another taxpayer, your spouse may still qualify - # to claim rebates or credits" (PIT-RC instructions). So only a return - # on which every filer is a dependent elsewhere is barred. - every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) - eligible = age_eligible & expense_eligible & ~every_filer_dependent + # NMSA 7-2-18.13(A) allows the credit to a taxpayer "who is sixty-five + # years of age or older and who is not a dependent of another + # taxpayer", and the PIT-RC instructions let a spouse who is not a + # dependent claim it when the other spouse is. So when a filer is a + # dependent elsewhere, a filer who is not must be 65 or older. + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimed_as_dependent_on_another_return", period) + independent_aged_filer = tax_unit.any(filer & ~claimed & aged) + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + age_eligible = where( + dependent_filer, independent_aged_filer, tax_unit.any(aged) + ) + eligible = age_eligible & expense_eligible # exemption is halved for married filing separately filing_status = tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py index 389227abe70..c083077aebd 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/deductions/certain_dependents/nm_deduction_for_certain_dependents_eligible.py @@ -14,10 +14,11 @@ class nm_deduction_for_certain_dependents_eligible(Variable): def formula(tax_unit, period, parameters): # The deduction uses the same "not a dependent of another individual" - # test as the rebates and credits on Schedule PIT-RC, whose - # instructions let a spouse who is not a dependent still qualify. So - # only a return on which every filer is a dependent elsewhere is - # barred. + # test as the rebates and credits on Schedule PIT-RC, and the law does + # not settle a joint return where only one spouse is a dependent. We + # apply the PIT-RC instructions' reading, which lets a spouse who is + # not a dependent still qualify, so only a return on which every + # filer is a dependent elsewhere is barred. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) # deduction does not apply if an exemption under IRS 151 is claimed; # IRC 151 refers to the federal personal exemption diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py index c49035340e2..0dab3d96509 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_2021_income_rebate.py @@ -14,11 +14,14 @@ class nm_2021_income_rebate(Variable): defined_for = StateCode.NM def formula(tax_unit, period, parameters): - # The rebate and credit schedule bars a filer who is a dependent of - # another taxpayer, but "If you are a dependent with a spouse who was - # not a dependent of another taxpayer, your spouse may still qualify - # to claim rebates or credits" (PIT-RC instructions). So only a return - # on which every filer is a dependent elsewhere is barred. + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) agi = tax_unit("adjusted_gross_income", period) filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py index f574c92dbab..78493990ef0 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_additional_2021_income_rebate.py @@ -14,11 +14,14 @@ class nm_additional_2021_income_rebate(Variable): defined_for = StateCode.NM def formula(tax_unit, period, parameters): - # The rebate and credit schedule bars a filer who is a dependent of - # another taxpayer, but "If you are a dependent with a spouse who was - # not a dependent of another taxpayer, your spouse may still qualify - # to claim rebates or credits" (PIT-RC instructions). So only a return - # on which every filer is a dependent elsewhere is barred. + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py index bf8f1a88d52..cd82e30a49b 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/2021_rebate/nm_supplemental_2021_income_rebate.py @@ -14,11 +14,14 @@ class nm_supplemental_2021_income_rebate(Variable): defined_for = StateCode.NM def formula(tax_unit, period, parameters): - # The rebate and credit schedule bars a filer who is a dependent of - # another taxpayer, but "If you are a dependent with a spouse who was - # not a dependent of another taxpayer, your spouse may still qualify - # to claim rebates or credits" (PIT-RC instructions). So only a return - # on which every filer is a dependent elsewhere is barred. + # The rebate goes to "a resident ... who is not a dependent of another + # individual", and the law does not settle a joint return where only + # one spouse is. For the same phrase on its rebate and credit schedule, + # TRD says "If you are a dependent with a spouse who was not a + # dependent of another taxpayer, your spouse may still qualify to + # claim rebates or credits" (PIT-RC instructions); we apply that + # reading, so only a return on which every filer is a dependent + # elsewhere is barred. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.nm.tax.income.rebates["2021_income"] filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py index 7b746711ad1..58934026c25 100644 --- a/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/va/tax/income/credits/low_income_credit/va_low_income_tax_credit_eligible.py @@ -18,10 +18,11 @@ def formula(tax_unit, period, parameters): program_eligible = add(tax_unit, period, p.ineligible_programs) == 0 # Criteria 2: Not qualified if a filer is claimed as a dependent on - # another taxpayer's return. Va. Code 58.1-339.8 bars the credit - # against the tax of a dependent of another individual or couple; a - # joint return carries both spouses' tax, so either spouse being - # claimed bars it. + # another taxpayer's return. Neither Va. Code 58.1-339.8 nor the + # instructions settle a joint return where only one spouse is + # claimed. We read the statute's bar on the credit "against such tax + # of a dependent" as reaching a joint return, which carries both + # spouses' tax, so either spouse being claimed bars it. filer_is_dependent_elsewhere = tax_unit( "head_or_spouse_is_dependent_elsewhere", period ) From e8386234d207195fbcc487daa650d584e25a83e2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 10:00:44 -0400 Subject: [PATCH 04/42] Allow a cent of rounding in the Hawaii self-employed joint pair Co-Authored-By: Claude Opus 5.5 --- .../gov/states/hi/tax/income/deductions/hi_deductions.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml index 584d987cbe7..56eb95f2a84 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/deductions/hi_deductions.yaml @@ -221,6 +221,7 @@ - name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the head) period: 2025 + absolute_error_margin: 0.01 input: people: person_a: @@ -249,6 +250,7 @@ - name: 2025 joint return with a claimable self-employed spouse uses earned income net of the self-employment tax deduction (the claimable spouse is the spouse) period: 2025 + absolute_error_margin: 0.01 input: people: person_b: From 41fb4a55471a77d8a4fd7508d5cd177d802557fd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 10:25:58 -0400 Subject: [PATCH 05/42] Address review round 2: worksheet earnings, minors' own status, input meaning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The federal and Oregon dependent worksheets use earned income net of the deductible part of self-employment tax, as Hawaii's now does. - Hawaii's food/excise minor-child count excludes children who can be claimed by another taxpayer and the filers themselves (Form N-311 line 3). - claimed_as_dependent_on_another_return documents what it stands for: a §151 deduction for the person is allowable to another taxpayer, so a person whose would-be claimer files only for a refund is not one (Publications 501 and 596); rules on actually being claimed read it too. - Hawaii's renters comment labels the mixed joint reading as an inference; the Hawaii dependent threshold cites HRS 235-2.4(a)(3) on its page. Co-Authored-By: Claude Opus 5.5 --- .../itemized/threshold/dependent.yaml | 2 +- ...t_elsewhere_head_spouse_swap_invariance.py | 6 +- .../eitc_demographic_eligible.yaml | 21 +++ .../standard/basic_standard_deduction.yaml | 22 +++ .../food_excise/hi_food_excise_credit.yaml | 134 ++++++++++++++++++ .../standard/or_standard_deduction.yaml | 22 +++ .../basic_standard_deduction.py | 10 +- ...hi_food_excise_credit_minor_child_count.py | 9 +- ...r_low_income_household_renters_eligible.py | 8 +- .../deductions/or_standard_deduction.py | 10 +- .../claimed_as_dependent_on_another_return.py | 10 +- 11 files changed, 241 insertions(+), 13 deletions(-) diff --git a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml index 3ff2ac9a451..36300fde6de 100644 --- a/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml +++ b/policyengine_us/parameters/gov/states/hi/tax/income/deductions/itemized/threshold/dependent.yaml @@ -16,6 +16,6 @@ metadata: - title: 2018 Hawaii Resident Income Tax Instructions Itemized Deductions Worksheet href: https://files.hawaii.gov/tax/forms/2018/n11ins.pdf#page=38 - title: Hawaii Income Tax Law, Chapter 235, Section 235-2.4, (a)(3) - href: https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=10 + href: https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11 - title: 2025 Hawaii N-11 Instructions Itemized Deductions href: https://files.hawaii.gov/tax/forms/current/n11ins.pdf#page=32 diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py index d64c56ff5ba..4e681240eb5 100644 --- a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -18,9 +18,9 @@ - Each spouse separately: Michigan's exemptions and Hawaii's food/excise, renters and Act 115 credits count only the filers who cannot be claimed. - Only when every filer is a dependent: New Mexico's rebates, credits and - dependents deduction, and Maine's sales tax fairness credit. New Mexico's - medical care credit also needs a filer who is both 65 or older and not a - dependent. + dependents deduction, and Maine's sales tax fairness credit. When a filer + is a dependent elsewhere, New Mexico's medical care credit also needs a + filer who is both 65 or older and not a dependent. - Never on a joint return: Missouri's own stop in its working family credit, since such a couple files as married filing combined. diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml index 80b379d010b..b922b61cb47 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml @@ -257,3 +257,24 @@ # The dependent condition in IRC 32(c)(1)(A)(ii)(III) applies only without a # qualifying child. eitc_demographic_eligible: true + +- name: 2025 childless single filer whose parent files only to claim a refund is not a dependent + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Publication 596 (2025, p.18): the credit is allowed when the person who can claim + # you files only to claim a refund; such a person is not a dependent, so the + # input is false. + eitc_demographic_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml index b9a821293a5..4099214070c 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/deductions/standard/basic_standard_deduction.yaml @@ -263,3 +263,25 @@ output: # Joint earned income $16,000 + $450 = $16,450, below the $31,500 joint amount. basic_standard_deduction: 16_450 + +- name: 2025 dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 3_000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: CA + output: + # Worksheet earned income subtracts Schedule 1 line 15: + # $3,000 - $3,000 x 0.9235 x 15.3% / 2 = $2,788.06; plus $450 = $3,238.06. + basic_standard_deduction: 3238.06 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml index eaf2ffad6cd..260651381eb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml @@ -461,3 +461,137 @@ output: # From 2023 the minor counts as a qualified exemption: 2 x $220. hi_food_excise_credit: 440 + +- name: 2022 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the head) + period: 2022 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Form N-311 line 3 lists only minors who cannot be claimed by another taxpayer, + # so only the spouse who cannot be claimed counts: $110. + hi_food_excise_credit: 110 + +- name: 2022 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the spouse) + period: 2022 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Form N-311 line 3 lists only minors who cannot be claimed by another taxpayer, + # so only the spouse who cannot be claimed counts: $110. + hi_food_excise_credit: 110 + +- name: 2025 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed counts: 1 x $220. + hi_food_excise_credit: 220 + +- name: 2025 joint return where one spouse and the publicly supported minor can be claimed elsewhere (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4_000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + # Only the spouse who cannot be claimed counts: 1 x $220. + hi_food_excise_credit: 220 diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml index 7818ce120d2..c040dee6619 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/or/tax/income/deductions/standard/or_standard_deduction.yaml @@ -191,3 +191,25 @@ output: # max($1,350, $3,000 + $450) = $3,450, below the $5,670 joint amount. or_standard_deduction: 3_450 + +- name: 2025 dependent filer with self-employment income uses earned income net of the self-employment tax deduction + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 20 + self_employment_income: 1_500 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head] + filing_status: SINGLE + households: + household: + members: [head] + state_code: OR + output: + # $1,500 - $1,500 x 0.9235 x 15.3% / 2 = $1,394.03; plus $450 = $1,844.03, + # above the $1,350 minimum and below the $2,835 single amount. + or_standard_deduction: 1844.03 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py index e8575cdcc7e..952da61a05f 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/standard_deduction/basic_standard_deduction.py @@ -25,11 +25,17 @@ def formula(tax_unit, period, parameters): # can be claimed. dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) standard_deduction = p.amount[filing_status] + # Worksheet earned income: wages and self-employment income minus the + # deductible part of self-employment tax (Schedule 1, line 15). + worksheet_earned_income = max_( + tax_unit("tax_unit_earned_income", period) + - tax_unit("self_employment_tax_ald", period), + 0, + ) standard_deduction_if_dependent = min_( standard_deduction, max_( - p.dependent.additional_earned_income - + tax_unit("tax_unit_earned_income", period), + p.dependent.additional_earned_income + worksheet_earned_income, p.dependent.amount, ), ) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py index ba06f9635a3..c4cc41a8d42 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py @@ -16,6 +16,13 @@ def formula(tax_unit, period, parameters): ) is_child = person("is_child", period) minor = person("age", period) < p.minor_child.age_threshold - eligible_minor_child = is_child & minor & public_support_over_half + # Form N-311 line 3 lists minor children who, among other tests, + # "Cannot be claimed as a dependent by another taxpayer"; the filers + # are listed on line 2. + claimed_elsewhere = person("claimed_as_dependent_on_another_return", period) + filer = person("is_tax_unit_head_or_spouse", period) + eligible_minor_child = ( + is_child & minor & public_support_over_half & ~claimed_elsewhere & ~filer + ) return tax_unit.sum(eligible_minor_child) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py index 61f4c81eaf2..e25924271ee 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_tax_credit_for_low_income_household_renters_eligible.py @@ -15,9 +15,11 @@ class hi_tax_credit_for_low_income_household_renters_eligible(Variable): def formula(tax_unit, period, parameters): # HRS 235-55.7(b) allows the credit to each resident taxpayer who - # cannot be claimed as a dependent. Schedule X stops at line 3 only - # for the first-listed filer; since a couple may list either spouse - # first, a joint return qualifies unless both spouses can be claimed. + # cannot be claimed as a dependent, and Schedule X stops at line 3 if + # "you" can be claimed. Neither settles a joint return where only one + # spouse can be claimed; since the credit and its exemption count are + # per person, we read them as letting the other spouse claim it, so a + # joint return qualifies unless both spouses can be claimed. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) p = parameters(period).gov.states.hi.tax.income.credits.lihrtc agi = tax_unit("hi_agi", period) diff --git a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py index eb3c543e6fa..54d2dc18fbc 100644 --- a/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py +++ b/policyengine_us/variables/gov/states/or/tax/income/deductions/or_standard_deduction.py @@ -20,8 +20,14 @@ def formula(tax_unit, period, parameters): # Core deduction based on filing status. filing_status = tax_unit("filing_status", period) initial_core_deduction = p.amount[filing_status] - # Replace if claimable as a dependent. - earned_income = tax_unit("tax_unit_earned_income", period) + # Replace if claimable as a dependent. Earned income is figured as in + # the federal worksheet, net of the deductible part of self-employment + # tax (Schedule 1, line 15). + earned_income = max_( + tax_unit("tax_unit_earned_income", period) + - tax_unit("self_employment_tax_ald", period), + 0, + ) claimable_dep_floor = p.claimable_as_dependent.min claimable_dep_earned_amount = ( earned_income + p.claimable_as_dependent.earned_income_addition diff --git a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py index a62ae0c4703..f941fcb3254 100644 --- a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py +++ b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py @@ -6,4 +6,12 @@ class claimed_as_dependent_on_another_return(Variable): entity = Person definition_period = YEAR label = "Is claimed as a dependent elsewhere" - documentation = "Whether the person is claimed as a dependent in another tax unit." + documentation = ( + "Whether another taxpayer can claim the person as a dependent, that " + "is, a deduction under IRC section 151 for the person is allowable to " + "another taxpayer, whether or not it is claimed. A person whose " + "would-be claimer is not required to file and files only to claim a " + "refund is not such a dependent (IRS Publications 501 and 596). Rules " + "that turn on actually being claimed also read this input; the model " + "has no separate input for that." + ) From 436805e3e0c3d386e66016e54f69967d37be4a1b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 10:27:15 -0400 Subject: [PATCH 06/42] Merge the Hawaii deductions references after syncing main Co-Authored-By: Claude Opus 5.5 --- .../gov/states/hi/tax/income/deductions/hi_deductions.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py index 646fee7433c..fe50a345a3c 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/deductions/hi_deductions.py @@ -6,12 +6,10 @@ class hi_deductions(Variable): entity = TaxUnit label = "Hawaii deductions" unit = USD - reference = ( - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15", # Itemized Deduction - "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=20", # Standard Deduction - ) reference = ( "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=11", + # PDF pages 15 (itemized deduction), 20 (standard deduction) + "https://files.hawaii.gov/tax/forms/2022/n11ins.pdf#page=15", # PDF pages 15, 20 "https://files.hawaii.gov/tax/forms/2025/n11ins.pdf#page=15", ) From 23c6229b2004fdc2a8a370c94399eae50dcaccfe Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 9 Oct 2026 12:03:39 -0400 Subject: [PATCH 07/42] Apply the federal rules for a filer who can be claimed as a dependent A filer who can be claimed on another return is treated as having no dependents (IRC 152(b)(1); on a joint return either spouse counts) and has no personal exemption (IRC 151(d)(2)); one who is claimed gets no education credits (25A(g)(3)), student loan interest deduction (221(c)) or recovery rebate (6428(d)(2), 6428A(d)(2), 6428B(c)(2)). The model read none of these. Now the child tax credit and credit for other dependents, the under-13 child and dependent care qualifying person, exemptions_count and its RI, DE, OH, OR, WI, HI and NM readers, the recovery rebates, the education credits and the student loan interest deduction apply them symmetrically for joint returns. Georgia keeps its own counts; the HI, WI and Treasury exemption reforms follow the corrected count. Co-Authored-By: Claude Opus 5.5 --- changelog.d/claimable-filer-federal.fixed.md | 1 + .../hi_dependent_exemption_reform.py | 24 ++- .../wi_dependent_exemption_reform.py | 23 ++- .../treasury/repeal_dependent_exemptions.py | 10 +- ...test_claimable_filer_federal_invariants.py | 153 ++++++++++++++++++ .../irs/credits/cdcc/is_cdcc_eligible.yaml | 96 +++++++++++ .../ctc/maximum/ctc_qualifying_child.yaml | 103 ++++++++++++ .../ctc_adult_individual_maximum.yaml | 93 +++++++++++ ...gible_for_american_opportunity_credit.yaml | 130 +++++++++++++++ ...eligible_for_lifetime_learning_credit.yaml | 107 ++++++++++++ .../recovery_rebate_credit.yaml | 112 +++++++++++++ .../student_loan_interest_ald_eligible.yaml | 69 ++++++++ .../exemptions/exemptions_count.yaml | 133 +++++++++++++++ .../personal/de_personal_credit_indv.yaml | 51 ++++++ .../credits/personal/de_personal_credits.yaml | 55 +++++++ .../states/ga/tax/income/credits/ga_ctc.yaml | 30 +++- .../income/credits/ga_low_income_credit.yaml | 16 +- .../nm_low_and_middle_income_exemption.yaml | 99 ++++++++++++ ...m_low_income_comprehensive_tax_rebate.yaml | 80 +++++++++ ...e_comprehensive_tax_rebate_exemptions.yaml | 101 ++++++++++++ .../oh_personal_exemptions_eligible.yaml | 54 +++++++ .../tax/income/exemption/ri_exemptions.yaml | 79 +++++++++ .../wi/tax/income/wi_base_exemption.yaml | 76 +++++++++ .../gov/irs/credits/cdcc/cdcc_eligible.py | 17 +- .../ctc/maximum/ctc_qualifying_child.py | 18 ++- .../ctc_adult_individual_maximum.py | 8 + ...ligible_for_american_opportunity_credit.py | 6 + ...s_eligible_for_lifetime_learning_credit.py | 6 + .../rrc_adult_count_with_valid_ssn.py | 8 +- .../rrc_arpa_dependents_with_valid_ssn.py | 8 +- .../student_loan_interest_ald_eligible.py | 8 +- .../exemptions/exemptions_count.py | 28 +++- .../personal/de_personal_credit_indv.py | 25 ++- .../income/credits/ctc/ga_ctc_potential.py | 12 +- .../credits/ga_low_income_credit_potential.py | 5 +- .../nm_low_and_middle_income_exemption.py | 3 + .../nm_low_income_comprehensive_tax_rebate.py | 8 +- ...ome_comprehensive_tax_rebate_exemptions.py | 6 + .../oh_personal_exemptions_eligible_person.py | 9 +- 39 files changed, 1820 insertions(+), 50 deletions(-) create mode 100644 changelog.d/claimable-filer-federal.fixed.md create mode 100644 policyengine_us/tests/core/test_claimable_filer_federal_invariants.py create mode 100644 policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml diff --git a/changelog.d/claimable-filer-federal.fixed.md b/changelog.d/claimable-filer-federal.fixed.md new file mode 100644 index 00000000000..545d474e1a6 --- /dev/null +++ b/changelog.d/claimable-filer-federal.fixed.md @@ -0,0 +1 @@ +Apply the federal rules for a filer who can be claimed as a dependent on another return: no child tax credit, credit for other dependents or under-13 care qualifying child on such a return (IRC 24, 21, 152(b)(1)), no personal exemption for that filer (IRC 151(d)(2)) in the exemption count Rhode Island, Delaware, Ohio, Wisconsin, Oregon, Hawaii and New Mexico read, no recovery rebate for that filer, and no education credits or student loan interest deduction on a return with a claimed filer (IRC 25A(g)(3), 221(c)). diff --git a/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py b/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py index c2ec738773b..3349f9a9f31 100644 --- a/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py @@ -15,7 +15,11 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) - is_dependent = person("is_tax_unit_dependent", period) + # A return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents (IRC 152(b)(1)). + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) # Apply age limit if in effect if p.age_limit.in_effect: @@ -49,7 +53,11 @@ class hi_older_dependents_count(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # A return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents (IRC 152(b)(1)). + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( "hi_eligible_dependents_count", period @@ -66,14 +74,16 @@ class hi_regular_exemptions(Variable): defined_for = StateCode.HI def formula(tax_unit, period, parameters): - exemptions_count = tax_unit("exemptions_count", period) p = parameters(period).gov.states.hi.tax.income.exemptions - # Personal exemptions exclude the dependent portion. Over-age - # dependents fall back to the personal count. - dependents = tax_unit("tax_unit_dependents", period) + # Personal exemptions exclude the dependent portion: the filers' + # own exemptions (none for a filer who can be claimed as a + # dependent). Over-age dependents fall back to the personal count. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) older_dependents = tax_unit("hi_older_dependents_count", period) - personal_count = exemptions_count - dependents + older_dependents + personal_count = own_exemptions + older_dependents # Aged heads and spouses get an extra base exemption (preserved # exactly as the baseline computes it). diff --git a/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py b/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py index cf44d6a86f2..85e40eefe7f 100644 --- a/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py @@ -15,7 +15,11 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) - is_dependent = person("is_tax_unit_dependent", period) + # A return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents (IRC 152(b)(1)). + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) # Apply age limit if in effect if p.age_limit.in_effect: @@ -49,7 +53,11 @@ class wi_older_dependents_count(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # A return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents (IRC 152(b)(1)). + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( "wi_eligible_dependents_count", period @@ -68,12 +76,13 @@ def formula(tax_unit, period, parameters): p_base = parameters(period).gov.states.wi.tax.income # Personal exemptions exclude the dependent portion. - total_exemptions_count = tax_unit("exemptions_count", period) - dependents = tax_unit("tax_unit_dependents", period) - older_dependents = tax_unit("wi_older_dependents_count", period) - personal_exemptions_count = ( - total_exemptions_count - dependents + older_dependents + # These are the filers' own exemptions (none for a filer who can + # be claimed as a dependent) plus over-age dependents. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period ) + older_dependents = tax_unit("wi_older_dependents_count", period) + personal_exemptions_count = own_exemptions + older_dependents personal_exemption_amount = ( personal_exemptions_count * p_base.exemption.base ) diff --git a/policyengine_us/reforms/treasury/repeal_dependent_exemptions.py b/policyengine_us/reforms/treasury/repeal_dependent_exemptions.py index ab54afe5e89..fbfd6b94c1e 100644 --- a/policyengine_us/reforms/treasury/repeal_dependent_exemptions.py +++ b/policyengine_us/reforms/treasury/repeal_dependent_exemptions.py @@ -13,7 +13,15 @@ class exemptions_count(Variable): def formula(tax_unit, period, parameters): total_unit_size = tax_unit("tax_unit_size", period) dependents = tax_unit("tax_unit_dependents", period) - return total_unit_size - dependents + # Only the filers' own exemptions remain; a filer who can be + # claimed as a dependent has none (IRC 151(d)(2)). + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return where( + dependent_filer, independent_filers, total_unit_size - dependents + ) class reform(Reform): def apply(self): diff --git a/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py b/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py new file mode 100644 index 00000000000..58eb58887b4 --- /dev/null +++ b/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py @@ -0,0 +1,153 @@ +"""Federal rules for a filer who can be claimed as a dependent elsewhere. + +A filer who can be claimed as a dependent on another return is treated as +having no dependents (IRC 152(b)(1); on a joint return, if either spouse can be +claimed, "you and your spouse can't claim any dependents", Publication 501) and +has no personal exemption (IRC 151(d)(2)). So: + +- `exemptions_count` counts only the filers who cannot be claimed, plus the + dependents when no filer can be claimed; +- `ctc_qualifying_child` and the credit for other dependents are zero on a + return with a claimable filer; +- a child under 13 is not a child and dependent care qualifying person on such + a return (a disabled person still is, IRC 21(b)(1)(B)); +- recovery rebates leave out a claimable filer and, on such a return, the + dependents. + +For couples drawn by Hypothesis and a seeded population, with either, both or +neither spouse claimed, in 2021-2026: + +1. Swap invariance: each output is the same under either head/spouse + labelling. +2. Monotonicity: marking another filer as claimed never raises any of the + amounts or counts below. +3. Identities: `exemptions_count` equals the filers who cannot be claimed plus + the dependents when no filer can be claimed, and a return with a claimable + filer has no CTC-qualifying child and no credit for other dependents. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.tests.core.test_dependent_elsewhere_head_spouse_swap_invariance import ( + CLAIM_PATTERNS, + TOLERANCE, + YEARS, + _calc, + _crafted_cases, + _monotone_variants, + _seeded_couples, + _situation, + _swap_variants, + adults, + dependents, +) + +# Texas has no income tax; the others read the federal exemption count. +STATES = ["TX", "OR", "HI", "NM", "GA", "RI", "DE", "OH", "WI"] +OUTPUTS = [ + "income_tax", + "ctc_maximum", + "ctc_qualifying_children", + "capped_count_cdcc_eligible", + "exemptions_count", + "rrc_arpa", +] +# Amounts that marking another filer as claimed must never raise. +MONOTONE = [ + "ctc_maximum", + "ctc_qualifying_children", + "capped_count_cdcc_eligible", + "exemptions_count", + "rrc_arpa", +] + + +@st.composite +def couples(draw): + return { + "state": draw(st.sampled_from(STATES)), + "adults": [draw(adults()), draw(adults())], + "dependents": draw(st.lists(dependents(), max_size=2)), + "claimed": draw(st.sampled_from(CLAIM_PATTERNS)), + } + + +def check_swap(units, year, outputs, identities=None): + """Each output is the same under either head/spouse labelling.""" + sim = Simulation(situation=_situation(units, year, _swap_variants)) + for name in outputs: + values = _calc(sim, name, year) + assert np.isfinite(values).all(), f"{name} in {year} is not finite" + drawn, swapped = values[0::2], values[1::2] + differs = ~(np.abs(swapped - drawn) <= TOLERANCE) + assert not differs.any(), ( + f"{name} in {year} changes when the head and spouse labels are " + "exchanged: " + + ", ".join( + f"{units[i]['state']} claimed={units[i]['claimed']} " + f"{drawn[i]:.2f} -> {swapped[i]:.2f}" + for i in np.flatnonzero(differs)[:10] + ) + ) + if identities is not None: + identities(sim, year) + + +def check_monotone(units, year, outputs, identities=None): + """Marking another filer as claimed never raises an output.""" + sim = Simulation(situation=_situation(units, year, _monotone_variants)) + for name in outputs: + values = _calc(sim, name, year).reshape(-1, 4) + nobody, first, second, both = values.T + for more, fewer, label in ( + (first, nobody, "first adult"), + (second, nobody, "second adult"), + (both, first, "both after first"), + (both, second, "both after second"), + ): + rises = more > fewer + TOLERANCE + assert not rises.any(), ( + f"{name} in {year} rises when the {label} is claimed " + "elsewhere: " + + ", ".join( + f"{units[i]['state']} {fewer[i]:.2f} -> {more[i]:.2f}" + for i in np.flatnonzero(rises)[:10] + ) + ) + if identities is not None: + identities(sim, year) + + +def _federal_identities(sim, year): + claimed_filer = _calc(sim, "head_or_spouse_is_dependent_elsewhere", year) > 0 + independent = _calc(sim, "head_spouse_count_not_dependent_elsewhere", year) + dependents_count = _calc(sim, "tax_unit_count_dependents", year) + expected = independent + np.where(claimed_filer, 0, dependents_count) + np.testing.assert_array_equal(_calc(sim, "exemptions_count", year), expected) + assert not _calc(sim, "ctc_qualifying_children", year)[claimed_filer].any() + odc = np.asarray( + sim.calculate("ctc_adult_individual_maximum", year, map_to="tax_unit") + ) + assert not odc[claimed_filer].any() + + +@settings( + max_examples=4, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.sampled_from(YEARS), st.lists(couples(), min_size=6, max_size=15)) +def test_claimable_filer_federal_rules_are_label_free(year, units): + check_swap(units, year, OUTPUTS, _federal_identities) + + +@pytest.mark.parametrize("year", YEARS) +def test_claimable_filer_federal_rules_seeded_population(year): + units = _seeded_couples(STATES, per_state=2) + _crafted_cases(STATES) + check_swap(units, year, OUTPUTS, _federal_identities) + check_monotone(units, year, MONOTONE, _federal_identities) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml index 9a4d8616b69..1c590c6f3cf 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml @@ -54,3 +54,99 @@ is_tax_unit_spouse: false output: is_cdcc_eligible: true + +- name: 2025 child under 13 is not a qualifying person when the single filer can be claimed as a dependent + period: 2025 + input: + people: + parent: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 5 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + # Publication 503: "Your qualifying child who is your dependent and who + # was under age 13"; IRC 152(b)(1). + is_cdcc_eligible: [false, false] + +- name: 2025 joint return with one claimable spouse has no under-13 qualifying person (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + is_cdcc_eligible: [false, false, false] + +- name: 2025 joint return with one claimable spouse has no under-13 qualifying person (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + is_cdcc_eligible: [false, false, false] + +- name: 2025 disabled person still qualifies when the filer can be claimed as a dependent + period: 2025 + input: + people: + filer: + age: 30 + claimed_as_dependent_on_another_return: true + relative: + age: 60 + is_incapable_of_self_care: true + tax_units: + tax_unit: + members: [filer, relative] + households: + household: + members: [filer, relative] + state_code: TX + output: + # IRC 21(b)(1)(B) determines dependency "without regard to subsections + # (b)(1), (b)(2), and (d)(1)(B)" of section 152; Publication 503 item + # 3(b)(iii). + is_cdcc_eligible: [false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml index 3f4a66e3619..0a40c086281 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml @@ -56,3 +56,106 @@ members: [adult, child, child2] output: ctc_qualifying_child: [False, True, False] + +- name: 2025 single filer who can be claimed as a dependent has no CTC-qualifying child + period: 2025 + input: + people: + parent: + age: 20 + employment_income: 5_000 + claimed_as_dependent_on_another_return: true + child: + age: 5 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + # IRC 24(a), 152(b)(1); Publication 501, Dependent Taxpayer Test. + ctc_qualifying_child: [false, false] + ctc_maximum: 0 + +- name: 2025 joint return with one claimable spouse has no CTC-qualifying child (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + # "If you are filing a joint return and your spouse can be claimed as a + # dependent by another taxpayer, you and your spouse can't claim any + # dependents on your joint return" (Publication 501). + ctc_qualifying_child: [false, false, false] + ctc_maximum: 0 + +- name: 2025 joint return with one claimable spouse has no CTC-qualifying child (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + ctc_qualifying_child: [false, false, false] + ctc_maximum: 0 + +- name: 2025 joint return with neither spouse claimable keeps the CTC-qualifying child + period: 2025 + input: + people: + person_a: + age: 20 + person_b: + age: 20 + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + ctc_qualifying_child: [false, false, true] + ctc_maximum: 2_200 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml index fec059bb2d7..9c46915a88e 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml @@ -43,3 +43,96 @@ ctc_child_individual_maximum: 0 output: ctc_adult_individual_maximum: 500 + +- name: 2025 single filer who can be claimed as a dependent gets no credit for other dependents + period: 2025 + input: + people: + filer: + age: 22 + claimed_as_dependent_on_another_return: true + sibling: + age: 18 + tax_units: + tax_unit: + members: [filer, sibling] + households: + household: + members: [filer, sibling] + state_code: TX + output: + # IRC 24(h)(4) "any dependent of the taxpayer"; IRC 152(b)(1). + ctc_adult_individual_maximum: [0, 0] + +- name: 2025 joint return with one claimable spouse gets no credit for other dependents (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + is_tax_unit_head: false + is_tax_unit_spouse: true + sibling: + age: 18 + tax_units: + tax_unit: + members: [person_a, person_b, sibling] + filing_status: JOINT + households: + household: + members: [person_a, person_b, sibling] + state_code: TX + output: + ctc_adult_individual_maximum: [0, 0, 0] + +- name: 2025 joint return with one claimable spouse gets no credit for other dependents (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + sibling: + age: 18 + tax_units: + tax_unit: + members: [person_b, person_a, sibling] + filing_status: JOINT + households: + household: + members: [person_b, person_a, sibling] + state_code: TX + output: + ctc_adult_individual_maximum: [0, 0, 0] + +- name: 2025 joint return with neither spouse claimable gets the credit for other dependents + period: 2025 + input: + people: + person_a: + age: 22 + person_b: + age: 22 + sibling: + age: 18 + tax_units: + tax_unit: + members: [person_a, person_b, sibling] + filing_status: JOINT + households: + household: + members: [person_a, person_b, sibling] + state_code: TX + output: + ctc_adult_individual_maximum: [0, 0, 500] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml index 0d102e18b78..908d3d3c657 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml @@ -177,3 +177,133 @@ has_tin: true output: is_eligible_for_american_opportunity_credit: false + + +- name: 2024 student claimed as a dependent elsewhere cannot claim the credit + period: 2024 + input: + people: + student: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [student] + households: + household: + members: [student] + state_code: TX + output: + # IRC 25A(g)(3); Form 8863: "You're claimed as a dependent on another + # person's tax return". + is_eligible_for_american_opportunity_credit: false + +- name: 2024 joint return with one spouse claimed elsewhere cannot claim the credit for either spouse (the claimable spouse is the head) + period: 2024 + input: + people: + person_a: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + # Form 1040 instructions: "or your spouse if filing jointly". + is_eligible_for_american_opportunity_credit: [false, false] + +- name: 2024 joint return with one spouse claimed elsewhere cannot claim the credit for either spouse (the claimable spouse is the spouse) + period: 2024 + input: + people: + person_b: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, false] + +- name: 2024 joint return with neither spouse claimed keeps both students' eligibility + period: 2024 + input: + people: + person_a: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + person_b: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [true, true] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/lifetime_learning_credit/is_eligible_for_lifetime_learning_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/lifetime_learning_credit/is_eligible_for_lifetime_learning_credit.yaml index 72ca159a702..4bff0f55a7d 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/lifetime_learning_credit/is_eligible_for_lifetime_learning_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/lifetime_learning_credit/is_eligible_for_lifetime_learning_credit.yaml @@ -70,3 +70,110 @@ has_tin: true output: is_eligible_for_lifetime_learning_credit: false + + +- name: 2024 student claimed as a dependent elsewhere cannot claim the credit + period: 2024 + input: + people: + student: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [student] + households: + household: + members: [student] + state_code: TX + output: + # IRC 25A(g)(3). + is_eligible_for_lifetime_learning_credit: false + +- name: 2024 joint return with one spouse claimed elsewhere cannot claim the credit for either spouse (the claimable spouse is the head) + period: 2024 + input: + people: + person_a: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + is_eligible_for_lifetime_learning_credit: [false, false] + +- name: 2024 joint return with one spouse claimed elsewhere cannot claim the credit for either spouse (the claimable spouse is the spouse) + period: 2024 + input: + people: + person_b: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + is_eligible_for_lifetime_learning_credit: [false, false] + +- name: 2024 joint return with neither spouse claimed keeps both students' eligibility + period: 2024 + input: + people: + person_a: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + person_b: + age: 30 + attends_eligible_educational_institution_for_lifetime_learning_credit: true + has_lifetime_learning_credit_1098_t_or_exception: true + has_tin: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + is_eligible_for_lifetime_learning_credit: [true, true] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml index e84c1db994a..0d21682feaf 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml @@ -47,3 +47,115 @@ members: [parent, child_1, child_2] output: recovery_rebate_credit: 3_920 # https://www.efile.com/stimulus-check-three/ + + +- name: 2021 single filer who can be claimed as a dependent gets no ARPA rebate + period: 2021 + input: + people: + filer: + age: 20 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: TX + output: + # IRC 6428B(c)(2): an eligible individual excludes "any individual who is a + # dependent of another taxpayer". + rrc_arpa: 0 + +- name: 2021 claimable single filer's child adds no ARPA rebate + period: 2021 + input: + people: + parent: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + # IRC 152(b)(1): such a filer has no dependents. + rrc_arpa: 0 + +- name: 2021 joint return with one claimable spouse gets the other spouse's ARPA rebate (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + # Each spouse is an eligible individual or not on their own; $1,400 for + # the spouse who cannot be claimed. + rrc_arpa: 1_400 + +- name: 2021 joint return with one claimable spouse gets the other spouse's ARPA rebate (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 22 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + rrc_arpa: 1_400 + +- name: 2021 joint return with neither spouse claimable gets both rebates and the child's + period: 2021 + input: + people: + person_a: + age: 20 + person_b: + age: 22 + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + # 3 x $1,400. + rrc_arpa: 4_200 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml index ca6f654768e..97566eae089 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml @@ -37,3 +37,72 @@ filing_status: SEPARATE output: student_loan_interest_ald_eligible: false + + +- name: 2024 single filer claimed as a dependent elsewhere cannot deduct student loan interest + period: 2024 + input: + people: + filer: + age: 24 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: TX + output: + # IRC 221(c). + student_loan_interest_ald_eligible: false + +- name: 2024 joint return with one spouse claimed elsewhere cannot deduct student loan interest (the claimable spouse is the head) + period: 2024 + input: + people: + person_a: + age: 24 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + # Form 1040 instructions: "you, or your spouse if filing jointly, aren't + # claimed as a dependent". + student_loan_interest_ald_eligible: [false, false] + +- name: 2024 joint return with one spouse claimed elsewhere cannot deduct student loan interest (the claimable spouse is the spouse) + period: 2024 + input: + people: + person_b: + age: 24 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 24 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + student_loan_interest_ald_eligible: [false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml new file mode 100644 index 00000000000..16c899b35cf --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml @@ -0,0 +1,133 @@ +- name: 2025 single filer with a child counts both + period: 2025 + input: + people: + parent: + age: 30 + child: + age: 5 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + exemptions_count: 2 + +- name: 2025 single filer who can be claimed as a dependent has no exemptions + period: 2025 + input: + people: + parent: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 5 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + # IRC 151(d)(2) for the filer; IRC 152(b)(1) for the child. + exemptions_count: 0 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + exemptions_count: 1 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + exemptions_count: 1 + +- name: 2025 joint return with both spouses claimable has no exemptions + period: 2025 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + person_b: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + exemptions_count: 0 + +- name: 2025 joint return with neither spouse claimable counts everyone + period: 2025 + input: + people: + person_a: + age: 20 + person_b: + age: 20 + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + exemptions_count: 3 diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml index 406b0d0f8f9..5964fe8cf76 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.yaml @@ -71,3 +71,54 @@ output: # Equal capacity: split evenly (both columns can absorb their share) de_personal_credit_indv: [110, 110] + + +- name: 2025 spouse who can be claimed as a dependent has no personal credit (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + de_income_tax_before_non_refundable_credits_indv: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + de_income_tax_before_non_refundable_credits_indv: 1000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: DE + output: + # 30 Del. C. 1110(b)(1): $110 for each personal exemption to which the + # individual is entitled for federal purposes; IRC 151(d)(2). + de_personal_credit_indv: [0, 110] + +- name: 2025 spouse who can be claimed as a dependent has no personal credit (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + de_income_tax_before_non_refundable_credits_indv: 1000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + de_income_tax_before_non_refundable_credits_indv: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: DE + output: + de_personal_credit_indv: [110, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credits.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credits.yaml index fd12e7c01ce..b13f8f2f86d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credits.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/de/tax/income/credits/personal/de_personal_credits.yaml @@ -30,3 +30,58 @@ de_income_tax_before_non_refundable_credits_unit: 1000000 output: de_personal_credit: 0 + + +- name: 2025 joint return with one claimable spouse gets one personal credit (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: DE + output: + # Federal exemptions: the other spouse only; IRC 152(b)(1) leaves no + # dependents. + de_personal_credit_potential: 110 + +- name: 2025 joint return with one claimable spouse gets one personal credit (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: DE + output: + de_personal_credit_potential: 110 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_ctc.yaml index 658c2647ad9..0690ba455ca 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_ctc.yaml @@ -6,7 +6,6 @@ age: 30 child: age: 2 - ctc_qualifying_child: true tax_units: tax_unit: members: @@ -30,7 +29,6 @@ age: 30 child: age: 6 - ctc_qualifying_child: true tax_units: tax_unit: members: @@ -54,10 +52,8 @@ age: 32 child1: age: 1 - ctc_qualifying_child: true child2: age: 5 - ctc_qualifying_child: true tax_units: tax_unit: members: @@ -83,7 +79,6 @@ age: 30 child: age: 2 - ctc_qualifying_child: true tax_units: tax_unit: members: @@ -107,7 +102,6 @@ age: 30 child: age: 2 - ctc_qualifying_child: true tax_units: tax_unit: members: @@ -123,3 +117,27 @@ state_code: GA output: ga_non_refundable_credits: 0 + + +- name: 2026 GA CTC still counts the child of a filer who can be claimed as a dependent + period: 2026 + input: + people: + parent: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: SINGLE + ga_income_tax_before_non_refundable_credits: 10000 + households: + household: + members: [parent, child] + state_code: GA + output: + # Georgia uses the IRC 24(c) qualifying-child definition only; whether IRC + # 152(b)(1) reaches it is not settled, so the child test is kept. + ga_ctc: 250 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_low_income_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_low_income_credit.yaml index 5a059239e46..2a410aa94df 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_low_income_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/tax/income/credits/ga_low_income_credit.yaml @@ -5,7 +5,7 @@ adjusted_gross_income: 14000 age_head: 67 age_spouse: 70 - exemptions_count: 2 + tax_unit_size: 2 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 32 @@ -16,7 +16,7 @@ adjusted_gross_income: 4000 age_head: 67 age_spouse: 56 - exemptions_count: 2 + tax_unit_size: 2 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 78 @@ -27,7 +27,7 @@ adjusted_gross_income: 15500 age_head: 80 age_spouse: 75 - exemptions_count: 3 + tax_unit_size: 3 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 25 @@ -38,7 +38,7 @@ adjusted_gross_income: 4000 age_head: 67 age_spouse: 56 - exemptions_count: 0 + tax_unit_size: 0 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 26 @@ -49,7 +49,7 @@ adjusted_gross_income: 10500 age_head: 67 age_spouse: 56 - exemptions_count: 0 + tax_unit_size: 0 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 8 @@ -60,7 +60,7 @@ adjusted_gross_income: 10500 age_head: 56 age_spouse: 56 - exemptions_count: 0 + tax_unit_size: 0 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 0 @@ -71,7 +71,7 @@ adjusted_gross_income: 10500 age_head: 65 age_spouse: 64 - exemptions_count: 1 + tax_unit_size: 1 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 16 @@ -82,7 +82,7 @@ adjusted_gross_income: 10000 age_head: 64 age_spouse: 64 - exemptions_count: 2 + tax_unit_size: 2 ga_income_tax_before_non_refundable_credits: 1000000 output: ga_low_income_credit: 16 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.yaml index f4acfca8392..5f236216e86 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.yaml @@ -27,3 +27,102 @@ exemptions_count: 3 output: nm_low_and_middle_income_exemption: 4_500 + + +- name: 2025 single filer who can be claimed as a dependent has no exemptions + period: 2025 + input: + people: + filer: + age: 30 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + adjusted_gross_income: 10_000 + households: + household: + members: [filer] + state_code: NM + output: + # NMSA 7-2-5.8 counts federal exemptions; IRC 151(d)(2). + nm_low_and_middle_income_exemption: 0 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b, child] + state_code: NM + output: + # One exemption (the child is not a dependent, IRC 152(b)(1)) x $2,500. + nm_low_and_middle_income_exemption: 2_500 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_b, person_a, child] + state_code: NM + output: + # One exemption (the child is not a dependent, IRC 152(b)(1)) x $2,500. + nm_low_and_middle_income_exemption: 2_500 + +- name: 2025 joint return with neither spouse claimable counts everyone + period: 2025 + input: + people: + person_a: + age: 30 + person_b: + age: 30 + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + adjusted_gross_income: 10_000 + households: + household: + members: [person_a, person_b, child] + state_code: NM + output: + # 3 exemptions x $2,500. + nm_low_and_middle_income_exemption: 7_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml index 7bcd28c10fa..6c7e83fb126 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml @@ -246,3 +246,83 @@ state_code: NM output: nm_low_income_comprehensive_tax_rebate: 583 + +- name: 2025 single filer who can be claimed as a dependent gets no rebate + period: 2025 + input: + people: + person: + age: 20 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person] + nm_modified_gross_income: 5_000 + households: + household: + members: [person] + state_code: NM + output: + # NMSA 7-2-14(A): only a resident "who is not a dependent of another + # individual" may claim the rebate. + nm_low_income_comprehensive_tax_rebate: 0 + +- name: 2025 single filer who cannot be claimed gets the one-exemption rebate + period: 2025 + input: + people: + person: + age: 20 + tax_units: + tax_unit: + members: [person] + nm_modified_gross_income: 5_000 + households: + household: + members: [person] + state_code: NM + output: + # $1,000-$7,500 row, one exemption. + nm_low_income_comprehensive_tax_rebate: 252 + +- name: 2025 single filer aged 65 or older who can be claimed as a dependent gets no rebate + period: 2025 + input: + people: + person: + age: 70 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person] + nm_modified_gross_income: 5_000 + households: + household: + members: [person] + state_code: NM + output: + # The age-65 exemptions do not make a dependent of another individual a + # claimant (NMSA 7-2-14(A)). + nm_low_income_comprehensive_tax_rebate: 0 + +- name: 2025 joint return with one claimable spouse keeps the rebate with the couple's combined income + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + person_b: + age: 30 + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + nm_modified_gross_income: 8_500 + households: + household: + members: [person_a, person_b] + state_code: NM + output: + # One exemption; the $8,000-$9,000 row of the one-exemption table. + nm_low_income_comprehensive_tax_rebate: 212 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.yaml index 5b10b5cf2d8..6fa092fcffe 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.yaml @@ -24,3 +24,104 @@ blind_head: true output: nm_low_income_comprehensive_tax_rebate_exemptions: 2 + +- name: 2025 single filer who can be claimed as a dependent has no rebate exemptions + period: 2025 + input: + people: + parent: + age: 20 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: NM + output: + # NMSA 7-2-14(A),(C); PIT-RC line 2a. + nm_low_income_comprehensive_tax_rebate_exemptions: 0 + +- name: 2025 joint return with one claimable spouse drops its ordinary exemption and the dependents (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 40 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: NM + output: + # "the non-qualifying spouse is not eligible" (PIT-RC line 2a) and a + # return on which a filer can be claimed has no dependents (IRC + # 152(b)(1)), leaving 1 ordinary exemption; NMSA 7-2-14(C) still adds 2 + # for "each individual ... included in the return who is sixty-five years + # of age or older". + nm_low_income_comprehensive_tax_rebate_exemptions: 3 + +- name: 2025 joint return with one claimable spouse drops its ordinary exemption and the dependents (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 40 + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 70 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: NM + output: + nm_low_income_comprehensive_tax_rebate_exemptions: 3 + +- name: 2025 joint return with neither spouse claimable counts everyone plus the age-65 exemptions + period: 2025 + input: + people: + person_a: + age: 70 + person_b: + age: 40 + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: NM + output: + # 3 federal exemptions + 2 for the spouse aged 65 or older. + nm_low_income_comprehensive_tax_rebate_exemptions: 5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml index 97f70fe2376..88f43d6e9af 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml @@ -27,3 +27,57 @@ is_tax_unit_dependent: true output: oh_personal_exemptions_eligible_person: true + + +- name: 2025 joint return with one claimable spouse makes neither that spouse nor the child eligible (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: OH + output: + # Ohio R.C. 5747.025; IRC 152(b)(1) leaves the return no dependents. + oh_personal_exemptions_eligible_person: [false, true, false] + +- name: 2025 joint return with one claimable spouse makes neither that spouse nor the child eligible (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: OH + output: + oh_personal_exemptions_eligible_person: [true, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/exemption/ri_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/exemption/ri_exemptions.yaml index 874ee89a624..539a40f00a6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/exemption/ri_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/exemption/ri_exemptions.yaml @@ -135,3 +135,82 @@ exemptions_count: 2 output: ri_exemptions: 0 + + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + ri_agi: 10000 + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # R.I. Gen. Laws 44-30-2.6: "If someone else can claim you on their return, + # your exemption amount is zero"; IRC 152(b)(1). + ri_exemptions: 5_100 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + ri_agi: 10000 + households: + household: + members: [person_b, person_a, child] + state_code: RI + output: + ri_exemptions: 5_100 + +- name: 2025 joint return with neither spouse claimable counts everyone + period: 2025 + input: + people: + person_a: + age: 30 + person_b: + age: 30 + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + ri_agi: 10000 + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + ri_exemptions: 15_300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_base_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_base_exemption.yaml index 9e54d8bd87f..f1f8087f317 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_base_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_base_exemption.yaml @@ -5,3 +5,79 @@ state_code: WI output: wi_base_exemption: 5 * 700 + + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # Form 1 instructions: "either you or your spouse (not both) can be claimed"; + # IRC 152(b)(1). + wi_base_exemption: 700 + +- name: 2025 joint return with one claimable spouse counts only the other spouse (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: WI + output: + wi_base_exemption: 700 + +- name: 2025 joint return with neither spouse claimable counts everyone + period: 2025 + input: + people: + person_a: + age: 30 + person_b: + age: 30 + child: + age: 5 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + wi_base_exemption: 2_100 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py index 82861496784..20d82cc32e0 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py @@ -9,6 +9,9 @@ class is_cdcc_eligible(Variable): reference = ( "https://www.law.cornell.edu/uscode/text/26/21#b_1", "https://www.law.cornell.edu/uscode/text/26/21#e_5", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", + # Publication 503, Who Is a Qualifying Person? + "https://www.irs.gov/pub/irs-prior/p503--2025.pdf#page=3", ) def formula(person, period, parameters): @@ -21,8 +24,18 @@ def formula(person, period, parameters): # qualifying individual even when the noncustodial parent claims the # dependent, so any under-age member other than the head or spouse # qualifies. - qualifies_by_age = ~head_or_spouse & (age < p.child_age) - # Subsections (b)(1)(B) (dependent) and (b)(1)(C) (spouse). + # A child qualifies only as the filer's dependent ("Your qualifying + # child who is your dependent", Publication 503), and under IRC + # 152(b)(1) a return on which the filer (or, if joint, either spouse) + # can be claimed as a dependent has no dependents. + filer_is_dependent = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + qualifies_by_age = ~head_or_spouse & (age < p.child_age) & ~filer_is_dependent + # Subsections (b)(1)(B) (dependent) and (b)(1)(C) (spouse). Subsection + # (b)(1)(B) determines dependency "without regard to subsections + # (b)(1), (b)(2), and (d)(1)(B)" of section 152, so a disabled person + # still qualifies when the filer can be claimed as a dependent. disabled = person("is_incapable_of_self_care", period) married = person.tax_unit("tax_unit_married", period) qualifies_by_disability = disabled & (is_dependent | (head_or_spouse & married)) diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py index ae2c1bfab24..bd478b60870 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py @@ -8,7 +8,13 @@ class ctc_qualifying_child(Variable): documentation = "Child qualifies for the Child Tax Credit" definition_period = YEAR defined_for = "is_tax_unit_dependent" - reference = "https://www.law.cornell.edu/uscode/text/26/24#c" + reference = ( + "https://www.law.cornell.edu/uscode/text/26/24#a", + "https://www.law.cornell.edu/uscode/text/26/24#c", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", + # Publication 501, Dependent Taxpayer Test. + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) def formula(person, period, parameters): age = person("age", period) @@ -18,4 +24,12 @@ def formula(person, period, parameters): meets_identification_requirements = person( "meets_ctc_child_identification_requirements", period ) - return age_eligible & meets_identification_requirements + # IRC 24(a) allows the credit only for a qualifying child for whom the + # taxpayer is allowed a deduction under section 151. Under IRC + # 152(b)(1) a taxpayer who can be claimed as a dependent is treated as + # having no dependents, and on a joint return neither spouse may be + # claimable (Publication 501, Dependent Taxpayer Test). + filer_is_dependent = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + return age_eligible & meets_identification_requirements & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py index 4caaab2f978..70f5b877edd 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py @@ -15,6 +15,7 @@ class ctc_adult_individual_maximum(Variable): "https://www.law.cornell.edu/uscode/text/26/24#a", "https://www.law.cornell.edu/uscode/text/26/24#h", "https://www.law.cornell.edu/uscode/text/26/24#i", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", ) def formula(person, period, parameters): @@ -24,9 +25,16 @@ def formula(person, period, parameters): filer_meets_tin_requirement = person.tax_unit( "filer_meets_ctc_identification_requirements", period ) + # IRC 24(h)(4) gives the credit for "any dependent of the taxpayer"; + # under IRC 152(b)(1) a return on which the filer (or, if joint, + # either spouse) can be claimed as a dependent has no dependents. + filer_is_dependent = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) return ( is_adult * dependent_has_tin * filer_meets_tin_requirement + * ~filer_is_dependent * p.amount.adult_dependent ) diff --git a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py index 6509aa1f045..c3a557b0d8f 100644 --- a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py @@ -18,6 +18,11 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE + # IRC 25A(g)(3): no credit to an individual claimed as a dependent by + # another taxpayer, and the Form 1040 instructions extend this to + # "your spouse if filing jointly", so either filer being claimed bars + # the return's credit. + filer_claimed = tax_unit("head_or_spouse_is_dependent_elsewhere", period) requires_1098_t = aoc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_american_opportunity_credit_1098_t_or_exception", period) @@ -66,4 +71,5 @@ def formula(person, period, parameters): period, ) & filing_status_eligible + & ~filer_claimed ) diff --git a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py index 3d80f23da5f..75c2e133d15 100644 --- a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py @@ -19,6 +19,11 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE + # IRC 25A(g)(3): no credit to an individual claimed as a dependent by + # another taxpayer, and the Form 1040 instructions extend this to + # "your spouse if filing jointly", so either filer being claimed bars + # the return's credit. + filer_claimed = tax_unit("head_or_spouse_is_dependent_elsewhere", period) requires_1098_t = llc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_lifetime_learning_credit_1098_t_or_exception", period) @@ -45,4 +50,5 @@ def formula(person, period, parameters): period, ) & filing_status_eligible + & ~filer_claimed ) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py index 2250e5d6698..4e08716df78 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py @@ -13,6 +13,7 @@ class rrc_adult_count_with_valid_ssn(Variable): "https://www.law.cornell.edu/uscode/text/26/6428A#g_2", "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_A", "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_B", + "https://www.law.cornell.edu/uscode/text/26/6428#d_2", ) def formula(tax_unit, period, parameters): @@ -20,4 +21,9 @@ def formula(tax_unit, period, parameters): head_or_spouse = person("is_tax_unit_head_or_spouse", period) # Reuse existing EITC SSN check - identical definition (SSN required) has_valid_ssn = person("meets_eitc_identification_requirements", period) - return tax_unit.sum(head_or_spouse & has_valid_ssn) + # An "eligible individual" excludes "any individual with respect to + # whom a deduction under section 151 is allowable to another + # taxpayer" (IRC 6428(d)(2), 6428A(d)(2)) or "who is a dependent of + # another taxpayer" (IRC 6428B(c)(2)). + claimed = person("claimed_as_dependent_on_another_return", period) + return tax_unit.sum(head_or_spouse & has_valid_ssn & ~claimed) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py index 00b05333a86..e91e2ee9a48 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py @@ -13,6 +13,12 @@ class rrc_arpa_dependents_with_valid_ssn(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # IRC 6428B(b)(2) counts "the number of dependents of the taxpayer", and + # under 152(b)(1) a return on which the filer (or, if joint, either + # spouse) can be claimed as a dependent has none. + dependent_filer = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + is_dependent = person("is_tax_unit_dependent", period) & ~dependent_filer has_valid_ssn = person("meets_eitc_identification_requirements", period) return tax_unit.sum(is_dependent & has_valid_ssn) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py index 9bf6a41feb0..3b45af22df5 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py @@ -19,9 +19,13 @@ class student_loan_interest_ald_eligible(Variable): ) def formula(person, period, parameters): - # Per IRC § 221(c), taxpayer cannot be claimed as a dependent head_or_spouse = person("is_tax_unit_head_or_spouse", period) + # Per IRC § 221(c), no deduction for an individual claimed as a + # dependent by another taxpayer; the Form 1040 instructions require + # that "you, or your spouse if filing jointly, aren't claimed as a + # dependent", so either filer being claimed bars the return. + filer_claimed = person.tax_unit("head_or_spouse_is_dependent_elsewhere", period) # Per IRC § 221(e)(2), taxpayer cannot file as Married Filing Separately filing_status = person.tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - return head_or_spouse & ~separate + return head_or_spouse & ~separate & ~filer_claimed diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py b/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py index b8e19acbdab..2fec52064f4 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py @@ -7,5 +7,31 @@ class exemptions_count(Variable): label = "Number of tax exemptions" unit = USD definition_period = YEAR + documentation = ( + "Number of personal exemptions on the federal return: one for each " + "filer who cannot be claimed as a dependent by another taxpayer, plus " + "one for each dependent unless a filer can be claimed." + ) + reference = ( + "https://www.law.cornell.edu/uscode/text/26/151#d_2", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", + # Publication 501, Dependent Taxpayer Test. + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + # 2017 Form 1040, line 6a: "If someone can claim you as a dependent, + # do not check box 6a"; instructions, line 6b: the spouse box only + # if the "spouse can't be claimed as a dependent". + "https://www.irs.gov/pub/irs-prior/f1040--2017.pdf#page=1", + "https://www.irs.gov/pub/irs-prior/i1040gi--2017.pdf#page=15", + ) - adds = ["tax_unit_size"] + def formula(tax_unit, period, parameters): + # IRC 151(d)(2) zeroes the exemption of a filer whom another taxpayer + # can claim, and IRC 152(b)(1) treats a return on which the filer (or, + # if joint, either spouse) can be claimed as having no dependents. So + # such a return counts only its filers who cannot be claimed. + tax_unit_size = tax_unit("tax_unit_size", period) + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + independent_filers = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return where(filer_is_dependent, independent_filers, tax_unit_size) diff --git a/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py b/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py index 6722f22b3c2..9701ad23833 100644 --- a/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py +++ b/policyengine_us/variables/gov/states/de/tax/income/credits/personal/de_personal_credit_indv.py @@ -32,16 +32,27 @@ def formula(person, period, parameters): fixed = person("de_aged_personal_credit_indv", period) + person( "de_cdcc_indv", period ) - # Each column's own $110 personal credit is fixed to that column. - head_fixed = person.tax_unit.sum(is_head * fixed) + credit_per - spouse_fixed = person.tax_unit.sum(is_spouse * fixed) + credit_per + # Each column's own $110 personal credit is fixed to that column. 30 + # Del. C. 1110(b)(1) gives it "for each personal exemption to which + # such individual is entitled ... for federal income tax purposes", + # and a spouse who can be claimed as a dependent has none (IRC + # 151(d)(2)). + head_own = where( + person.tax_unit("head_is_dependent_elsewhere", period), 0, credit_per + ) + spouse_own = where( + person.tax_unit("spouse_is_dependent_elsewhere", period), 0, credit_per + ) + head_fixed = person.tax_unit.sum(is_head * fixed) + head_own + spouse_fixed = person.tax_unit.sum(is_spouse * fixed) + spouse_own head_capacity = max_(head_tax - head_fixed, 0) spouse_capacity = max_(spouse_tax - spouse_fixed, 0) total_units = person.tax_unit("exemptions_count", period) - # Dependent increments: exemptions beyond the two spouses. - dep_units = max_(total_units - 2, 0) + # Dependent increments: exemptions beyond the spouses' own. + own_units = person.tax_unit("head_spouse_count_not_dependent_elsewhere", period) + dep_units = max_(total_units - own_units, 0) # Optimal dependent split: try proportional floor and ceil, pick # whichever maximises effective credits (min of alloc vs @@ -62,7 +73,7 @@ def formula(person, period, parameters): ) n_head_dep = where(eff_high > eff_low, n_high, n_low) - head_alloc = credit_per + n_head_dep * credit_per - spouse_alloc = credit_per + (dep_units - n_head_dep) * credit_per + head_alloc = head_own + n_head_dep * credit_per + spouse_alloc = spouse_own + (dep_units - n_head_dep) * credit_per return is_head_or_spouse * where(is_head, head_alloc, spouse_alloc) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py index 46ceaac454b..61195c8cb2c 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py @@ -16,7 +16,17 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.credits.ctc person = tax_unit.members age = person("age", period) - ctc_eligible_child = person("ctc_qualifying_child", period) + # Georgia gives "qualifying child" the meaning of IRC 24(c) only, so + # it is not limited to children for whom the federal credit is + # allowed (IRC 24(a), 152(b)(1)). Whether a filer who can be claimed + # as a dependent can claim it is not settled; this keeps the IRC + # 24(c) child test. + p_federal = parameters(period).gov.irs.credits.ctc + ctc_eligible_child = ( + person("is_tax_unit_dependent", period) + & (age < p_federal.amount.base.thresholds[-1]) + & person("meets_ctc_child_identification_requirements", period) + ) ga_child_age_eligible = age < p.age_threshold eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/credits/ga_low_income_credit_potential.py b/policyengine_us/variables/gov/states/ga/tax/income/credits/ga_low_income_credit_potential.py index 4487e9e448a..07708fa354f 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/credits/ga_low_income_credit_potential.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/credits/ga_low_income_credit_potential.py @@ -15,7 +15,10 @@ def formula(tax_unit, period, parameters): # "multiplied by the number of dependents which the taxpayer is entitled to claim." # The tax form excludes adult dependents: # "Exemptions are self, spouse and natural or legally adopted children" - exemptions = tax_unit("exemptions_count", period) + # Georgia counts its own exemptions (O.C.G.A. 48-7A-3 deems each joint + # spouse a dependent), not the federal count, which leaves out a + # filer who can be claimed as a dependent. + exemptions = tax_unit("tax_unit_size", period) p = parameters(period).gov.states.ga.tax.income.credits.low_income # age threshold age_threshold = p.supplement_age_eligibility diff --git a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.py b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.py index 98a265dc9e8..c1540e1b65f 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/exemptions/nm_low_and_middle_income_exemption.py @@ -25,5 +25,8 @@ def formula(tax_unit, period, parameters): exemption_amount = p.max_amount - reduction_amount # Multiply the exemption amount by the number of exemptions + # NMSA 7-2-5.8 multiplies by the number of federal exemptions, which + # leaves out a filer who can be claimed as a dependent and, on such a + # return, the dependents. exemptions_count = tax_unit("exemptions_count", period) return eligible * exemption_amount * exemptions_count diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.py index 1fbb3a4d6b7..33bf6d2e2a3 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.py @@ -49,4 +49,10 @@ def formula(tax_unit, period, parameters): p.divisor, 1, ) - return rebate / divisor + # NMSA 7-2-14(A) allows the rebate only to a resident "who is not a + # dependent of another individual"; on a joint return the spouse who + # is not may still claim it (PIT-RC). The table starts at one + # exemption. + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) + eligible = ~every_filer_dependent & (exemptions > 0) + return where(eligible, rebate / divisor, 0) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.py b/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.py index cd8f89ad180..05621600269 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate_exemptions.py @@ -9,6 +9,7 @@ class nm_low_income_comprehensive_tax_rebate_exemptions(Variable): definition_period = YEAR reference = ( "https://law.justia.com/codes/new-mexico/chapter-7/article-2/section-7-2-14/", + "https://realfile.tax.newmexico.gov/2025pit-rc-ins.pdf#page=3", ) defined_for = StateCode.NM @@ -21,6 +22,11 @@ def formula(tax_unit, period, parameters): tax purposes for each individual included in the return who is domiciled in New Mexico... """ + # The federal count leaves out a filer who can be claimed as a + # dependent and, on such a return, the dependents, as PIT-RC line 2a + # does: "If either you or your spouse meet the general + # qualifications, but not both, then the non-qualifying spouse is not + # eligible". federal_exemptions = tax_unit("exemptions_count", period) """ ...plus two additional exemptions for each individual domiciled diff --git a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py index e222b7bf362..b78575932fb 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py @@ -18,6 +18,11 @@ def formula(person, period, parameters): dependent_on_another_return = person( "claimed_as_dependent_on_another_return", period ) - # The personal exemption is also provided to dependents - dependent = person("is_tax_unit_dependent", period) + # The personal exemption is also provided to dependents, but a return + # on which the filer (or, if joint, either spouse) can be claimed as a + # dependent has none (IRC 152(b)(1)). + dependent_filer = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + dependent = person("is_tax_unit_dependent", period) & ~dependent_filer return (~dependent_on_another_return & head_or_spouse) | dependent From b007fb697d30af7a6e6a6e34e352608abc533eaf Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:14 -0400 Subject: [PATCH 08/42] Apply Rhode Island's rules for a filer who can be claimed as a dependent - Property tax relief (R.I. Gen. Laws 44-33-3): the claimant is a filer aged 65 or older or disabled who is not claimed as a dependent; household members choose the claimant, so either spouse qualifies, but the age or disability and the dependency test belong to the same person. - 2021-return child rebate (44-30-103(b)(1)): only children the taxpayer "validly claims as a dependent", and a return with a claimable filer has none (IRC 152(b)(1)). The exemptions, EITC and care credit already follow the federal fixes. Co-Authored-By: Claude Opus 5.5 --- .../property_tax/ri_property_tax_credit.yaml | 5 +- .../ri_property_tax_credit_eligible.yaml | 350 ++++++++++++++++-- .../credits/rebates/ri_child_tax_rebate.yaml | 138 +++++++ .../ri_property_tax_credit_eligible.py | 20 +- .../credits/rebates/ri_child_tax_rebate.py | 7 +- 5 files changed, 484 insertions(+), 36 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit.yaml index b9a19ddc96c..073f9801cb1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit.yaml @@ -88,6 +88,9 @@ tax_unit_size: 1 real_estate_taxes: 2_000 rent: 0 - greater_age_head_spouse: 70 + # The claimant's own age now sets eligibility (R.I. Gen. Laws 44-33-3), + # so the income cutoff alone makes this filer ineligible. + age: 70 output: + ri_property_tax_credit_eligible: false ri_property_tax_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml index 4e50ae67af2..1e828db6a9d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml @@ -1,59 +1,357 @@ - name: Head above minimum age, and disabled period: 2022 input: - state_code: RI - greater_age_head_spouse: 65 - head_is_disabled: true - spouse_is_disabled: false + people: + head: + age: 65 + is_disabled: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: RI output: ri_property_tax_credit_eligible: true -- name: Head above minimum age, not diabled +- name: Head above minimum age, not disabled period: 2022 input: - state_code: RI - greater_age_head_spouse: 65 - head_is_disabled: false - spouse_is_disabled: false + people: + head: + age: 65 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: RI output: ri_property_tax_credit_eligible: true - name: Head disabled, not above minimum age period: 2022 input: - state_code: RI - greater_age_head_spouse: 64 - head_is_disabled: true - spouse_is_disabled: false + people: + head: + age: 64 + is_disabled: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: RI output: ri_property_tax_credit_eligible: true - + - name: Spouse above minimum age, not disabled period: 2021 input: - state_code: RI - greater_age_head_spouse: 65 - head_is_disabled: false - spouse_is_disabled: false + people: + head: + age: 64 + spouse: + age: 65 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: RI output: ri_property_tax_credit_eligible: true - name: Not above minimum age, not disabled period: 2021 input: - state_code: RI - greater_age_head_spouse: 64 - head_is_disabled: false - spouse_is_disabled: false + people: + head: + age: 64 + spouse: + age: 64 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: RI output: ri_property_tax_credit_eligible: false - name: Spouse disabled period: 2021 input: - state_code: RI - greater_age_head_spouse: 64 - head_is_disabled: false - spouse_is_disabled: true + people: + head: + age: 64 + spouse: + age: 64 + is_disabled: true + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: RI + output: + ri_property_tax_credit_eligible: true + +- name: 2025 single filer who can be claimed as a dependent: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent + period: 2025 + input: + people: + filer: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: RI + output: + # R.I. Gen. Laws 44-33-3: the claimant does not include any person + # claimed as a dependent by any taxpayer. + ri_property_tax_credit_eligible: false + ri_property_tax_credit: 0 + +- name: 2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: RI + output: + # The unclaimed spouse, aged 65, may be the claimant (household + # members choose who claims). Household income $0, so the + # threshold is $0; property tax $1,000 is capped at the 2025 + # maximum of $700. + ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 + +- name: 2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 65 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: RI + output: + ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 + +- name: 2025 joint return with both spouses claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent + period: 2025 + input: + people: + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: RI + output: + ri_property_tax_credit_eligible: false + ri_property_tax_credit: 0 + +- name: 2025 joint return with neither spouse claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent + period: 2025 + input: + people: + person_a: + age: 65 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: RI + output: + # Same as above: min($1,000 - 0 x 3%, $700) = $700. + ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 + +- name: 2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 40 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: RI + output: + # The age test and the dependency test apply to the same claimant, + # so the claimed spouse's age does not qualify the other spouse. + ri_property_tax_credit_eligible: false + ri_property_tax_credit: 0 + +- name: 2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 40 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: RI + output: + ri_property_tax_credit_eligible: false + ri_property_tax_credit: 0 + +- name: 2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 40 + is_disabled: true + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: RI + output: + # The disabled, unclaimed spouse is the claimant. + ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 + +- name: 2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 40 + is_disabled: true + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: RI output: ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml index a69c04874f5..cbc54e9ccab 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml @@ -200,3 +200,141 @@ ri_refundable_credits: 250 # 1,000 before refundable credits - 250 refundable credits = 750. ri_income_tax: 750 + +- name: 2021 single filer who can be claimed as a dependent: the 2021 child tax rebate needs a child validly claimed as a dependent + period: 2021 + input: + people: + filer: + age: 20 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: RI + output: + # R.I. Gen. Laws 44-30-103: $250 for each child the taxpayer validly + # claimed as a dependent; under IRC 152(b)(1) a filer who can be + # claimed has no dependents. + ri_child_tax_rebate: 0 + +- name: 2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 20 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # On a joint return either spouse being claimable leaves no + # dependents (Publication 501, Dependent Taxpayer Test). + ri_child_tax_rebate: 0 + +- name: 2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 20 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: RI + output: + ri_child_tax_rebate: 0 + +- name: 2021 joint return with both spouses claimable: the 2021 child tax rebate needs a child validly claimed as a dependent + period: 2021 + input: + people: + person_a: + age: 20 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + ri_child_tax_rebate: 0 + +- name: 2021 joint return with neither spouse claimable: the 2021 child tax rebate needs a child validly claimed as a dependent + period: 2021 + input: + people: + person_a: + age: 20 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 20 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: RI + output: + # One child aged 4: min(1, 3) x $250. + ri_child_tax_rebate: 250 diff --git a/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py b/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py index a69dffa25c4..0567f3df8f0 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py @@ -14,15 +14,19 @@ class ri_property_tax_credit_eligible(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ri.tax.income.credits.property_tax - # minimum age eligibility - greater_head_or_spouse_age = tax_unit("greater_age_head_spouse", period) - age_eligible = greater_head_or_spouse_age >= p.age_threshold - # disability eligibility - head_is_disabled = tax_unit("head_is_disabled", period) - spouse_is_disabled = tax_unit("spouse_is_disabled", period) - head_or_spouse_disabled = head_is_disabled | spouse_is_disabled + person = tax_unit.members + # R.I. Gen. Laws 44-33-3: the claimant is a homeowner or renter aged + # 65 or older or disabled, and "does not include any person claimed as + # a dependent by any taxpayer". Members of a household choose who + # claims, so either spouse may be the claimant, but the age or + # disability and the dependency test belong to the same person. + filer = person("is_tax_unit_head_or_spouse", period) + age_eligible = person("age", period) >= p.age_threshold + disabled = person("is_disabled", period) + claimed = person("claimed_as_dependent_on_another_return", period) + eligible_claimant = filer & (age_eligible | disabled) & ~claimed household_income = tax_unit("ri_property_tax_household_income", period) income_threshold = p.rate.one_person.thresholds[-1] # The tax form RI-1040H specifies the income of a household as a eligibility requirement household_income_eligible = household_income <= income_threshold - return (age_eligible | head_or_spouse_disabled) & household_income_eligible + return tax_unit.any(eligible_claimant) & household_income_eligible diff --git a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py index 32313e54694..1fe1b39f701 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py @@ -18,7 +18,12 @@ def formula(tax_unit, period, parameters): dependent = person("is_tax_unit_dependent", period) p = parameters(period).gov.states.ri.tax.income.credits.child_tax_rebate age_eligibility = age <= p.limit.age - eligible_child = age_eligibility & dependent + # R.I. Gen. Laws 44-30-103(b)(1) pays the rebate for each child "whom + # the eligible taxpayer validly claims as a dependent" on the 2021 + # return. Under IRC 152(b)(1) a return on which the filer (or, if + # joint, either spouse) can be claimed as a dependent has none. + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + eligible_child = age_eligibility & dependent & ~filer_is_dependent child_count = tax_unit.sum(eligible_child) capped_children = min_(child_count, p.limit.child) return capped_children * p.amount From cfb4eb0d8ed2a1ade132c6f7e44a7461d3c3fa2e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:14 -0400 Subject: [PATCH 09/42] Apply South Carolina's rules for a filer who can be claimed as a dependent The dependent exemption (S.C. Code 12-6-1140(13): dependents must meet IRC 151 and 152) and the under-six deduction (12-6-1160) are zero on a return where the filer, or either joint spouse, can be claimed (IRC 152(b)(1)). The EITC and care credit already follow the federal fixes. Co-Authored-By: Claude Opus 5.5 --- .../deductions/sc_young_child_deduction.yaml | 135 +++++++++++++++++ .../exemptions/sc_dependent_exemption.yaml | 137 ++++++++++++++++++ .../deductions/sc_young_child_deduction.py | 13 +- .../exemptions/sc_dependent_exemption.py | 10 +- 4 files changed, 292 insertions(+), 3 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml index c7acead018f..fcfcde04747 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml @@ -72,3 +72,138 @@ # Young child deduction requires child to be under age 6 (ineligible_age = 6) # Child age 6 does NOT qualify - must be younger than 6 sc_young_child_deduction: 0 + +- name: 2025 single filer who can be claimed as a dependent: no South Carolina young child deduction + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: SC + output: + # S.C. Code 12-6-1160 requires a dependent eligible for the South + # Carolina dependent exemption, which a claimable filer has none of. + sc_young_child_deduction: 0 + +- name: 2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + sc_young_child_deduction: 0 + +- name: 2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: SC + output: + sc_young_child_deduction: 0 + +- name: 2025 joint return with both spouses claimable: no South Carolina young child deduction + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + sc_young_child_deduction: 0 + +- name: 2025 joint return with neither spouse claimable: no South Carolina young child deduction + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + # One dependent under 6 x $4,930 (2025). + sc_young_child_deduction: 4930 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml index 23771760581..a8f931ed996 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml @@ -63,3 +63,140 @@ output: # 2025 dependent exemption amount is $4,930; 3 * $4,930 = $14,790. sc_dependent_exemption: 14_790 + +- name: 2025 single filer who can be claimed as a dependent: no South Carolina dependent exemption + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: SC + output: + # S.C. Code 12-6-1140(13) allows the exemption for dependents claimed + # under IRC 151 and 152; IRC 152(b)(1) leaves a claimable filer none. + sc_dependent_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + # Either spouse being claimable leaves the joint return no + # dependents (Publication 501, Dependent Taxpayer Test). + sc_dependent_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: SC + output: + sc_dependent_exemption: 0 + +- name: 2025 joint return with both spouses claimable: no South Carolina dependent exemption + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + sc_dependent_exemption: 0 + +- name: 2025 joint return with neither spouse claimable: no South Carolina dependent exemption + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: SC + output: + # One dependent x $4,930 (2025). + sc_dependent_exemption: 4930 diff --git a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py index 2e827e9eeea..9844e435023 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py @@ -20,7 +20,18 @@ def formula(tax_unit, period, parameters): # Determine eligibility for each person in the tax unit. person = tax_unit.members meets_age_limit = person("age", period) < p.ineligible_age - eligible_dependent = meets_age_limit & person("is_tax_unit_dependent", period) + # Section 12-6-1160 requires a "dependent eligible for the South + # Carolina dependent exemption", which a return on which the filer (or, + # if joint, either spouse) can be claimed as a dependent has none of + # (IRC 152(b)(1), incorporated by Section 12-6-1140(13)). + filer_is_dependent = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + eligible_dependent = ( + meets_age_limit + & person("is_tax_unit_dependent", period) + & ~filer_is_dependent + ) # Count number of eligible people in the tax unit. total_eligible_dependents = tax_unit.sum(eligible_dependent) # Multiply by the amount per eligible dependent. diff --git a/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py b/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py index 77c51754adb..a9fe09799bd 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py @@ -17,7 +17,13 @@ class sc_dependent_exemption(Variable): def formula(tax_unit, period, parameters): # Section 12-6-1140(13): a dependent exemption for every dependent. p = parameters(period).gov.states.sc.tax.income.deductions.dependent_exemption - # every dependent is eligible - dependents = tax_unit("tax_unit_dependents", period) + # Section 12-6-1140(13): "each dependent must meet the eligibility + # requirements of Section 151 and 152", and under IRC 152(b)(1) a + # return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents. + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependents = where( + filer_is_dependent, 0, tax_unit("tax_unit_dependents", period) + ) # Multiply by the amount per exemption. return dependents * p.amount From 932ce9e490cfaf7f88e0fc00ed7c1d4c022cf82f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:14 -0400 Subject: [PATCH 10/42] Apply Utah's rules for a filer who can be claimed as a dependent The taxpayer credit's qualifying dependents (Utah Code 59-10-1018(1)(c): those for whom the claimant is allowed an IRC 24 credit), its birth-year additional dependent and the at-home parent credit's qualifying child (59-10-1005) are zero on a return where the filer, or either joint spouse, can be claimed (IRC 152(b)(1)). The child tax credit and EITC already follow the federal fixes. Co-Authored-By: Claude Opus 5.5 --- .../ut_at_home_parent_credit_potential.yaml | 136 +++++++++++++++++ .../income/credits/ut_personal_exemption.yaml | 140 +++++++++++++++++ .../income/credits/ut_total_dependents.yaml | 141 ++++++++++++++++++ ...exemption_additional_dependent_eligible.py | 8 +- .../taxpayer_credit/ut_total_dependents.py | 12 +- .../ut_at_home_parent_credit_potential.py | 7 +- 6 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml new file mode 100644 index 00000000000..9b5c9f8d9ca --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -0,0 +1,136 @@ +- name: 2025 single filer who can be claimed as a dependent: no Utah at-home parent credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [filer, newborn] + households: + household: + members: [filer, newborn] + state_code: UT + output: + # Utah Code 59-10-1005: the parent must claim the qualifying child as a + # dependent; under IRC 152(b)(1) a claimable filer has no dependents. + ut_at_home_parent_credit_potential: 0 + +- name: 2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 4000 + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + # Either spouse being claimable leaves the joint return no dependents. + ut_at_home_parent_credit_potential: 0 + +- name: 2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 24 + employment_income: 4000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_b, person_a, newborn] + filing_status: JOINT + households: + household: + members: [person_b, person_a, newborn] + state_code: UT + output: + ut_at_home_parent_credit_potential: 0 + +- name: 2025 joint return with both spouses claimable: no Utah at-home parent credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 4000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + ut_at_home_parent_credit_potential: 0 + +- name: 2025 joint return with neither spouse claimable: no Utah at-home parent credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 2000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 4000 + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + # One child under 1 x $100 x one parent earning $3,000 or less + # (the other parent earns $4,000); AGI $6,000 is under $50,000. + ut_at_home_parent_credit_potential: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml index 3e278e3e5a2..958c07d2e6d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml @@ -33,3 +33,143 @@ state_code: UT output: ut_personal_exemption: 5_406 # 1802 * 3 + +- name: 2025 single filer who can be claimed as a dependent: no Utah additional dependent for a child born this year + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [filer, newborn] + households: + household: + members: [filer, newborn] + state_code: UT + output: + # The birth-year dependent (59-10-1018(1)(g)) must also be a dependent + # of the return; IRC 152(b)(1) leaves a claimable filer none. + ut_personal_exemption_additional_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + ut_personal_exemption_additional_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_b, person_a, newborn] + filing_status: JOINT + households: + household: + members: [person_b, person_a, newborn] + state_code: UT + output: + ut_personal_exemption_additional_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with both spouses claimable: no Utah additional dependent for a child born this year + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + ut_personal_exemption_additional_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with neither spouse claimable: no Utah additional dependent for a child born this year + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + # (1 dependent + 1 additional dependent) x $2,111 = $4,222. + ut_personal_exemption_additional_dependents: 1 + ut_personal_exemption: 4222 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml new file mode 100644 index 00000000000..37b86f349a1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml @@ -0,0 +1,141 @@ +- name: 2025 single filer who can be claimed as a dependent: no Utah dependents for the taxpayer credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: UT + output: + # Utah Code 59-10-1018 counts dependents for whom the claimant is + # entitled to an IRC 24 credit; under IRC 152(b)(1) a claimable filer + # has no dependents. + ut_total_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: UT + output: + # Either spouse being claimable leaves the joint return no dependents. + ut_total_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: UT + output: + ut_total_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with both spouses claimable: no Utah dependents for the taxpayer credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: UT + output: + ut_total_dependents: 0 + ut_personal_exemption: 0 + +- name: 2025 joint return with neither spouse claimable: no Utah dependents for the taxpayer credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: UT + output: + # One dependent x $2,111 (2025). + ut_total_dependents: 1 + ut_personal_exemption: 2111 diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py index bf3e43f098f..a364e9adfa2 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py @@ -13,4 +13,10 @@ def formula(person, period, parameters): is_dependent = person("is_tax_unit_dependent", period) birth_year = person("birth_year", period) born_this_year = birth_year == period.start.year - return is_dependent & born_this_year + # The additional dependent must also be a dependent of the return, and + # a return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has none (IRC 152(b)(1)). + filer_is_dependent = person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) + return is_dependent & born_this_year & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py index f9be89959c4..25eaa0048ed 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py @@ -9,4 +9,14 @@ class ut_total_dependents(Variable): documentation = "Form TC-40, line 2c" definition_period = YEAR defined_for = StateCode.UT - adds = ["tax_unit_count_dependents"] + reference = "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1018.html" + + def formula(tax_unit, period, parameters): + # Utah Code 59-10-1018(1)(c) counts "qualifying dependents": those + # "with respect to whom the claimant is allowed to claim a tax credit + # under Section 24". Under IRC 152(b)(1) a return on which the filer + # (or, if joint, either spouse) can be claimed as a dependent has no + # dependents, and so no such credit. + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependents = tax_unit("tax_unit_count_dependents", period) + return where(filer_is_dependent, 0, dependents) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py index 6e18a6211b7..d1ca093e8ec 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py @@ -18,7 +18,12 @@ def formula(tax_unit, period, parameters): age = person("age", period) is_dependent = person("is_tax_unit_dependent", period) p = parameters(period).gov.states.ut.tax.income.credits.at_home_parent - qualifying_child = (age < p.max_child_age) & is_dependent + # Utah Code 59-10-1005 requires that the parent claim the qualifying + # child as a dependent. Under IRC 152(b)(1) a return on which the filer + # (or, if joint, either spouse) can be claimed as a dependent has no + # dependents. + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + qualifying_child = (age < p.max_child_age) & is_dependent & ~filer_is_dependent count_qualifying_children = tax_unit.sum(qualifying_child) # Multiply by each qualifying parent; they can claim it separately. From e7fd1e7bc95147f7b65cc9f8f327c9551bd4b0e8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:28 -0400 Subject: [PATCH 11/42] Apply Vermont's rules for a filer who can be claimed as a dependent - Personal exemptions (32 V.S.A. 5811(21)(C)): no dependents on a return where the filer, or either joint spouse, can be claimed (IRC 152(b)(1)); the own and spouse exemptions already excluded a claimable filer. - Child tax credit (5830f): requires federal child tax credit entitlement for an IRC 152(c) qualifying child; the identification-number waiver does not reach 152(b)(1), so a return with a claimable filer gets none, and a young member who is not a dependent no longer counts. - Renter credit (6066): the claimant may not be claimed as a dependent, but an unclaimed spouse can be the claimant; fair market rent is for a number of bedrooms equal to the personal exemptions (6066(b)(2)). - Student loan interest (5811(21)(B)(vi), (29); IN-112 line 16): from 2022 Vermont subtracts the qualified interest paid less the federal deduction, for AGI up to $200,000 joint or $120,000 otherwise. The model subtracted the raw interest from 2021, deducting it twice for filers who took the federal deduction; a claimed filer, who gets no federal deduction (IRC 221(c)), now subtracts the whole amount. The EITC and care credit already follow the federal fixes. Co-Authored-By: Claude Opus 5.5 --- .../student_loan_interest/income_limit.yaml | 26 ++ .../vt/tax/income/agi/subtractions.yaml | 12 +- .../vt_student_loan_interest_subtraction.yaml | 258 ++++++++++++++++++ .../income/credits/rent/vt_renter_credit.yaml | 142 ++++++++++ .../states/vt/tax/income/credits/vt_ctc.yaml | 159 +++++++++++ .../exemptions/vt_personal_exemptions.yaml | 151 +++++++++- .../vt_student_loan_interest_subtraction.py | 38 +++ .../vt/tax/income/credits/ctc/vt_ctc.py | 12 +- .../income/credits/renter/vt_renter_credit.py | 14 +- .../exemptions/vt_personal_exemptions.py | 11 +- 10 files changed, 813 insertions(+), 10 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml create mode 100644 policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml new file mode 100644 index 00000000000..5798498b482 --- /dev/null +++ b/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml @@ -0,0 +1,26 @@ +description: Vermont allows the student loan interest subtraction only to filers with federal adjusted gross income at or below this amount, by filing status. +metadata: + propagate_metadata_to_children: true + unit: currency-USD + period: year + label: Vermont student loan interest subtraction income limit + breakdown: + - filing_status + reference: + - title: 32 V.S.A. 5811(29)(B) + href: https://legislature.vermont.gov/statutes/section/32/151/05811 + - title: 2022 Act 138, Sec. 4 (effective January 1, 2022) + href: https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=4 + - title: 2025 Vermont income tax booklet, Schedule IN-112 line 16c + href: https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=33 +SINGLE: + 2022-01-01: 120_000 +HEAD_OF_HOUSEHOLD: + 2022-01-01: 120_000 +SEPARATE: + 2022-01-01: 120_000 +SURVIVING_SPOUSE: + # IN-112 line 16c: "All other filers, if AGI is greater than $120,000, enter -0-." + 2022-01-01: 120_000 +JOINT: + 2022-01-01: 200_000 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml index 720da356559..9005a8af60c 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml @@ -3,7 +3,15 @@ values: 2021-01-01: - us_govt_interest - vt_medical_expense_deduction - - student_loan_interest + - vt_capital_gains_exclusion + - vt_retirement_income_exemption + - salt_refund_income + # 2022 Act 138 added the student loan interest subtraction from 2022 + # (32 V.S.A. 5811(21)(B)(vi)); the 2021 Schedule IN-112 has no such line. + 2022-01-01: + - us_govt_interest + - vt_medical_expense_deduction + - vt_student_loan_interest_subtraction - vt_capital_gains_exclusion - vt_retirement_income_exemption - salt_refund_income @@ -11,6 +19,8 @@ metadata: unit: list label: Vermont subtractions reference: + - title: 2025 SCHEDULE IN-112 # Line 16a-16c, student loan interest + href: https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=23 - title: 2024 SCHEDULE IN-112 # PART 1 SUBTRACTIONS FROM FEDERAL ADJUSTED GROSS INCOME href: https://tax.vermont.gov/sites/tax/files/documents/IN-112-2024.pdf#page=1 - title: 2023 SCHEDULE IN-112 # PART 1 SUBTRACTIONS FROM FEDERAL ADJUSTED GROSS INCOME diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml new file mode 100644 index 00000000000..188a3350016 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml @@ -0,0 +1,258 @@ +- name: 2025 single filer who can be claimed as a dependent: Vermont subtracts the student loan interest not deducted federally + period: 2025 + input: + people: + filer: + age: 24 + employment_income: 10000 + student_loan_interest: 1000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + output: + # A claimed filer gets no federal deduction (IRC 221(c)), so Schedule + # IN-112 line 16 subtracts all $1,000 of interest: $10,000 - $1,000. + student_loan_interest_ald: 0 + vt_student_loan_interest_subtraction: 1000 + vt_agi: 9000 + +- name: 2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + output: + # Neither spouse may be claimed for the federal deduction; Vermont + # subtracts the full $1,000. + student_loan_interest_ald: [0, 0] + vt_student_loan_interest_subtraction: 1000 + vt_agi: 9000 + +- name: 2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: VT + output: + student_loan_interest_ald: [0, 0] + vt_student_loan_interest_subtraction: 1000 + vt_agi: 9000 + +- name: 2025 joint return with both spouses claimable: Vermont subtracts the student loan interest not deducted federally + period: 2025 + input: + people: + person_a: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + output: + student_loan_interest_ald: [0, 0] + vt_student_loan_interest_subtraction: 1000 + vt_agi: 9000 + +- name: 2025 joint return with neither spouse claimable: Vermont subtracts the student loan interest not deducted federally + period: 2025 + input: + people: + person_a: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 24 + employment_income: 5000 + student_loan_interest: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + output: + # The federal deduction takes the $1,000 (line 16b), so line 16c is 0; + # federal AGI is already $9,000. Subtracting the raw interest again + # would deduct it twice. + student_loan_interest_ald: [500, 500] + vt_student_loan_interest_subtraction: 0 + vt_agi: 9000 + +- name: 2025 interest above the $2,500 federal cap: Vermont subtracts the excess + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 50000 + student_loan_interest: 4000 + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + output: + # IN-112 line 16c: $4,000 paid - $2,500 deducted federally. + student_loan_interest_ald: 2500 + vt_student_loan_interest_subtraction: 1500 + +- name: 2025 single filer with adjusted gross income above $120,000 gets no Vermont student loan interest subtraction + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 125000 + student_loan_interest: 3000 + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + output: + # 5811(29)(B)(i): AGI must be at most $120,000 unless joint. + vt_student_loan_interest_subtraction: 0 + +- name: 2025 joint return with adjusted gross income under $200,000 subtracts the interest left after the federal phase-out + period: 2025 + input: + people: + head: + age: 30 + employment_income: 195000 + student_loan_interest: 3000 + spouse: + age: 30 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: VT + output: + # Federal: $2,500 x (1 - ($195,000 - $170,000) / $30,000) = $416.67. + # AGI $194,583 is within Vermont's $200,000 joint limit: + # $3,000 - $416.67 = $2,583.33. + student_loan_interest_ald: [416.6667, 0] + vt_student_loan_interest_subtraction: 2583.3333 + +- name: 2025 joint return with adjusted gross income over $200,000 gets no Vermont student loan interest subtraction + period: 2025 + input: + people: + head: + age: 30 + employment_income: 205000 + student_loan_interest: 3000 + spouse: + age: 30 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: VT + output: + # 5811(29)(B)(ii): AGI must be at most $200,000 if joint. + student_loan_interest_ald: [0, 0] + vt_student_loan_interest_subtraction: 0 + +- name: 2021 Vermont has no student loan interest subtraction + period: 2021 + input: + people: + filer: + age: 24 + employment_income: 10000 + student_loan_interest: 1000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + output: + # 2022 Act 138 added 5811(21)(B)(vi) from 2022; the 2021 Schedule + # IN-112 has no such line. + vt_subtractions: 0 + vt_agi: 10000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml index bb540781e29..20795293c25 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml @@ -145,3 +145,145 @@ state_code: VT output: vt_renter_credit: 1_692 + +- name: 2025 single filer who can be claimed as a dependent: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 1000 + rent: 12000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + # 32 V.S.A. 6066(c)(2): the claimant may not be a person claimed as a + # dependent by any taxpayer. + vt_renter_credit: 0 + +- name: 2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + rent: 6000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + rent: 6000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + # The unclaimed spouse is the claimant. 6066(b)(2) sizes fair market + # rent by the number of personal exemptions: one. Income $1,000 is + # below the full-credit limit: round($1,476 x 12 x 10%) = $1,771. + vt_renter_credit: 1771 + +- name: 2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 500 + rent: 6000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 500 + rent: 6000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + vt_renter_credit: 1771 + +- name: 2025 joint return with both spouses claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + rent: 6000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + rent: 6000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + vt_renter_credit: 0 + +- name: 2025 joint return with neither spouse claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + rent: 6000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + rent: 6000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + # Two exemptions: round($1,936 x 12 x 10%) = $2,323. + vt_renter_credit: 2323 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index 757e825b1e3..02c939b31c3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -90,3 +90,162 @@ adjusted_gross_income: 50_000 output: vt_ctc: 0 + +- name: 2025 single filer who can be claimed as a dependent: no Vermont child tax credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: VT + output: + # 32 V.S.A. 5830f requires entitlement to the federal child tax credit + # (waiving only the identification-number rules); IRC 152(b)(1) leaves + # a claimable filer no dependents and so no federal credit. + vt_ctc: 0 + +- name: 2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 1000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # Either spouse being claimable leaves the joint return no dependents. + vt_ctc: 0 + +- name: 2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 1000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: VT + output: + vt_ctc: 0 + +- name: 2025 joint return with both spouses claimable: no Vermont child tax credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 1000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + vt_ctc: 0 + +- name: 2025 joint return with neither spouse claimable: no Vermont child tax credit + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 1000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 1000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # One qualifying child aged 6 or under x $1,000; AGI $2,000 is below + # the $125,000 phase-out start. + vt_ctc: 1000 + +- name: 2025 a young household member who is not a dependent does not count for the Vermont child tax credit + period: 2025 + input: + people: + parent: + age: 30 + employment_income: 2000 + sibling: + age: 5 + is_tax_unit_dependent: false + tax_units: + tax_unit: + members: [parent, sibling] + households: + household: + members: [parent, sibling] + state_code: VT + output: + # 5830f counts qualifying children under IRC 152(c), who are dependents. + vt_ctc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml index 584f51e0e02..f37515b5fdd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml @@ -4,6 +4,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 0 state_code: VT output: @@ -15,10 +16,12 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 2 state_code: VT output: - vt_personal_exemptions: 9_000 # 2*4500 + # IRC 152(b)(1): a return with a claimable filer has no dependents. + vt_personal_exemptions: 0 - name: "Joint household, one claimable elsewhere, with no dependents" period: 2022 @@ -26,6 +29,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: false + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 0 state_code: VT output: @@ -37,10 +41,12 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: false + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 2 state_code: VT output: - vt_personal_exemptions: 13_500 # 3*4500 + # The unclaimed spouse only; IRC 152(b)(1) leaves the return no dependents. + vt_personal_exemptions: 4_500 # 1*4500 - name: "Joint household, neither claimable elsewhere, with no dependents" period: 2022 @@ -69,6 +75,7 @@ input: filing_status: SINGLE head_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 0 state_code: VT output: @@ -79,10 +86,12 @@ input: filing_status: SINGLE head_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere: true tax_unit_count_dependents: 2 state_code: VT output: - vt_personal_exemptions: 9_000 # 2*4500 + # IRC 152(b)(1): a filer who can be claimed has no dependents. + vt_personal_exemptions: 0 - name: "Single household, not claimable elsewhere, with no dependents" period: 2022 @@ -143,3 +152,139 @@ state_code: VT output: vt_personal_exemptions: 13_050 # 3*4350 + +- name: 2025 single filer who can be claimed as a dependent: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: VT + output: + # 32 V.S.A. 5811(21)(C): no exemption for a dependent individual, and + # IRC 152(b)(1) leaves a claimable filer no dependents. + vt_personal_exemptions: 0 + +- name: 2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # The unclaimed spouse's exemption only: 1 x $5,300. + vt_personal_exemptions: 5300 + +- name: 2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: VT + output: + vt_personal_exemptions: 5300 + +- name: 2025 joint return with both spouses claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + vt_personal_exemptions: 0 + +- name: 2025 joint return with neither spouse claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: VT + output: + # 3 exemptions x $5,300 (2025). + vt_personal_exemptions: 15900 diff --git a/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py b/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py new file mode 100644 index 00000000000..a8faf6b6881 --- /dev/null +++ b/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py @@ -0,0 +1,38 @@ +from policyengine_us.model_api import * + + +class vt_student_loan_interest_subtraction(Variable): + value_type = float + entity = TaxUnit + label = "Vermont student loan interest subtraction" + unit = USD + definition_period = YEAR + reference = ( + "https://legislature.vermont.gov/statutes/section/32/151/05811", + "https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=4", + # Schedule IN-112 lines 16a-16c, form and instructions + # PDF pages 23, 33 + "https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=23", + ) + defined_for = StateCode.VT + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.vt.tax.income.agi.student_loan_interest + # 32 V.S.A. 5811(21)(B)(vi) subtracts the interest a qualified resident + # taxpayer paid on a qualified education loan. Schedule IN-112 line 16 + # subtracts the interest paid (16a) less the amount already deducted + # on federal Schedule 1 (16b), so interest is not deducted twice. The + # subtraction has no dependency test of its own: a filer who is + # claimed elsewhere loses the federal deduction (IRC 221(c)), and + # Vermont then subtracts the whole amount. + person = tax_unit.members + filer = person("is_tax_unit_head_or_spouse", period) + interest_paid = tax_unit.sum(filer * person("student_loan_interest", period)) + federal_deduction = add(tax_unit, period, ["student_loan_interest_ald"]) + remaining_interest = max_(interest_paid - federal_deduction, 0) + # 5811(29)(B): a qualified resident taxpayer's adjusted gross income is + # at most $200,000 if married filing jointly and $120,000 otherwise. + agi = tax_unit("adjusted_gross_income", period) + filing_status = tax_unit("filing_status", period) + income_eligible = agi <= p.income_limit[filing_status] + return income_eligible * remaining_interest diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index 6d58a34dfe1..bef5b96211f 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -11,11 +11,19 @@ class vt_ctc(Variable): defined_for = StateCode.VT def formula(tax_unit, period, parameters): - # Get age status of all people in the tax unit. + # 32 V.S.A. 5830f: a taxpayer entitled to a child tax credit under the + # laws of the United States (or who would be but for the taxpayer + # identification number requirements of IRC 24(e) and (h)(7)) gets the + # credit for each qualifying child under IRC 152(c) at or under the + # age limit. The identification waiver does not reach IRC 152(b)(1): + # a return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents and no federal credit. person = tax_unit.members age = person("age", period) p = parameters(period).gov.states.vt.tax.income.credits.ctc - eligible = age <= p.age_limit + dependent = person("is_tax_unit_dependent", period) + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + eligible = (age <= p.age_limit) & dependent & ~filer_is_dependent count_eligible = tax_unit.sum(eligible) # Get maximum credit amount. max_credit = p.amount * count_eligible diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py index c8fd1ec7368..9d6231f20e1 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py @@ -8,6 +8,7 @@ class vt_renter_credit(Variable): unit = USD definition_period = YEAR reference = ( + "https://legislature.vermont.gov/statutes/fullchapter/32/154", # 6066(b)(2), (c)(2) "https://law.justia.com/codes/vermont/2022/title-32/chapter-154/section-6066/", # b "https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=35", # the formula used in this file is based on the excel sheet provided on the official Vermont government website @@ -20,6 +21,11 @@ def formula(tax_unit, period, parameters): # get tax unit size and county. tax_unit_size = tax_unit("tax_unit_size", period) county = tax_unit.household("county", period) + # 32 V.S.A. 6066(b)(2): the fair market rent is for "a number of + # bedrooms equal to the number of personal exemptions" under + # 5811(21)(C), which allows none for a filer who can be claimed as a + # dependent and no dependents on a return where either filer can be. + exemptions = clip(tax_unit("exemptions_count", period), 1, 8) # locate the values by family size and county full_credit_income_limit = p.income_limit_ami.thirty_percent[tax_unit_size][ county @@ -27,7 +33,7 @@ def formula(tax_unit, period, parameters): partial_credit_income_limit = p.income_limit_ami.fifty_percent[tax_unit_size][ county ] - fmr = p.fair_market_rent[tax_unit_size][county] + fmr = p.fair_market_rent[exemptions][county] base_credit_amount = fmr * MONTHS_IN_YEAR * p.fmr_rate # Compute what the spreadsheet calls the "percent rebate claimable" @@ -71,4 +77,8 @@ def formula(tax_unit, period, parameters): # 6066(b)(1)); 2026 Vt. Act 169 raises this for claim year 2027 only. capped_credit = min_(credit_value, p.max_credit) unrounded = capped_credit * (1 - shared_residence_reduction) - return np.round(unrounded) + # 6066(c)(2): the claimant "may not be a person claimed as a dependent + # by any taxpayer". On a joint return either spouse may be the + # claimant, so only a return on which every filer is claimed loses it. + claimant_eligible = ~tax_unit("every_filer_is_dependent_elsewhere", period) + return claimant_eligible * np.round(unrounded) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py b/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py index c93c3526bca..7c1055b60df 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py @@ -18,7 +18,14 @@ def formula(tax_unit, period, parameters): eligible_head = (~elsewhere_head).astype(int) eligible_spouse = (~elsewhere_spouse).astype(int) eligible_count = eligible_head + (eligible_spouse * is_joint) - # add number of other dependents claimed on federal Form 1040 (line 5c) - dependents = tax_unit("tax_unit_count_dependents", period) + # add number of other dependents claimed on federal Form 1040 (line 5c). + # 32 V.S.A. 5811(21)(C): "no exemption may be claimed" for a dependent + # individual, and under IRC 152(b)(1) a return on which the filer (or, + # if joint, either spouse) can be claimed as a dependent has no + # dependents. + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependents = where( + filer_is_dependent, 0, tax_unit("tax_unit_count_dependents", period) + ) total_exemption_count = eligible_count + dependents return total_exemption_count * p.personal From fb81f9d77383bebe58180b7beba668abc6bccc77 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:28 -0400 Subject: [PATCH 12/42] Apply West Virginia's rules for a filer who can be claimed as a dependent - Personal exemptions (W. Va. Code 11-21-16): $2,000 per exemption a filer may claim (none for a claimable filer, no dependents on a return where either joint spouse can be claimed), and a single $500 when the return has none (IT-140: "If box e is zero, enter $500"). The model counted every member. - Low-income family credit (11-21-22a): family size is the number of exemptions that may be legally claimed, and a return with zero exemptions cannot claim the credit. The care credit already follows the federal fix. Co-Authored-By: Claude Opus 5.5 --- ...low_income_family_tax_credit_eligible.yaml | 140 ++++++++++++ .../exemptions/wv_personal_exemption.yaml | 212 ++++++++++++++++++ ...v_low_income_family_tax_credit_eligible.py | 13 +- .../wv_low_income_family_tax_credit_fpg.py | 8 +- .../exemptions/wv_personal_exemption.py | 18 +- 5 files changed, 384 insertions(+), 7 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml index 73a49c95403..c9bf3cd63ef 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml @@ -13,3 +13,143 @@ alternative_minimum_tax: 0 output: wv_low_income_family_tax_credit_eligible: true + +- name: 2025 single filer who can be claimed as a dependent: West Virginia low-income family tax credit uses the exemptions that may be claimed + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 16000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WV + output: + # W. Va. Code 11-21-22A: family size is the number of exemptions that + # may be legally claimed; with zero exemptions there is no credit. + wv_low_income_family_tax_credit_eligible: false + wv_low_income_family_tax_credit_potential: 0 + +- name: 2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + # Family size 1: 2025 FPG $15,650. Taxable income $16,000 - $2,000 = + # $14,000; tax $10,000 x 2.22% + $4,000 x 2.96% = $340.40. Income + # exceeds the FPG by $350, so the credit is 80%: $272.32. + wv_low_income_family_tax_credit_eligible: true + wv_low_income_family_tax_credit_fpg: 15650 + wv_income_tax_before_non_refundable_credits: 340.4 + wv_low_income_family_tax_credit_potential: 272.32 + +- name: 2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WV + output: + wv_low_income_family_tax_credit_eligible: true + wv_low_income_family_tax_credit_fpg: 15650 + wv_income_tax_before_non_refundable_credits: 340.4 + wv_low_income_family_tax_credit_potential: 272.32 + +- name: 2025 joint return with both spouses claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + wv_low_income_family_tax_credit_eligible: false + wv_low_income_family_tax_credit_potential: 0 + +- name: 2025 joint return with neither spouse claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + # Family size 2: $15,650 + $5,500 = $21,150. Taxable income $12,000; + # tax $222 + $2,000 x 2.96% = $281.20; income below the FPG: 100%. + wv_low_income_family_tax_credit_eligible: true + wv_low_income_family_tax_credit_fpg: 21150 + wv_income_tax_before_non_refundable_credits: 281.2 + wv_low_income_family_tax_credit_potential: 281.2 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml index a41435eb495..7e43f04808d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml @@ -28,3 +28,215 @@ tax_unit_size: 2 output: wv_personal_exemption: 4_000 + +- name: 2025 single filer who can be claimed as a dependent: West Virginia personal exemption counts only exemptions that may be claimed + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 16000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WV + output: + # W. Va. Code 11-21-16 and IRC 151(d)(2): no exemption for a filer who + # can be claimed; a return with no exemptions takes $500 instead. + wv_personal_exemption: 500 + +- name: 2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + # One exemption for the spouse who cannot be claimed: $2,000. + wv_personal_exemption: 2000 + +- name: 2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WV + output: + wv_personal_exemption: 2000 + +- name: 2025 joint return with both spouses claimable: West Virginia personal exemption counts only exemptions that may be claimed + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + # No exemptions on the return: the $500 fallback, once per return. + wv_personal_exemption: 500 + +- name: 2025 joint return with neither spouse claimable: West Virginia personal exemption counts only exemptions that may be claimed + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WV + output: + # Two exemptions x $2,000. + wv_personal_exemption: 4000 + +- name: 2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + # IRC 152(b)(1): either spouse being claimable leaves the return no + # dependents; one exemption x $2,000 (the control below gets $6,000). + wv_personal_exemption: 2000 + +- name: 2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 8000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: WV + output: + wv_personal_exemption: 2000 + +- name: 2025 joint return with neither spouse claimable and a child: West Virginia personal exemption + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 8000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 8000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WV + output: + wv_personal_exemption: 6000 diff --git a/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.py b/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.py index 28551572e43..0b45e1eb847 100644 --- a/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.py @@ -6,10 +6,19 @@ class wv_low_income_family_tax_credit_eligible(Variable): entity = TaxUnit label = "Eligible for the West Virginia low-income family tax credit" unit = USD - reference = "https://code.wvlegislature.gov/11-21-22/" + reference = ( + "https://code.wvlegislature.gov/11-21-22/", + "https://code.wvlegislature.gov/11-21-22A/", + "https://tax.wv.gov/Documents/PIT/2025/it140.PersonalIncomeTaxFormsAndInstructions.2025.pdf#page=13", + ) definition_period = YEAR defined_for = StateCode.WV def formula(tax_unit, period, parameters): alternative_minimum_tax = tax_unit("alternative_minimum_tax", period) - return alternative_minimum_tax == 0 + # Family size is the number of exemptions that may be legally claimed + # (W. Va. Code 11-21-22A), so a return with zero exemptions, such as a + # filer who can be claimed as a dependent, cannot claim the credit + # (IT-140 instructions). + has_exemption = tax_unit("exemptions_count", period) > 0 + return (alternative_minimum_tax == 0) & has_exemption diff --git a/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_fpg.py b/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_fpg.py index d61d8af8840..3e4e341b9d0 100644 --- a/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_fpg.py +++ b/policyengine_us/variables/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_fpg.py @@ -9,6 +9,7 @@ class wv_low_income_family_tax_credit_fpg(Variable): ) unit = USD definition_period = YEAR + reference = "https://code.wvlegislature.gov/11-21-22A/" defined_for = "wv_low_income_family_tax_credit_eligible" def formula(tax_unit, period, parameters): @@ -16,8 +17,11 @@ def formula(tax_unit, period, parameters): period ).gov.states.wv.tax.income.credits.liftc # low_income_family_tax_credit - # max family size limit - n = tax_unit("tax_unit_size", period) + # W. Va. Code 11-21-22A: family size is "the total number of exemptions + # that may be legally claimed", not the number of people in the unit. + # A filer who can be claimed as a dependent has no exemption, and a + # return where either filer can be has no dependent exemptions. + n = tax_unit("exemptions_count", period) state_group = tax_unit.household("state_group_str", period) p_fpg = parameters(period).gov.hhs.fpg diff --git a/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py b/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py index 4e685595093..4c729af5874 100644 --- a/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py +++ b/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py @@ -8,9 +8,21 @@ class wv_personal_exemption(Variable): defined_for = StateCode.WV unit = USD definition_period = YEAR - reference = "https://code.wvlegislature.gov/11-21/" + reference = ( + "https://code.wvlegislature.gov/11-21-16/", + # Form IT-140 exemptions box (e) and line 6 instructions + # PDF pages 3, 26 + "https://tax.wv.gov/Documents/PIT/2025/it140.PersonalIncomeTaxFormsAndInstructions.2025.pdf#page=3", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wv.tax.income.exemptions - tax_unit_size = tax_unit("tax_unit_size", period) - return where(tax_unit_size == 0, p.base_personal, p.personal * tax_unit_size) + # W. Va. Code 11-21-16 allows $2,000 per federal exemption. The IT-140 + # instructions allow none for a filer who "can be claimed as a + # dependent on another person's return" and add: "You cannot claim + # any dependents if you can be claimed as a dependent" (IRC 151(d)(2), + # 152(b)(1)). 11-21-16(d) gives an individual whose federal exemption + # is zero under 151(d)(2) a single $500 exemption; the form applies it + # once per return: "If box e is zero, enter $500 on line 6". + exemptions = tax_unit("exemptions_count", period) + return where(exemptions == 0, p.base_personal, p.personal * exemptions) From 21d37ba9c9f91938f0502fa85a09e265a1850ae2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:28 -0400 Subject: [PATCH 13/42] Apply Wisconsin's rules for a filer who can be claimed as a dependent - Standard deduction (Wis. Stat. 71.05(22)(f)): when the filer, or either joint spouse, can be claimed, the lesser of the ordinary deduction and the greater of $1,350 or earned income plus $450 (2025; indexed as the federal amounts), on both the regular and the retirement-exclusion paths. - $250 exemption (Form 1 line 10b): only for a filer aged 65 or older who is allowed the $700 exemption, so each spouse is tested separately. - Homestead credit (71.53; Schedule H): the claimant is the spouse who is 18 or older, has earned income or is 62 or older or disabled, and is not claimed as a dependent unless 62 or older; the $500 household-income deduction counts no dependents on a return with a claimable filer. The $700 exemption and care credits already follow the federal fixes. Co-Authored-By: Claude Opus 5.5 --- .../tax/income/wi_additional_exemption.yaml | 211 ++++++++- .../states/wi/tax/income/wi_exemption.yaml | 26 +- .../wi/tax/income/wi_homestead_eligible.yaml | 401 +++++++++++++++--- .../wi/tax/income/wi_homestead_income.yaml | 151 +++++++ .../wi/tax/income/wi_standard_deduction.yaml | 201 +++++++++ .../homestead/wi_homestead_eligible.py | 45 +- .../credits/homestead/wi_homestead_income.py | 8 +- .../exemptions/wi_additional_exemption.py | 19 +- .../wi_retirement_income_exclusion_tax.py | 10 +- .../wi/tax/income/wi_standard_deduction.py | 27 +- 10 files changed, 1001 insertions(+), 98 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml index c97d3f5d799..6881d0092fe 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml @@ -1,25 +1,220 @@ - name: Aged head period: 2022 input: - age_head: 65 - state_code: WI + people: + head: + age: 65 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: WI output: wi_additional_exemption: 250 - name: Aged head and aged spouse period: 2022 input: - age_head: 65 - age_spouse: 65 - state_code: WI + people: + head: + age: 65 + spouse: + age: 65 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: WI output: wi_additional_exemption: 500 - name: No aged head or aged spouse period: 2022 input: - age_head: 64 - age_spouse: 64 - state_code: WI + people: + head: + age: 64 + spouse: + age: 64 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + households: + household: + members: [head, spouse] + state_code: WI + output: + wi_additional_exemption: 0 + +- name: 2025 single filer who can be claimed as a dependent: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed + period: 2025 + input: + people: + filer: + age: 65 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # Form 1 instructions, line 10b: no $250 exemption for a filer whom + # someone else can claim as a dependent. + wi_additional_exemption: 0 + +- name: 2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Each spouse is tested separately: only the unclaimed spouse counts. + wi_additional_exemption: 250 + +- name: 2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 65 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI + output: + wi_additional_exemption: 250 + +- name: 2025 joint return with both spouses claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed + period: 2025 + input: + people: + person_a: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + wi_additional_exemption: 0 + +- name: 2025 joint return with neither spouse claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed + period: 2025 + input: + people: + person_a: + age: 65 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Two filers aged 65 or older x $250. + wi_additional_exemption: 500 + +- name: 2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 40 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + wi_additional_exemption: 0 + +- name: 2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI output: wi_additional_exemption: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml index 96a1ec8eddb..2efa15ea6b6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml @@ -1,16 +1,32 @@ - name: WI exemption unit test 1 period: 2021 input: - exemptions_count: 5 - state_code: WI + people: + head: + tax_units: + tax_unit: + members: [head] + exemptions_count: 5 + households: + household: + members: [head] + state_code: WI output: wi_exemption: 5 * 700 - name: WI exemption unit test 2 period: 2022 input: - exemptions_count: 5 - age_head: 65 - state_code: WI + people: + head: + age: 65 + tax_units: + tax_unit: + members: [head] + exemptions_count: 5 + households: + household: + members: [head] + state_code: WI output: wi_exemption: 5 * 700 + 250 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index 4802db8710f..b51daff71f6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -1,97 +1,386 @@ - name: wi_homestead_eligible unit test 1 - absolute_error_margin: 0.01 period: 2021 input: - age_head: 18 - age_spouse: 17 - employment_income: 8_000 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 18 + employment_income: 8000 + spouse: + age: 17 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 2 - absolute_error_margin: 0.01 period: 2021 input: - age_head: 17 - age_spouse: 17 - employment_income: 8_000 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 17 + employment_income: 8000 + spouse: + age: 17 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 3 - absolute_error_margin: 0.01 period: 2021 input: - age_head: 18 - age_spouse: 17 - employment_income: 0 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 18 + employment_income: 0 + spouse: + age: 17 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 4 - absolute_error_margin: 0.01 period: 2021 input: - age_head: 62 - age_spouse: 60 - employment_income: 0 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 62 + employment_income: 0 + spouse: + age: 60 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 5 - absolute_error_margin: 0.01 period: 2022 input: - age_head: 61 - age_spouse: 60 - employment_income: 0 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 61 + employment_income: 0 + spouse: + age: 60 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 6 - absolute_error_margin: 0.01 period: 2022 input: - age_head: 61 - age_spouse: 60 - employment_income: 0 - head_is_disabled: false - spouse_is_disabled: true - wi_homestead_income: 9_000 - state_code: WI + people: + head: + age: 61 + employment_income: 0 + spouse: + age: 60 + is_disabled: true + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 9000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 7 - absolute_error_margin: 0.01 period: 2021 input: - age_head: 18 - age_spouse: 17 - employment_income: 8_000 - head_is_disabled: false - spouse_is_disabled: false - wi_homestead_income: 25_000 - state_code: WI + people: + head: + age: 18 + employment_income: 8000 + spouse: + age: 17 + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [head, spouse] + filing_status: JOINT + wi_homestead_income: 25000 + households: + household: + members: [head, spouse] + state_code: WI output: wi_homestead_eligible: false + +- name: 2025 single filer who can be claimed as a dependent: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older + period: 2025 + input: + people: + filer: + age: 30 + employment_income: 1000 + real_estate_taxes: 1000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # Wis. Stat. 71.53(2)(d): no claim by a claimant claimed as a dependent + # by another person, unless the claimant is 62 or older. + wi_homestead_eligible: false + wi_homestead_credit: 0 + +- name: 2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # The unclaimed spouse may be the claimant (Tax 14.02). Household + # income $1,000 is below the $8,060 phaseout start; property tax + # $1,000 x 80% = $800. + wi_homestead_eligible: true + wi_homestead_credit: 800 + +- name: 2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI + output: + wi_homestead_eligible: true + wi_homestead_credit: 800 + +- name: 2025 joint return with both spouses claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + wi_homestead_eligible: false + wi_homestead_credit: 0 + +- name: 2025 joint return with neither spouse claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 500 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Same as above: $1,000 x 80% = $800. + wi_homestead_eligible: true + wi_homestead_credit: 800 + +- name: 2025 joint return with both spouses claimable: Wisconsin homestead credit allows a claimant aged 62 or older even if claimed + period: 2025 + input: + people: + person_a: + age: 62 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 62 + employment_income: 500 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # The age-62 exception keeps both claimed spouses eligible: $800. + wi_homestead_eligible: true + wi_homestead_credit: 800 + +- name: 2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 1000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Schedule H instructions: the spouse with earned income must be the + # claimant, and a claimant under 62 may not be claimed as a dependent; + # the unclaimed spouse has no earned income and is not 62 or disabled. + wi_homestead_eligible: false + wi_homestead_credit: 0 + +- name: 2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 1000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI + output: + wi_homestead_eligible: false + wi_homestead_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 2cea0b82040..0fe68260a6f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -99,3 +99,154 @@ # loss_ald deducts the loss of total_self_employment_income (0 here), so # the add-back follows that total and household income is the same. wi_homestead_income: 5_000 + +- name: 2025 single filer who can be claimed as a dependent: no Wisconsin homestead $500 dependent deduction + period: 2025 + input: + people: + filer: + age: 62 + employment_income: 10000 + real_estate_taxes: 1000 + claimed_as_dependent_on_another_return: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: WI + output: + # The $500 deduction is for a dependent under IRC 152, and a claimant + # who can be claimed has none (IRC 152(b)(1)). The claimant, 62 or + # older, stays eligible: ($1,000 - ($10,000 - $8,060) x 8.785%) x 80%. + wi_homestead_income: 10000 + wi_homestead_credit: 663.6568 + +- name: 2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # Either spouse being claimable leaves the joint return no dependents. + wi_homestead_income: 10000 + wi_homestead_credit: 663.6568 + +- name: 2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: WI + output: + wi_homestead_income: 10000 + wi_homestead_credit: 663.6568 + +- name: 2025 joint return with both spouses claimable: no Wisconsin homestead $500 dependent deduction + period: 2025 + input: + people: + person_a: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + wi_homestead_income: 10000 + wi_homestead_credit: 663.6568 + +- name: 2025 joint return with neither spouse claimable: no Wisconsin homestead $500 dependent deduction + period: 2025 + input: + people: + person_a: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 62 + employment_income: 5000 + real_estate_taxes: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 4 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: WI + output: + # $10,000 - $500 = $9,500; ($1,000 - $1,440 x 8.785%) x 80% = $698.7968. + wi_homestead_income: 9500 + wi_homestead_credit: 698.7968 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml index 24ec29f0189..b50cbb6021f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml @@ -97,3 +97,204 @@ # same amount through the 22.515% and 12% segments that meet at $58,827, # within $0.05 of the schedule. wi_standard_deduction: 13_960 - 0.12 * (70_000 - 20_120) + +- name: 2025 single filer who can be claimed as a dependent: Wisconsin standard deduction limited for a filer who can be claimed + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 1000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # Wis. Stat. 71.05(22)(f): the lesser of the ordinary deduction and + # max($1,350, earned income + $450) = max($1,350, $1,000 + $450). + wi_standard_deduction: 1450 + +- name: 2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Form 1 worksheet: applies if you or your spouse can be claimed; the + # couple's earned income is $1,000, so $1,450. + wi_standard_deduction: 1450 + +- name: 2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 22 + employment_income: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 22 + employment_income: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI + output: + wi_standard_deduction: 1450 + +- name: 2025 joint return with both spouses claimable: Wisconsin standard deduction limited for a filer who can be claimed + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 500 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + wi_standard_deduction: 1450 + +- name: 2025 joint return with neither spouse claimable: Wisconsin standard deduction limited for a filer who can be claimed + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 500 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + employment_income: 500 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Joint maximum at WI income $1,000: $25,110 (2025). + wi_standard_deduction: 25110 + +- name: 2025 single filer who cannot be claimed gets the full Wisconsin standard deduction + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 1000 + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + wi_standard_deduction: 13560 + +- name: 2025 single filer who can be claimed and has no earned income gets the Wisconsin dependent floor + period: 2025 + input: + people: + filer: + age: 20 + taxable_interest_income: 2000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # max($1,350, $0 + $450) = $1,350. + wi_standard_deduction: 1350 + +- name: 2021 single filer who can be claimed: Wisconsin dependent standard deduction + period: 2021 + input: + people: + filer: + age: 20 + employment_income: 600 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # 2021 Form 1 worksheet: max($1,100, $600 + $350) = $1,100. + wi_standard_deduction: 1100 + +- name: 2023 single filer who can be claimed: Wisconsin dependent standard deduction + period: 2023 + input: + people: + filer: + age: 20 + employment_income: 3000 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # 2023 Form 1 worksheet: max($1,250, $3,000 + $400) = $3,400. + wi_standard_deduction: 3400 diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py index c616d3e4e0e..a99a39f3a0d 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py @@ -6,29 +6,34 @@ class wi_homestead_eligible(Variable): entity = TaxUnit label = "Wisconsin homestead credit eligibility status" definition_period = YEAR - reference = "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7" + reference = ( + "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7", + # Schedule H instructions, Steps 1 and 2 and questions 1c-1d + # PDF pages 4, 10 + "https://www.revenue.wi.gov/TaxForms2025/2025-ScheduleH-inst.pdf#page=4", + ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): - p = parameters(period).gov.states.wi.tax.income.credits - # minimum age eligibility - head_age = tax_unit("age_head", period) - spouse_age = tax_unit("age_spouse", period) - min_age = p.homestead.eligible.min_age - age_eligible = (head_age >= min_age) | (spouse_age >= min_age) - # earnings eligibility - earnings = add(tax_unit, period, p.homestead.eligible.earnings_sources) - has_positive_earnings = earnings > 0 - min_elderly_age = p.homestead.eligible.min_elderly_age - head_is_elderly = head_age >= min_elderly_age - spouse_is_elderly = spouse_age >= min_elderly_age - is_elderly = head_is_elderly | spouse_is_elderly - head_is_disabled = tax_unit("head_is_disabled", period) - spouse_is_disabled = tax_unit("spouse_is_disabled", period) - is_disabled = head_is_disabled | spouse_is_disabled - earnings_eligible = has_positive_earnings | is_elderly | is_disabled + p = parameters(period).gov.states.wi.tax.income.credits.homestead.eligible + person = tax_unit.members + filer = person("is_tax_unit_head_or_spouse", period) + age = person("age", period) + elderly = age >= p.min_elderly_age + # The claimant must be 18 or older and must be 62 or older, disabled + # or have earned income; the Schedule H instructions add, for a + # married couple, "The spouse that meets this definition must be the + # claimant." + earnings = add(person, period, p.earnings_sources) + disabled = person("is_disabled", period) + qualifies = (age >= p.min_age) & (elderly | disabled | (earnings > 0)) + # Wis. Stat. 71.53(2)(d) and Schedule H Step 2a: no claim by a + # claimant who was or will be claimed as a dependent on another + # person's federal return, unless the claimant is 62 or older. + claimed = person("claimed_as_dependent_on_another_return", period) + eligible_claimant = filer & qualifies & (~claimed | elderly) # income eligibility homestead_income = tax_unit("wi_homestead_income", period) - income_eligible = homestead_income < p.homestead.eligible.max_income + income_eligible = homestead_income < p.max_income # overall eligibility - return age_eligible & earnings_eligible & income_eligible + return tax_unit.any(eligible_claimant) & income_eligible diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py index 7ab07ce19be..6fec858fbc0 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py @@ -18,5 +18,11 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wi.tax.income.credits income = add(tax_unit, period, p.homestead.income.sources) disqualified_losses = tax_unit("wi_homestead_disqualified_losses", period) - dependents = tax_unit("tax_unit_dependents", period) + # The $500 deduction is for each dependent under IRC 152; a return on + # which the claimant (or, if joint, either spouse) can be claimed as a + # dependent has none (IRC 152(b)(1)). + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependents = where( + filer_is_dependent, 0, tax_unit("tax_unit_dependents", period) + ) return income + disqualified_losses - dependents * p.homestead.income.exemption diff --git a/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py b/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py index c270c849cba..a8a3a4218f3 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py @@ -13,14 +13,23 @@ class wi_additional_exemption(Variable): "https://www.revenue.wi.gov/TaxForms2022/2022-Form1f.pdf", "https://www.revenue.wi.gov/TaxForms2022/2022-Form1-Inst.pdf", "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2023/0002_individual_income_tax_informational_paper_2.pdf", + # Lines 10a and 10b + # PDF pages 15-16 + "https://www.revenue.wi.gov/TaxForms2025/2025-Form1-Inst.pdf#page=15", ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): # compute extra exemption amount p = parameters(period).gov.states.wi.tax.income - elderly_head = (tax_unit("age_head", period) >= p.exemption.old_age).astype(int) - elderly_spouse = (tax_unit("age_spouse", period) >= p.exemption.old_age).astype( - int - ) - return (elderly_head + elderly_spouse) * p.exemption.extra + person = tax_unit.members + filer = person("is_tax_unit_head_or_spouse", period) + elderly = person("age", period) >= p.exemption.old_age + # Form 1 instructions, line 10b: the $250 exemption is for "you and/or + # your spouse only if you and/or your spouse are 65 years of age or + # older and are allowed the $700 exemption on line 10a", which a + # filer who can be claimed as a dependent is not. Each spouse is + # tested separately. + claimed = person("claimed_as_dependent_on_another_return", period) + eligible_filers = tax_unit.sum(filer & elderly & ~claimed) + return eligible_filers * p.exemption.extra diff --git a/policyengine_us/variables/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.py b/policyengine_us/variables/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.py index ff92acb6368..7ca795c93dc 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.py @@ -1,5 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.wi.tax.income.wi_standard_deduction import ( + wi_dependent_standard_deduction_limit, wi_standard_deduction_for_income, ) @@ -35,8 +36,13 @@ def formula(tax_unit, period, parameters): fstatus = tax_unit("filing_status", period) wi_agi = tax_unit("wi_agi", period) standard_deduction_full = tax_unit("wi_standard_deduction", period) - standard_deduction_reduced = wi_standard_deduction_for_income( - max_(0, wi_agi - line16), fstatus, parameters, period + # The dependent limit (71.05(22)(f)) caps the deduction on this path + # too; it depends on earned income, which line 16 does not change. + standard_deduction_reduced = min_( + wi_standard_deduction_for_income( + max_(0, wi_agi - line16), fstatus, parameters, period + ), + wi_dependent_standard_deduction_limit(tax_unit, period, parameters), ) extra_standard_deduction = standard_deduction_reduced - standard_deduction_full exclusion_taxinc = max_(0, taxinc - line16 - extra_standard_deduction) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py b/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py index 362bdf7f14b..fe0d2008d2d 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py @@ -29,6 +29,23 @@ def wi_standard_deduction_for_income(income, filing_status, parameters, period): return max_(0, max_amount - phase_out_amount) +def wi_dependent_standard_deduction_limit(tax_unit, period, parameters): + # Wis. Stat. 71.05(22)(f): for a taxpayer whom another person can claim + # as a dependent, the standard deduction is the lesser of the ordinary + # deduction and the greater of $500 or earned income (IRC 911(d)(2)) plus + # $250, both indexed "in the manner prescribed by sections 1 (f) (3) to + # (6) and 63 (c) (4)" of the IRC, so they equal the federal amounts (Form + # 1 instructions: $1,100 + $350 in 2021 through $1,350 + $450 in 2025). + # The Form 1 worksheet applies when "you (or your spouse if filing a + # joint return) can be claimed", using the return's earned income. + # Returns infinity when no filer can be claimed. + p = parameters(period).gov.irs.deductions.standard.dependent + earned_income = max_(tax_unit("tax_unit_earned_income", period), 0) + limit = max_(p.amount, earned_income + p.additional_earned_income) + filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + return where(filer_is_dependent, limit, np.inf) + + class wi_standard_deduction(Variable): value_type = float entity = TaxUnit @@ -44,10 +61,18 @@ class wi_standard_deduction(Variable): # Standard Deduction Table (keyed to WI income, line 7) and the statute, corroborating the phaseout: "https://www.revenue.wi.gov/TaxForms2025/2025-Form1-inst.pdf#page=35", "https://docs.legis.wisconsin.gov/statutes/statutes/71/i/05/22", + # Standard Deduction Worksheet for Dependents + "https://www.revenue.wi.gov/TaxForms2025/2025-Form1-Inst.pdf#page=15", ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): fstatus = tax_unit("filing_status", period) agi = tax_unit("wi_agi", period) - return wi_standard_deduction_for_income(agi, fstatus, parameters, period) + standard_deduction = wi_standard_deduction_for_income( + agi, fstatus, parameters, period + ) + return min_( + standard_deduction, + wi_dependent_standard_deduction_limit(tax_unit, period, parameters), + ) From 74a7b9efec911e9003e11a9cd9f1e57902b6d208 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:11:28 -0400 Subject: [PATCH 14/42] Add claimable-filer invariants for RI, SC, UT, VT, WV and WI Swap invariance, monotonicity and identities (dependent-based amounts are zero on a return with a claimable filer; a return whose every filer is claimed gets no RI relief or VT renter credit and West Virginia's single $500 exemption), plus the changelog fragment. Co-Authored-By: Claude Opus 5.5 --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 1 + ...able_filer_ri_sc_ut_vt_wv_wi_invariants.py | 119 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md create mode 100644 policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md new file mode 100644 index 00000000000..d5140a8e085 --- /dev/null +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -0,0 +1 @@ +Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent (dependent exemptions and deductions, child credits, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, and the RI, VT and WI property tax and renter claimant tests), and subtract only the Vermont student loan interest not already deducted federally, from 2022. diff --git a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py new file mode 100644 index 00000000000..d76bd0dd0ca --- /dev/null +++ b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py @@ -0,0 +1,119 @@ +"""Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin +rules for a filer who can be claimed as a dependent. + +A return on which the filer (or, if joint, either spouse) can be claimed has +no dependents (IRC 152(b)(1)), so the dependent-based amounts below are zero +on it. Claimant rules (RI property tax relief, VT renter credit, WI homestead +credit) and per-filer exemptions (WV, WI $250) test each spouse separately. + +For couples drawn by Hypothesis and a seeded population in these states, +with either, both or neither spouse claimed: + +1. Swap invariance: each output is the same under either head/spouse + labelling. +2. Monotonicity: marking another filer as claimed never raises the amounts + in MONOTONE. The Vermont student loan subtraction, Wisconsin homestead + income and the West Virginia family credit are left out by design: the + first replaces the federal deduction a claimed filer loses, and the other + two rise when a dependent deduction or exemption goes away. +3. Identities: the dependent-based amounts are zero on a return with a + claimable filer, and a return whose every filer is claimed gets no RI + property tax relief or VT renter credit and the single $500 West Virginia + exemption. +""" + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us.tests.core.test_claimable_filer_federal_invariants import ( + check_monotone, + check_swap, +) +from policyengine_us.tests.core.test_dependent_elsewhere_head_spouse_swap_invariance import ( + CLAIM_PATTERNS, + _calc, + _crafted_cases, + _seeded_couples, + adults, + dependents, +) + +STATES = ["RI", "SC", "UT", "VT", "WV", "WI"] +YEARS = [2021, 2023, 2025, 2026] +# Zero on a return where either filer can be claimed (IRC 152(b)(1)). +DEPENDENT_BASED = [ + "ri_child_tax_rebate", + "sc_dependent_exemption", + "sc_young_child_deduction", + "ut_total_dependents", + "ut_personal_exemption", + "ut_at_home_parent_credit_potential", + "vt_ctc", +] +MONOTONE = DEPENDENT_BASED + [ + "ri_property_tax_credit", + "vt_personal_exemptions", + "vt_renter_credit", + "wv_personal_exemption", + "wv_low_income_family_tax_credit_eligible", + "wv_low_income_family_tax_credit_fpg", + "wi_standard_deduction", + "wi_additional_exemption", + "wi_homestead_credit", +] +OUTPUTS = MONOTONE + [ + "vt_student_loan_interest_subtraction", + "wv_low_income_family_tax_credit", + "wi_homestead_income", + "wi_retirement_income_exclusion_tax", + "state_income_tax", +] + + +@st.composite +def couples(draw): + return { + "state": draw(st.sampled_from(STATES)), + "adults": [draw(adults()), draw(adults())], + "dependents": draw(st.lists(dependents(), max_size=2)), + "claimed": draw(st.sampled_from(CLAIM_PATTERNS)), + } + + +def _identities(sim, year): + either = _calc(sim, "head_or_spouse_is_dependent_elsewhere", year) > 0 + every = _calc(sim, "every_filer_is_dependent_elsewhere", year) > 0 + for name in DEPENDENT_BASED: + values = _calc(sim, name, year) + assert not values[either].any(), ( + f"{name} in {year} is positive on a return with a claimable filer" + ) + for name in ["ri_property_tax_credit", "vt_renter_credit"]: + assert not _calc(sim, name, year)[every].any(), ( + f"{name} in {year} is positive when every filer is claimed" + ) + state = np.asarray(sim.calculate("state_code_str", year, map_to="tax_unit")) + wv_every = every & (state == "WV") + np.testing.assert_array_equal( + _calc(sim, "wv_personal_exemption", year)[wv_every], 500 + ) + + +@settings( + max_examples=2, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.sampled_from(YEARS), st.lists(couples(), min_size=6, max_size=12)) +def test_ri_sc_ut_vt_wv_wi_claimable_filer_rules_are_label_free(year, units): + check_swap(units, year, OUTPUTS, _identities) + + +@pytest.mark.parametrize("year", YEARS) +def test_ri_sc_ut_vt_wv_wi_claimable_filer_rules_seeded_population(year): + units = _seeded_couples(STATES, per_state=2) + _crafted_cases(STATES) + check_swap(units, year, OUTPUTS, _identities) + check_monotone(units, year, MONOTONE, _identities) From 3c39849d4a0f19e3859fd70b1ac04960d7d4d8c5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:13:04 -0400 Subject: [PATCH 15/42] Pay Utah's at-home parent credit once per qualifying child per return Utah Code 59-10-1005(2) allows $100 "for each qualifying child if ... the claimant or another claimant filing a joint individual income tax return with the claimant is an at-home parent". The model multiplied the credit by the number of spouses earning $3,000 or less, doubling it for such couples; the existing two-parent test asserted $400 for two children and now expects $200. Co-Authored-By: Claude Opus 5.5 --- .../credits/ut_at_home_parent_credit.yaml | 6 +++-- .../ut_at_home_parent_credit_potential.yaml | 27 +++++++++++++++++++ .../ut_at_home_parent_credit_potential.py | 19 ++++++++----- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml index b582eaadba7..e1cb5eeedd3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml @@ -55,7 +55,7 @@ state_code: UT output: ut_at_home_parent_credit: 0 -- name: 2 eligible parents with 1 child +- name: 1 eligible parent with 2 children period: 2022 input: people: @@ -123,4 +123,6 @@ - person4 state_code: UT output: - ut_at_home_parent_credit: 400 + # Utah Code 59-10-1005(2): $100 for each qualifying child when the claimant + # or a joint-filing spouse is an at-home parent, once per return. + ut_at_home_parent_credit: 200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml index 9b5c9f8d9ca..ddaeebc5a38 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -134,3 +134,30 @@ # One child under 1 x $100 x one parent earning $3,000 or less # (the other parent earns $4,000); AGI $6,000 is under $50,000. ut_at_home_parent_credit_potential: 100 + +- name: 2025 two at-home parents with one newborn: one Utah at-home parent credit per child on the return + period: 2025 + input: + people: + head: + age: 30 + employment_income: 2000 + spouse: + age: 30 + employment_income: 1000 + is_tax_unit_spouse: true + newborn: + age: 0 + tax_units: + tax_unit: + members: [head, spouse, newborn] + filing_status: JOINT + households: + household: + members: [head, spouse, newborn] + state_code: UT + output: + # Utah Code 59-10-1005(2): $100 for each qualifying child if the claimant + # or a joint-filing spouse is an at-home parent; both spouses earning + # $3,000 or less does not double it. + ut_at_home_parent_credit_potential: 100 diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py index d1ca093e8ec..6568fdba57f 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py @@ -10,6 +10,7 @@ class ut_at_home_parent_credit_potential(Variable): defined_for = "ut_at_home_parent_credit_agi_eligible" reference = ( "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1005.html", + "https://le.utah.gov/xcode/Title59/Chapter10/C59-10-S1005_2026050620260506.pdf#page=1", "https://www.taxformfinder.org/forms/2021/2021-utah-tc-40-full-packet.pdf#page=23", ) @@ -26,11 +27,17 @@ def formula(tax_unit, period, parameters): qualifying_child = (age < p.max_child_age) & is_dependent & ~filer_is_dependent count_qualifying_children = tax_unit.sum(qualifying_child) - # Multiply by each qualifying parent; they can claim it separately. - income_eligible_person = add( - tax_unit, - period, - ["ut_at_home_parent_credit_earned_income_eligible_person"], + # 59-10-1005(2): $100 "for each qualifying child if ... the claimant or + # another claimant filing a joint individual income tax return with + # the claimant is an at-home parent", so one credit per child on the + # return however many spouses qualify. + has_at_home_parent = ( + add( + tax_unit, + period, + ["ut_at_home_parent_credit_earned_income_eligible_person"], + ) + > 0 ) - return p.amount * count_qualifying_children * income_eligible_person + return p.amount * count_qualifying_children * has_at_home_parent From 52fa346030c1090634d0405d5e8f93429a702df7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:14:21 -0400 Subject: [PATCH 16/42] Phase out Vermont's child tax credit per child 32 V.S.A. 5830f(b) reduces "the amount of the credit per child" by $20 for each $1,000 of AGI over $125,000; the model reduced the total once, so a family with two young children at $150,000 got $1,500 instead of $1,000. The existing phase-out and age cases used the filer as the child; they now have a parent and a child, since 5830f counts IRC 152(c) qualifying children. Co-Authored-By: Claude Opus 5.5 --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 +- .../states/vt/tax/income/credits/vt_ctc.yaml | 137 +++++++++++++++--- .../vt/tax/income/credits/ctc/vt_ctc.py | 11 +- 3 files changed, 121 insertions(+), 29 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index d5140a8e085..7d77d06cd1a 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1 +1 @@ -Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent (dependent exemptions and deductions, child credits, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, and the RI, VT and WI property tax and renter claimant tests), and subtract only the Vermont student loan interest not already deducted federally, from 2022. +Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent (dependent exemptions and deductions, child credits, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, and the RI, VT and WI property tax and renter claimant tests). Also subtract only the Vermont student loan interest not already deducted federally, from 2022; phase out Vermont's child tax credit per child; and pay Utah's at-home parent credit once per qualifying child per return. diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index 02c939b31c3..c5845ae7a47 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -30,64 +30,157 @@ - name: Fully phases out at $175,000. 1000-20*(175000-125000)/1000 = 0 period: 2023 input: - state_code: VT - age: 5 - adjusted_gross_income: 175_000 + people: + parent: + age: 35 + child1: + age: 5 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 175_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 0 - name: Check we're not creating a negative value. period: 2023 input: - state_code: VT - age: 5 - adjusted_gross_income: 200_000 + people: + parent: + age: 35 + child1: + age: 5 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 200_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 0 - name: Halfway phased out at $150,000. 1000-20*(150000-125000)/1000 = 500 period: 2023 input: - state_code: VT - age: 5 - adjusted_gross_income: 150_000 + people: + parent: + age: 35 + child1: + age: 5 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 150_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 500 +- name: Two children halfway phased out at $150,000: each child's $1,000 is reduced by $500 + period: 2023 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 3 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 150_000 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # 32 V.S.A. 5830f(b): the credit per child is reduced by $20 per $1,000 + # over $125,000: 2 x ($1,000 - 25 x $20) = $1,000. + vt_ctc: 1_000 + # S.51 (2025) tests for expanded age eligibility - name: S.51 - CTC includes 6-year-olds starting 2025 period: 2025 input: - state_code: VT - age: 6 - adjusted_gross_income: 50_000 + people: + parent: + age: 35 + child1: + age: 6 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 50_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 1_000 - name: S.51 - CTC still applies to 5-year-olds in 2025 period: 2025 input: - state_code: VT - age: 5 - adjusted_gross_income: 50_000 + people: + parent: + age: 35 + child1: + age: 5 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 50_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 1_000 - name: S.51 - CTC does not apply to 7-year-olds in 2025 period: 2025 input: - state_code: VT - age: 7 - adjusted_gross_income: 50_000 + people: + parent: + age: 35 + child1: + age: 7 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 50_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 0 - name: S.51 - CTC did not include 6-year-olds before 2025 period: 2024 input: - state_code: VT - age: 6 - adjusted_gross_income: 50_000 + people: + parent: + age: 35 + child1: + age: 6 + tax_units: + tax_unit: + members: [parent, child1] + adjusted_gross_income: 50_000 + households: + household: + members: [parent, child1] + state_code: VT output: vt_ctc: 0 @@ -200,7 +293,7 @@ output: vt_ctc: 0 -- name: 2025 joint return with neither spouse claimable: no Vermont child tax credit +- name: 2025 joint return with neither spouse claimable (control): Vermont child tax credit period: 2025 input: people: diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index bef5b96211f..887bc55968c 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -25,13 +25,12 @@ def formula(tax_unit, period, parameters): filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) eligible = (age <= p.age_limit) & dependent & ~filer_is_dependent count_eligible = tax_unit.sum(eligible) - # Get maximum credit amount. - max_credit = p.amount * count_eligible # Get adjusted gross income. agi = tax_unit("adjusted_gross_income", period) - # Reduce credit amount over the phaseout range. + # 5830f(b): "the amount of the credit per child" is reduced, but not + # below zero, by $20 for each $1,000 of AGI over $125,000. excess_agi = max_(agi - p.reduction.start, 0) increments = np.ceil(excess_agi / p.reduction.increment) - total_reduction = p.reduction.amount * increments - # Return reduced credit amount. - return max_(max_credit - total_reduction, 0) + reduction_per_child = p.reduction.amount * increments + credit_per_child = max_(p.amount - reduction_per_child, 0) + return count_eligible * credit_per_child From d7bea36ce394c2bfd6dcbc08e741f290fc26ee7f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:14:21 -0400 Subject: [PATCH 17/42] Name the nobody-claimed YAML cases as controls Co-Authored-By: Claude Opus 5.5 --- .../sc/tax/income/deductions/sc_young_child_deduction.yaml | 2 +- .../states/sc/tax/income/exemptions/sc_dependent_exemption.yaml | 2 +- .../tax/income/credits/ut_at_home_parent_credit_potential.yaml | 2 +- .../gov/states/ut/tax/income/credits/ut_personal_exemption.yaml | 2 +- .../gov/states/ut/tax/income/credits/ut_total_dependents.yaml | 2 +- .../baseline/gov/states/wi/tax/income/wi_homestead_income.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml index fcfcde04747..19ac591c971 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml @@ -180,7 +180,7 @@ output: sc_young_child_deduction: 0 -- name: 2025 joint return with neither spouse claimable: no South Carolina young child deduction +- name: 2025 joint return with neither spouse claimable (control): South Carolina young child deduction period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml index a8f931ed996..9ca9fc9890e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml @@ -173,7 +173,7 @@ output: sc_dependent_exemption: 0 -- name: 2025 joint return with neither spouse claimable: no South Carolina dependent exemption +- name: 2025 joint return with neither spouse claimable (control): South Carolina dependent exemption period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml index ddaeebc5a38..4b4c0309e0f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -106,7 +106,7 @@ output: ut_at_home_parent_credit_potential: 0 -- name: 2025 joint return with neither spouse claimable: no Utah at-home parent credit +- name: 2025 joint return with neither spouse claimable (control): Utah at-home parent credit period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml index 958c07d2e6d..b8f62c758d2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml @@ -145,7 +145,7 @@ ut_personal_exemption_additional_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with neither spouse claimable: no Utah additional dependent for a child born this year +- name: 2025 joint return with neither spouse claimable (control): Utah additional dependent for a child born this year period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml index 37b86f349a1..7ad60a085e3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml @@ -111,7 +111,7 @@ ut_total_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with neither spouse claimable: no Utah dependents for the taxpayer credit +- name: 2025 joint return with neither spouse claimable (control): Utah dependents for the taxpayer credit period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 0fe68260a6f..3b4055b5836 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -220,7 +220,7 @@ wi_homestead_income: 10000 wi_homestead_credit: 663.6568 -- name: 2025 joint return with neither spouse claimable: no Wisconsin homestead $500 dependent deduction +- name: 2025 joint return with neither spouse claimable (control): Wisconsin homestead $500 dependent deduction period: 2025 input: people: From 52faacfb9fd972ecb3ad78bd99ccf5e725c8342e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:18:49 -0400 Subject: [PATCH 18/42] Quote YAML case names that contain a colon Co-Authored-By: Claude Opus 5.5 --- .../ri_property_tax_credit_eligible.yaml | 18 +++++++++--------- .../credits/rebates/ri_child_tax_rebate.yaml | 10 +++++----- .../deductions/sc_young_child_deduction.yaml | 10 +++++----- .../exemptions/sc_dependent_exemption.yaml | 10 +++++----- .../ut_at_home_parent_credit_potential.yaml | 12 ++++++------ .../income/credits/ut_personal_exemption.yaml | 10 +++++----- .../income/credits/ut_total_dependents.yaml | 10 +++++----- .../vt_student_loan_interest_subtraction.yaml | 12 ++++++------ .../income/credits/rent/vt_renter_credit.yaml | 10 +++++----- .../states/vt/tax/income/credits/vt_ctc.yaml | 12 ++++++------ .../exemptions/vt_personal_exemptions.yaml | 10 +++++----- .../wi/tax/income/wi_additional_exemption.yaml | 14 +++++++------- .../wi/tax/income/wi_homestead_eligible.yaml | 16 ++++++++-------- .../wi/tax/income/wi_homestead_income.yaml | 10 +++++----- .../wi/tax/income/wi_standard_deduction.yaml | 14 +++++++------- ..._low_income_family_tax_credit_eligible.yaml | 10 +++++----- .../exemptions/wv_personal_exemption.yaml | 16 ++++++++-------- 17 files changed, 102 insertions(+), 102 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml index 1e828db6a9d..940a617cba9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml @@ -109,7 +109,7 @@ output: ri_property_tax_credit_eligible: true -- name: 2025 single filer who can be claimed as a dependent: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent +- name: "2025 single filer who can be claimed as a dependent: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent" period: 2025 input: people: @@ -130,7 +130,7 @@ ri_property_tax_credit_eligible: false ri_property_tax_credit: 0 -- name: 2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the head)" period: 2025 input: people: @@ -161,7 +161,7 @@ ri_property_tax_credit_eligible: true ri_property_tax_credit: 700 -- name: 2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent (the claimable spouse is the spouse)" period: 2025 input: people: @@ -188,7 +188,7 @@ ri_property_tax_credit_eligible: true ri_property_tax_credit: 700 -- name: 2025 joint return with both spouses claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent +- name: "2025 joint return with both spouses claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent" period: 2025 input: people: @@ -216,7 +216,7 @@ ri_property_tax_credit_eligible: false ri_property_tax_credit: 0 -- name: 2025 joint return with neither spouse claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent +- name: "2025 joint return with neither spouse claimable: property tax relief needs a claimant aged 65 or older or disabled who is not claimed as a dependent" period: 2025 input: people: @@ -243,7 +243,7 @@ ri_property_tax_credit_eligible: true ri_property_tax_credit: 700 -- name: 2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the head) +- name: "2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the head)" period: 2025 input: people: @@ -272,7 +272,7 @@ ri_property_tax_credit_eligible: false ri_property_tax_credit: 0 -- name: 2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the spouse) +- name: "2025 joint return where only the claimable spouse is 65 or older: property tax relief has no eligible claimant (the claimable spouse is the spouse)" period: 2025 input: people: @@ -299,7 +299,7 @@ ri_property_tax_credit_eligible: false ri_property_tax_credit: 0 -- name: 2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the head) +- name: "2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the head)" period: 2025 input: people: @@ -328,7 +328,7 @@ ri_property_tax_credit_eligible: true ri_property_tax_credit: 700 -- name: 2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the spouse) +- name: "2025 joint return where the unclaimed spouse is disabled: property tax relief stays available (the claimable spouse is the spouse)" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml index cbc54e9ccab..c59f55ea156 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml @@ -201,7 +201,7 @@ # 1,000 before refundable credits - 250 refundable credits = 750. ri_income_tax: 750 -- name: 2021 single filer who can be claimed as a dependent: the 2021 child tax rebate needs a child validly claimed as a dependent +- name: "2021 single filer who can be claimed as a dependent: the 2021 child tax rebate needs a child validly claimed as a dependent" period: 2021 input: people: @@ -224,7 +224,7 @@ # claimed has no dependents. ri_child_tax_rebate: 0 -- name: 2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the head) +- name: "2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the head)" period: 2021 input: people: @@ -254,7 +254,7 @@ # dependents (Publication 501, Dependent Taxpayer Test). ri_child_tax_rebate: 0 -- name: 2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the spouse) +- name: "2021 joint return with one claimable spouse: the 2021 child tax rebate needs a child validly claimed as a dependent (the claimable spouse is the spouse)" period: 2021 input: people: @@ -282,7 +282,7 @@ output: ri_child_tax_rebate: 0 -- name: 2021 joint return with both spouses claimable: the 2021 child tax rebate needs a child validly claimed as a dependent +- name: "2021 joint return with both spouses claimable: the 2021 child tax rebate needs a child validly claimed as a dependent" period: 2021 input: people: @@ -311,7 +311,7 @@ output: ri_child_tax_rebate: 0 -- name: 2021 joint return with neither spouse claimable: the 2021 child tax rebate needs a child validly claimed as a dependent +- name: "2021 joint return with neither spouse claimable: the 2021 child tax rebate needs a child validly claimed as a dependent" period: 2021 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml index 19ac591c971..09ba8177f9d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/deductions/sc_young_child_deduction.yaml @@ -73,7 +73,7 @@ # Child age 6 does NOT qualify - must be younger than 6 sc_young_child_deduction: 0 -- name: 2025 single filer who can be claimed as a dependent: no South Carolina young child deduction +- name: "2025 single filer who can be claimed as a dependent: no South Carolina young child deduction" period: 2025 input: people: @@ -95,7 +95,7 @@ # Carolina dependent exemption, which a claimable filer has none of. sc_young_child_deduction: 0 -- name: 2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the head)" period: 2025 input: people: @@ -123,7 +123,7 @@ output: sc_young_child_deduction: 0 -- name: 2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no South Carolina young child deduction (the claimable spouse is the spouse)" period: 2025 input: people: @@ -151,7 +151,7 @@ output: sc_young_child_deduction: 0 -- name: 2025 joint return with both spouses claimable: no South Carolina young child deduction +- name: "2025 joint return with both spouses claimable: no South Carolina young child deduction" period: 2025 input: people: @@ -180,7 +180,7 @@ output: sc_young_child_deduction: 0 -- name: 2025 joint return with neither spouse claimable (control): South Carolina young child deduction +- name: "2025 joint return with neither spouse claimable (control): South Carolina young child deduction" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml index 9ca9fc9890e..12d16796878 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml @@ -64,7 +64,7 @@ # 2025 dependent exemption amount is $4,930; 3 * $4,930 = $14,790. sc_dependent_exemption: 14_790 -- name: 2025 single filer who can be claimed as a dependent: no South Carolina dependent exemption +- name: "2025 single filer who can be claimed as a dependent: no South Carolina dependent exemption" period: 2025 input: people: @@ -86,7 +86,7 @@ # under IRC 151 and 152; IRC 152(b)(1) leaves a claimable filer none. sc_dependent_exemption: 0 -- name: 2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the head)" period: 2025 input: people: @@ -116,7 +116,7 @@ # dependents (Publication 501, Dependent Taxpayer Test). sc_dependent_exemption: 0 -- name: 2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no South Carolina dependent exemption (the claimable spouse is the spouse)" period: 2025 input: people: @@ -144,7 +144,7 @@ output: sc_dependent_exemption: 0 -- name: 2025 joint return with both spouses claimable: no South Carolina dependent exemption +- name: "2025 joint return with both spouses claimable: no South Carolina dependent exemption" period: 2025 input: people: @@ -173,7 +173,7 @@ output: sc_dependent_exemption: 0 -- name: 2025 joint return with neither spouse claimable (control): South Carolina dependent exemption +- name: "2025 joint return with neither spouse claimable (control): South Carolina dependent exemption" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml index 4b4c0309e0f..42d4838f042 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -1,4 +1,4 @@ -- name: 2025 single filer who can be claimed as a dependent: no Utah at-home parent credit +- name: "2025 single filer who can be claimed as a dependent: no Utah at-home parent credit" period: 2025 input: people: @@ -20,7 +20,7 @@ # dependent; under IRC 152(b)(1) a claimable filer has no dependents. ut_at_home_parent_credit_potential: 0 -- name: 2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the head)" period: 2025 input: people: @@ -49,7 +49,7 @@ # Either spouse being claimable leaves the joint return no dependents. ut_at_home_parent_credit_potential: 0 -- name: 2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no Utah at-home parent credit (the claimable spouse is the spouse)" period: 2025 input: people: @@ -77,7 +77,7 @@ output: ut_at_home_parent_credit_potential: 0 -- name: 2025 joint return with both spouses claimable: no Utah at-home parent credit +- name: "2025 joint return with both spouses claimable: no Utah at-home parent credit" period: 2025 input: people: @@ -106,7 +106,7 @@ output: ut_at_home_parent_credit_potential: 0 -- name: 2025 joint return with neither spouse claimable (control): Utah at-home parent credit +- name: "2025 joint return with neither spouse claimable (control): Utah at-home parent credit" period: 2025 input: people: @@ -135,7 +135,7 @@ # (the other parent earns $4,000); AGI $6,000 is under $50,000. ut_at_home_parent_credit_potential: 100 -- name: 2025 two at-home parents with one newborn: one Utah at-home parent credit per child on the return +- name: "2025 two at-home parents with one newborn: one Utah at-home parent credit per child on the return" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml index b8f62c758d2..c9f16e5b851 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_personal_exemption.yaml @@ -34,7 +34,7 @@ output: ut_personal_exemption: 5_406 # 1802 * 3 -- name: 2025 single filer who can be claimed as a dependent: no Utah additional dependent for a child born this year +- name: "2025 single filer who can be claimed as a dependent: no Utah additional dependent for a child born this year" period: 2025 input: people: @@ -57,7 +57,7 @@ ut_personal_exemption_additional_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the head)" period: 2025 input: people: @@ -86,7 +86,7 @@ ut_personal_exemption_additional_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no Utah additional dependent for a child born this year (the claimable spouse is the spouse)" period: 2025 input: people: @@ -115,7 +115,7 @@ ut_personal_exemption_additional_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with both spouses claimable: no Utah additional dependent for a child born this year +- name: "2025 joint return with both spouses claimable: no Utah additional dependent for a child born this year" period: 2025 input: people: @@ -145,7 +145,7 @@ ut_personal_exemption_additional_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with neither spouse claimable (control): Utah additional dependent for a child born this year +- name: "2025 joint return with neither spouse claimable (control): Utah additional dependent for a child born this year" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml index 7ad60a085e3..928dcca4bdd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml @@ -1,4 +1,4 @@ -- name: 2025 single filer who can be claimed as a dependent: no Utah dependents for the taxpayer credit +- name: "2025 single filer who can be claimed as a dependent: no Utah dependents for the taxpayer credit" period: 2025 input: people: @@ -22,7 +22,7 @@ ut_total_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the head)" period: 2025 input: people: @@ -52,7 +52,7 @@ ut_total_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no Utah dependents for the taxpayer credit (the claimable spouse is the spouse)" period: 2025 input: people: @@ -81,7 +81,7 @@ ut_total_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with both spouses claimable: no Utah dependents for the taxpayer credit +- name: "2025 joint return with both spouses claimable: no Utah dependents for the taxpayer credit" period: 2025 input: people: @@ -111,7 +111,7 @@ ut_total_dependents: 0 ut_personal_exemption: 0 -- name: 2025 joint return with neither spouse claimable (control): Utah dependents for the taxpayer credit +- name: "2025 joint return with neither spouse claimable (control): Utah dependents for the taxpayer credit" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml index 188a3350016..4557dea7b9a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml @@ -1,4 +1,4 @@ -- name: 2025 single filer who can be claimed as a dependent: Vermont subtracts the student loan interest not deducted federally +- name: "2025 single filer who can be claimed as a dependent: Vermont subtracts the student loan interest not deducted federally" period: 2025 input: people: @@ -21,7 +21,7 @@ vt_student_loan_interest_subtraction: 1000 vt_agi: 9000 -- name: 2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the head)" period: 2025 input: people: @@ -53,7 +53,7 @@ vt_student_loan_interest_subtraction: 1000 vt_agi: 9000 -- name: 2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the spouse)" period: 2025 input: people: @@ -83,7 +83,7 @@ vt_student_loan_interest_subtraction: 1000 vt_agi: 9000 -- name: 2025 joint return with both spouses claimable: Vermont subtracts the student loan interest not deducted federally +- name: "2025 joint return with both spouses claimable: Vermont subtracts the student loan interest not deducted federally" period: 2025 input: people: @@ -114,7 +114,7 @@ vt_student_loan_interest_subtraction: 1000 vt_agi: 9000 -- name: 2025 joint return with neither spouse claimable: Vermont subtracts the student loan interest not deducted federally +- name: "2025 joint return with neither spouse claimable: Vermont subtracts the student loan interest not deducted federally" period: 2025 input: people: @@ -146,7 +146,7 @@ vt_student_loan_interest_subtraction: 0 vt_agi: 9000 -- name: 2025 interest above the $2,500 federal cap: Vermont subtracts the excess +- name: "2025 interest above the $2,500 federal cap: Vermont subtracts the excess" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml index 20795293c25..a5df0d28a46 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml @@ -146,7 +146,7 @@ output: vt_renter_credit: 1_692 -- name: 2025 single filer who can be claimed as a dependent: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions +- name: "2025 single filer who can be claimed as a dependent: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions" period: 2025 input: people: @@ -168,7 +168,7 @@ # dependent by any taxpayer. vt_renter_credit: 0 -- name: 2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the head)" period: 2025 input: people: @@ -200,7 +200,7 @@ # below the full-credit limit: round($1,476 x 12 x 10%) = $1,771. vt_renter_credit: 1771 -- name: 2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions (the claimable spouse is the spouse)" period: 2025 input: people: @@ -229,7 +229,7 @@ output: vt_renter_credit: 1771 -- name: 2025 joint return with both spouses claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions +- name: "2025 joint return with both spouses claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions" period: 2025 input: people: @@ -259,7 +259,7 @@ output: vt_renter_credit: 0 -- name: 2025 joint return with neither spouse claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions +- name: "2025 joint return with neither spouse claimable: Vermont renter credit needs a claimant who is not claimed as a dependent and sizes rent by personal exemptions" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index c5845ae7a47..2d0f332495b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -84,7 +84,7 @@ output: vt_ctc: 500 -- name: Two children halfway phased out at $150,000: each child's $1,000 is reduced by $500 +- name: "Two children halfway phased out at $150,000: each child's $1,000 is reduced by $500" period: 2023 input: people: @@ -184,7 +184,7 @@ output: vt_ctc: 0 -- name: 2025 single filer who can be claimed as a dependent: no Vermont child tax credit +- name: "2025 single filer who can be claimed as a dependent: no Vermont child tax credit" period: 2025 input: people: @@ -207,7 +207,7 @@ # a claimable filer no dependents and so no federal credit. vt_ctc: 0 -- name: 2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the head)" period: 2025 input: people: @@ -236,7 +236,7 @@ # Either spouse being claimable leaves the joint return no dependents. vt_ctc: 0 -- name: 2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no Vermont child tax credit (the claimable spouse is the spouse)" period: 2025 input: people: @@ -264,7 +264,7 @@ output: vt_ctc: 0 -- name: 2025 joint return with both spouses claimable: no Vermont child tax credit +- name: "2025 joint return with both spouses claimable: no Vermont child tax credit" period: 2025 input: people: @@ -293,7 +293,7 @@ output: vt_ctc: 0 -- name: 2025 joint return with neither spouse claimable (control): Vermont child tax credit +- name: "2025 joint return with neither spouse claimable (control): Vermont child tax credit" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml index f37515b5fdd..b54ae76fac2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml @@ -153,7 +153,7 @@ output: vt_personal_exemptions: 13_050 # 3*4350 -- name: 2025 single filer who can be claimed as a dependent: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents +- name: "2025 single filer who can be claimed as a dependent: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents" period: 2025 input: people: @@ -175,7 +175,7 @@ # IRC 152(b)(1) leaves a claimable filer no dependents. vt_personal_exemptions: 0 -- name: 2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the head)" period: 2025 input: people: @@ -204,7 +204,7 @@ # The unclaimed spouse's exemption only: 1 x $5,300. vt_personal_exemptions: 5300 -- name: 2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents (the claimable spouse is the spouse)" period: 2025 input: people: @@ -232,7 +232,7 @@ output: vt_personal_exemptions: 5300 -- name: 2025 joint return with both spouses claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents +- name: "2025 joint return with both spouses claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents" period: 2025 input: people: @@ -261,7 +261,7 @@ output: vt_personal_exemptions: 0 -- name: 2025 joint return with neither spouse claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents +- name: "2025 joint return with neither spouse claimable: Vermont personal exemptions count only filers who cannot be claimed and, when none can be, the dependents" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml index 6881d0092fe..b37c41341cd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml @@ -54,7 +54,7 @@ output: wi_additional_exemption: 0 -- name: 2025 single filer who can be claimed as a dependent: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed +- name: "2025 single filer who can be claimed as a dependent: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed" period: 2025 input: people: @@ -73,7 +73,7 @@ # someone else can claim as a dependent. wi_additional_exemption: 0 -- name: 2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the head)" period: 2025 input: people: @@ -98,7 +98,7 @@ # Each spouse is tested separately: only the unclaimed spouse counts. wi_additional_exemption: 250 -- name: 2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed (the claimable spouse is the spouse)" period: 2025 input: people: @@ -122,7 +122,7 @@ output: wi_additional_exemption: 250 -- name: 2025 joint return with both spouses claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed +- name: "2025 joint return with both spouses claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed" period: 2025 input: people: @@ -147,7 +147,7 @@ output: wi_additional_exemption: 0 -- name: 2025 joint return with neither spouse claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed +- name: "2025 joint return with neither spouse claimable: Wisconsin $250 exemption for a filer aged 65 or older who cannot be claimed" period: 2025 input: people: @@ -171,7 +171,7 @@ # Two filers aged 65 or older x $250. wi_additional_exemption: 500 -- name: 2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the head) +- name: "2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the head)" period: 2025 input: people: @@ -195,7 +195,7 @@ output: wi_additional_exemption: 0 -- name: 2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the spouse) +- name: "2025 joint return where only the claimable spouse is 65 or older: no Wisconsin $250 exemption (the claimable spouse is the spouse)" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index b51daff71f6..abc93f23800 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -153,7 +153,7 @@ output: wi_homestead_eligible: false -- name: 2025 single filer who can be claimed as a dependent: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older +- name: "2025 single filer who can be claimed as a dependent: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" period: 2025 input: people: @@ -175,7 +175,7 @@ wi_homestead_eligible: false wi_homestead_credit: 0 -- name: 2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the head)" period: 2025 input: people: @@ -207,7 +207,7 @@ wi_homestead_eligible: true wi_homestead_credit: 800 -- name: 2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the spouse)" period: 2025 input: people: @@ -236,7 +236,7 @@ wi_homestead_eligible: true wi_homestead_credit: 800 -- name: 2025 joint return with both spouses claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older +- name: "2025 joint return with both spouses claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" period: 2025 input: people: @@ -266,7 +266,7 @@ wi_homestead_eligible: false wi_homestead_credit: 0 -- name: 2025 joint return with neither spouse claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older +- name: "2025 joint return with neither spouse claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" period: 2025 input: people: @@ -295,7 +295,7 @@ wi_homestead_eligible: true wi_homestead_credit: 800 -- name: 2025 joint return with both spouses claimable: Wisconsin homestead credit allows a claimant aged 62 or older even if claimed +- name: "2025 joint return with both spouses claimable: Wisconsin homestead credit allows a claimant aged 62 or older even if claimed" period: 2025 input: people: @@ -326,7 +326,7 @@ wi_homestead_eligible: true wi_homestead_credit: 800 -- name: 2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the head) +- name: "2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the head)" period: 2025 input: people: @@ -357,7 +357,7 @@ wi_homestead_eligible: false wi_homestead_credit: 0 -- name: 2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the spouse) +- name: "2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the spouse)" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 3b4055b5836..29a4f76df90 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -100,7 +100,7 @@ # the add-back follows that total and household income is the same. wi_homestead_income: 5_000 -- name: 2025 single filer who can be claimed as a dependent: no Wisconsin homestead $500 dependent deduction +- name: "2025 single filer who can be claimed as a dependent: no Wisconsin homestead $500 dependent deduction" period: 2025 input: people: @@ -125,7 +125,7 @@ wi_homestead_income: 10000 wi_homestead_credit: 663.6568 -- name: 2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the head)" period: 2025 input: people: @@ -157,7 +157,7 @@ wi_homestead_income: 10000 wi_homestead_credit: 663.6568 -- name: 2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: no Wisconsin homestead $500 dependent deduction (the claimable spouse is the spouse)" period: 2025 input: people: @@ -188,7 +188,7 @@ wi_homestead_income: 10000 wi_homestead_credit: 663.6568 -- name: 2025 joint return with both spouses claimable: no Wisconsin homestead $500 dependent deduction +- name: "2025 joint return with both spouses claimable: no Wisconsin homestead $500 dependent deduction" period: 2025 input: people: @@ -220,7 +220,7 @@ wi_homestead_income: 10000 wi_homestead_credit: 663.6568 -- name: 2025 joint return with neither spouse claimable (control): Wisconsin homestead $500 dependent deduction +- name: "2025 joint return with neither spouse claimable (control): Wisconsin homestead $500 dependent deduction" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml index b50cbb6021f..4aca346ed9e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml @@ -98,7 +98,7 @@ # within $0.05 of the schedule. wi_standard_deduction: 13_960 - 0.12 * (70_000 - 20_120) -- name: 2025 single filer who can be claimed as a dependent: Wisconsin standard deduction limited for a filer who can be claimed +- name: "2025 single filer who can be claimed as a dependent: Wisconsin standard deduction limited for a filer who can be claimed" period: 2025 input: people: @@ -118,7 +118,7 @@ # max($1,350, earned income + $450) = max($1,350, $1,000 + $450). wi_standard_deduction: 1450 -- name: 2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the head)" period: 2025 input: people: @@ -146,7 +146,7 @@ # couple's earned income is $1,000, so $1,450. wi_standard_deduction: 1450 -- name: 2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: Wisconsin standard deduction limited for a filer who can be claimed (the claimable spouse is the spouse)" period: 2025 input: people: @@ -172,7 +172,7 @@ output: wi_standard_deduction: 1450 -- name: 2025 joint return with both spouses claimable: Wisconsin standard deduction limited for a filer who can be claimed +- name: "2025 joint return with both spouses claimable: Wisconsin standard deduction limited for a filer who can be claimed" period: 2025 input: people: @@ -199,7 +199,7 @@ output: wi_standard_deduction: 1450 -- name: 2025 joint return with neither spouse claimable: Wisconsin standard deduction limited for a filer who can be claimed +- name: "2025 joint return with neither spouse claimable: Wisconsin standard deduction limited for a filer who can be claimed" period: 2025 input: people: @@ -261,7 +261,7 @@ # max($1,350, $0 + $450) = $1,350. wi_standard_deduction: 1350 -- name: 2021 single filer who can be claimed: Wisconsin dependent standard deduction +- name: "2021 single filer who can be claimed: Wisconsin dependent standard deduction" period: 2021 input: people: @@ -280,7 +280,7 @@ # 2021 Form 1 worksheet: max($1,100, $600 + $350) = $1,100. wi_standard_deduction: 1100 -- name: 2023 single filer who can be claimed: Wisconsin dependent standard deduction +- name: "2023 single filer who can be claimed: Wisconsin dependent standard deduction" period: 2023 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml index c9bf3cd63ef..f1e5e340755 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml @@ -14,7 +14,7 @@ output: wv_low_income_family_tax_credit_eligible: true -- name: 2025 single filer who can be claimed as a dependent: West Virginia low-income family tax credit uses the exemptions that may be claimed +- name: "2025 single filer who can be claimed as a dependent: West Virginia low-income family tax credit uses the exemptions that may be claimed" period: 2025 input: people: @@ -35,7 +35,7 @@ wv_low_income_family_tax_credit_eligible: false wv_low_income_family_tax_credit_potential: 0 -- name: 2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the head)" period: 2025 input: people: @@ -67,7 +67,7 @@ wv_income_tax_before_non_refundable_credits: 340.4 wv_low_income_family_tax_credit_potential: 272.32 -- name: 2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the spouse)" period: 2025 input: people: @@ -96,7 +96,7 @@ wv_income_tax_before_non_refundable_credits: 340.4 wv_low_income_family_tax_credit_potential: 272.32 -- name: 2025 joint return with both spouses claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed +- name: "2025 joint return with both spouses claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed" period: 2025 input: people: @@ -124,7 +124,7 @@ wv_low_income_family_tax_credit_eligible: false wv_low_income_family_tax_credit_potential: 0 -- name: 2025 joint return with neither spouse claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed +- name: "2025 joint return with neither spouse claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed" period: 2025 input: people: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml index 7e43f04808d..d4a07b0daa7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml @@ -29,7 +29,7 @@ output: wv_personal_exemption: 4_000 -- name: 2025 single filer who can be claimed as a dependent: West Virginia personal exemption counts only exemptions that may be claimed +- name: "2025 single filer who can be claimed as a dependent: West Virginia personal exemption counts only exemptions that may be claimed" period: 2025 input: people: @@ -49,7 +49,7 @@ # can be claimed; a return with no exemptions takes $500 instead. wv_personal_exemption: 500 -- name: 2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the head)" period: 2025 input: people: @@ -76,7 +76,7 @@ # One exemption for the spouse who cannot be claimed: $2,000. wv_personal_exemption: 2000 -- name: 2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the spouse)" period: 2025 input: people: @@ -102,7 +102,7 @@ output: wv_personal_exemption: 2000 -- name: 2025 joint return with both spouses claimable: West Virginia personal exemption counts only exemptions that may be claimed +- name: "2025 joint return with both spouses claimable: West Virginia personal exemption counts only exemptions that may be claimed" period: 2025 input: people: @@ -130,7 +130,7 @@ # No exemptions on the return: the $500 fallback, once per return. wv_personal_exemption: 500 -- name: 2025 joint return with neither spouse claimable: West Virginia personal exemption counts only exemptions that may be claimed +- name: "2025 joint return with neither spouse claimable: West Virginia personal exemption counts only exemptions that may be claimed" period: 2025 input: people: @@ -156,7 +156,7 @@ # Two exemptions x $2,000. wv_personal_exemption: 4000 -- name: 2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the head) +- name: "2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the head)" period: 2025 input: people: @@ -186,7 +186,7 @@ # dependents; one exemption x $2,000 (the control below gets $6,000). wv_personal_exemption: 2000 -- name: 2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the spouse) +- name: "2025 joint return with one claimable spouse and a child: West Virginia personal exemption has no dependent exemption (the claimable spouse is the spouse)" period: 2025 input: people: @@ -214,7 +214,7 @@ output: wv_personal_exemption: 2000 -- name: 2025 joint return with neither spouse claimable and a child: West Virginia personal exemption +- name: "2025 joint return with neither spouse claimable and a child: West Virginia personal exemption" period: 2025 input: people: From 6e4ece4788440088a1540fa93bfcfde5dfe1914c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 06:20:23 -0400 Subject: [PATCH 19/42] Count no dependents on a claimable filer's return in the Utah dependent exemption reform When in effect, the contributed reform recounts is_tax_unit_dependent itself; it now applies the same IRC 152(b)(1) gate as the baseline ut_total_dependents, as the federal branch did for the HI, WI and Treasury reforms. Co-Authored-By: Claude Opus 5.5 --- .../ut_dependent_exemption_reform.py | 7 +- .../ut_dependent_exemption.yaml | 109 ++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py b/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py index 5e4ebc0dccc..7bb66db5fce 100644 --- a/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py @@ -33,7 +33,12 @@ def formula(tax_unit, period, parameters): return p.personal_exemption * total_dependents person = tax_unit.members age = person("age", period) - dependent = person("is_tax_unit_dependent", period) + # A return on which the filer (or, if joint, either spouse) can be + # claimed as a dependent has no dependents (IRC 152(b)(1)), as in + # the baseline ut_total_dependents. + dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere", period + ) # Dependents under the age threshold (all dependents when the # age limit is off) take the contrib amount; older dependents # keep the baseline (uprated) personal-exemption amount. diff --git a/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml b/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml index 49278857c1e..9e66390db99 100644 --- a/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml @@ -148,3 +148,112 @@ # in_effect is false, so the $0 amount does not apply: two dependents # at the uprated baseline amount. ut_personal_exemption: 4_318 + +- name: Reform in effect counts no dependents on a claimable filer's return (single filer) + period: 2026 + reforms: policyengine_us.reforms.states.ut.dependent_exemption.ut_dependent_exemption_reform + input: + gov.contrib.states.ut.dependent_exemption.in_effect: true + gov.contrib.states.ut.dependent_exemption.amount: 3_000 + people: + filer: + age: 20 + claimed_as_dependent_on_another_return: true + child1: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [filer, child1] + households: + household: + members: [filer, child1] + state_code: UT + output: + # IRC 152(b)(1): a filer who can be claimed has no dependents, as in the + # baseline ut_total_dependents. + ut_personal_exemption: 0 + +- name: Reform in effect counts no dependents when one joint spouse can be claimed (the claimable spouse is the spouse) + period: 2026 + reforms: policyengine_us.reforms.states.ut.dependent_exemption.ut_dependent_exemption_reform + input: + gov.contrib.states.ut.dependent_exemption.in_effect: true + gov.contrib.states.ut.dependent_exemption.amount: 3_000 + people: + person_b: + age: 22 + is_tax_unit_head: true + person_a: + age: 20 + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + child1: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person_b, person_a, child1] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child1] + state_code: UT + output: + ut_personal_exemption: 0 + +- name: Reform in effect counts no dependents when one joint spouse can be claimed (the claimable spouse is the head) + period: 2026 + reforms: policyengine_us.reforms.states.ut.dependent_exemption.ut_dependent_exemption_reform + input: + gov.contrib.states.ut.dependent_exemption.in_effect: true + gov.contrib.states.ut.dependent_exemption.amount: 3_000 + people: + person_a: + age: 20 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: true + person_b: + age: 22 + is_tax_unit_spouse: true + child1: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person_a, person_b, child1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child1] + state_code: UT + output: + ut_personal_exemption: 0 + +- name: Reform in effect, neither spouse claimable (control) + period: 2026 + reforms: policyengine_us.reforms.states.ut.dependent_exemption.ut_dependent_exemption_reform + input: + gov.contrib.states.ut.dependent_exemption.in_effect: true + gov.contrib.states.ut.dependent_exemption.amount: 3_000 + people: + person_a: + age: 20 + is_tax_unit_head: true + person_b: + age: 22 + is_tax_unit_spouse: true + child1: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person_a, person_b, child1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child1] + state_code: UT + output: + # One dependent at the reform amount (no age limit): $3,000. + ut_personal_exemption: 3_000 From 4597c55a869b45c17376673fbe4aa70f72ca0f3c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 09:35:20 -0400 Subject: [PATCH 20/42] Address the federal claimable-filer review - Child and dependent care: childcare expenses do not count on a return with a claimable filer unless a child under 13 still qualifies (IRC 21(b)(2)(A)(ii)). - Tuition and fees deduction: not allowed to a filer who can be claimed (IRC 222(c)(3)), so the education credit bar no longer makes such a filer eligible. - Recovery rebates: the Armed Forces exception waives the identification rule only, not the exclusion of a spouse who is another taxpayer's dependent. - Georgia: the contributed refundable credit and SB 520 reforms count the same children as ga_ctc_potential through a new ga_ctc_eligible_child. - Education credits and student loan interest read the claimed input directly, since IRC 25A(g)(3) and 221(c) turn on an actual claim. - One reference per source document; quote IRC 24(h)(4)(A) exactly. - Tests: mixed-recipient care, tuition deduction, military rebates, Georgia refund cap, and the swapped New Mexico rebate case; the property test now covers the state readers of the exemption count. Co-Authored-By: Claude Opus 5.5 --- changelog.d/claimable-filer-federal.fixed.md | 2 +- .../reforms/states/ga/ctc/ga_ctc_reform.py | 7 +- .../states/ga/sb520/ga_sb520_reform.py | 8 +- ...test_claimable_filer_federal_invariants.py | 42 +++-- .../credits/cdcc/cdcc_relevant_expenses.yaml | 169 ++++++++++++++++++ .../recovery_rebate_credit.yaml | 154 +++++++++++++++- .../tuition_and_fees_deduction_eligible.yaml | 90 ++++++++++ ...m_low_income_comprehensive_tax_rebate.yaml | 33 +++- .../contrib/states/ga/ctc/ga_ctc_reform.yaml | 90 ++++++++++ .../gov/irs/credits/cdcc/cdcc_eligible.py | 4 +- .../credits/cdcc/cdcc_relevant_expenses.py | 13 ++ .../ctc/maximum/ctc_qualifying_child.py | 2 +- .../ctc_adult_individual_maximum.py | 2 +- ...ligible_for_american_opportunity_credit.py | 14 +- ...s_eligible_for_lifetime_learning_credit.py | 14 +- .../rrc_adult_count_with_valid_ssn.py | 3 +- .../recovery_rebate_credit/rrc_arpa.py | 10 +- .../credits/recovery_rebate_credit/rrc_caa.py | 10 +- .../recovery_rebate_credit/rrc_cares.py | 10 +- .../student_loan_interest_ald_eligible.py | 13 +- .../tuition_and_fees_deduction_eligible.py | 7 +- .../credits/ctc/ga_ctc_eligible_child.py | 29 +++ .../income/credits/ctc/ga_ctc_potential.py | 16 +- 23 files changed, 673 insertions(+), 69 deletions(-) create mode 100644 policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py diff --git a/changelog.d/claimable-filer-federal.fixed.md b/changelog.d/claimable-filer-federal.fixed.md index 545d474e1a6..82a77ce8992 100644 --- a/changelog.d/claimable-filer-federal.fixed.md +++ b/changelog.d/claimable-filer-federal.fixed.md @@ -1 +1 @@ -Apply the federal rules for a filer who can be claimed as a dependent on another return: no child tax credit, credit for other dependents or under-13 care qualifying child on such a return (IRC 24, 21, 152(b)(1)), no personal exemption for that filer (IRC 151(d)(2)) in the exemption count Rhode Island, Delaware, Ohio, Wisconsin, Oregon, Hawaii and New Mexico read, no recovery rebate for that filer, and no education credits or student loan interest deduction on a return with a claimed filer (IRC 25A(g)(3), 221(c)). +Apply the federal rules for a filer who can be claimed as a dependent on another return: no child tax credit, credit for other dependents or under-13 care qualifying child (or that child's care expenses) on such a return (IRC 24, 21, 152(b)(1)), no personal exemption for that filer (IRC 151(d)(2)) in the exemption count Rhode Island, Delaware, Ohio, Wisconsin, Oregon, Hawaii and New Mexico read, no recovery rebate for that filer, including under the Armed Forces exception, no tuition and fees deduction (IRC 222(c)(3)), and no education credits or student loan interest deduction on a return with a claimed filer (IRC 25A(g)(3), 221(c)). diff --git a/policyengine_us/reforms/states/ga/ctc/ga_ctc_reform.py b/policyengine_us/reforms/states/ga/ctc/ga_ctc_reform.py index 6ff5bf1eb8a..8b8f54bc4c6 100644 --- a/policyengine_us/reforms/states/ga/ctc/ga_ctc_reform.py +++ b/policyengine_us/reforms/states/ga/ctc/ga_ctc_reform.py @@ -33,12 +33,7 @@ def formula(tax_unit, period, parameters): unused_credit = max_(potential - non_refundable, 0) # Cap the refund at amount per eligible child (same child count as # ga_ctc_potential: qualifying children under the age threshold). - baseline = parameters(period).gov.states.ga.tax.income.credits.ctc - person = tax_unit.members - age = person("age", period) - ctc_eligible_child = person("ctc_qualifying_child", period) - ga_child_age_eligible = age < baseline.age_threshold - eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible) + eligible_children = add(tax_unit, period, ["ga_ctc_eligible_child"]) refund_limit = p.refundable.amount * eligible_children refundable_credit = min_(unused_credit, refund_limit) # Only pay the refund in periods where the reform is in effect. The diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index c6b4cfb0fc5..cabfb410d6c 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -89,12 +89,8 @@ def formula(tax_unit, period, parameters): sb520_active = p_sb520.in_effect - person = tax_unit.members - age = person("age", period) - ctc_eligible_child = person("ctc_qualifying_child", period) - # SB 520 preserves baseline GA CTC age threshold (under 6) - ga_child_age_eligible = age < p.age_threshold - eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible) + # SB 520 preserves the baseline GA CTC child test (under 6) + eligible_children = add(tax_unit, period, ["ga_ctc_eligible_child"]) sb520_amount = p_sb520.credits.ctc.amount baseline_amount = p.amount diff --git a/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py b/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py index 58eb58887b4..aab5f3a0156 100644 --- a/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_federal_invariants.py @@ -12,18 +12,24 @@ - a child under 13 is not a child and dependent care qualifying person on such a return (a disabled person still is, IRC 21(b)(1)(B)); - recovery rebates leave out a claimable filer and, on such a return, the - dependents. + dependents; +- the state amounts that read the exemption count follow it (Georgia's two + credits keep their own count). -For couples drawn by Hypothesis and a seeded population, with either, both or -neither spouse claimed, in 2021-2026: +For couples with either, both or neither spouse claimed, in 2021-2026: 1. Swap invariance: each output is the same under either head/spouse - labelling. + labelling (Hypothesis batches and the seeded population). 2. Monotonicity: marking another filer as claimed never raises any of the - amounts or counts below. + amounts or counts below (seeded population only). 3. Identities: `exemptions_count` equals the filers who cannot be claimed plus the dependents when no filer can be claimed, and a return with a claimable - filer has no CTC-qualifying child and no credit for other dependents. + filer has no CTC-qualifying child and no credit for other dependents + (both populations). + +The generated adults have no tuition or student loan interest, so the education +credit, tuition deduction and student loan interest gates are covered by YAML +cases only, as are the contributed reforms. """ import numpy as np @@ -48,22 +54,30 @@ # Texas has no income tax; the others read the federal exemption count. STATES = ["TX", "OR", "HI", "NM", "GA", "RI", "DE", "OH", "WI"] -OUTPUTS = [ - "income_tax", - "ctc_maximum", - "ctc_qualifying_children", - "capped_count_cdcc_eligible", - "exemptions_count", - "rrc_arpa", +# State amounts that read the federal exemption count; each is zero outside +# its state. +STATE_OUTPUTS = [ + "ri_exemptions", + "de_personal_credit_potential", + "oh_personal_exemptions", + "wi_base_exemption", + "or_regular_exemptions", + "hi_regular_exemptions", + "nm_low_and_middle_income_exemption", + "nm_low_income_comprehensive_tax_rebate", + "ga_ctc_potential", + "ga_low_income_credit_potential", ] # Amounts that marking another filer as claimed must never raise. MONOTONE = [ "ctc_maximum", "ctc_qualifying_children", "capped_count_cdcc_eligible", + "cdcc_relevant_expenses", "exemptions_count", "rrc_arpa", -] +] + STATE_OUTPUTS +OUTPUTS = ["income_tax"] + MONOTONE @st.composite diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml index 8ed8c1ecdd6..15dfeb1fe4a 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml @@ -40,3 +40,172 @@ min_head_spouse_earned: 10_000 output: cdcc_relevant_expenses: 3_000 # 1_000 childcare + 2_000 adult care + +# IRC 21(b)(2)(A)(ii): only expenses for the care of a qualifying individual count. +# The child of a return on which a filer can be claimed is not one (IRC 152(b)(1)), +# while the incapacitated relative still is (IRC 21(b)(1)(B)). +- name: 2025 childcare for the child of a claimable single filer does not count + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 5000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, child, relative] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [person_a, child, relative] + state_code: TX + output: + count_cdcc_eligible: 1 + cdcc_relevant_expenses: 0 + +- name: 2025 childcare for the child of a joint return with one claimable spouse does not count (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 5000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + employment_income: 5000 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child, relative] + filing_status: JOINT + tax_unit_childcare_expenses: 3000 + households: + household: + members: [person_a, person_b, child, relative] + state_code: TX + output: + count_cdcc_eligible: 1 + cdcc_relevant_expenses: 0 + +- name: 2025 childcare for the child of a joint return with one claimable spouse does not count (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + employment_income: 5000 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + employment_income: 5000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child, relative] + filing_status: JOINT + tax_unit_childcare_expenses: 3000 + households: + household: + members: [person_b, person_a, child, relative] + state_code: TX + output: + count_cdcc_eligible: 1 + cdcc_relevant_expenses: 0 + +- name: 2025 care expenses for the incapacitated relative of a claimable filer still count + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 5000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + pre_subsidy_care_expenses: 2000 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, child, relative] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [person_a, child, relative] + state_code: TX + output: + cdcc_relevant_expenses: 2000 + +- name: 2025 the same household with nobody claimable counts the childcare + period: 2025 + input: + people: + person_a: + age: 30 + employment_income: 5000 + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, child, relative] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [person_a, child, relative] + state_code: TX + output: + count_cdcc_eligible: 2 + cdcc_relevant_expenses: 3000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml index 0d21682feaf..045236d3234 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml @@ -48,7 +48,6 @@ output: recovery_rebate_credit: 3_920 # https://www.efile.com/stimulus-check-three/ - - name: 2021 single filer who can be claimed as a dependent gets no ARPA rebate period: 2021 input: @@ -159,3 +158,156 @@ output: # 3 x $1,400. rrc_arpa: 4_200 + +# The Armed Forces exception (IRC 6428B(e)(2)(E)) waives the identification rule +# for the other spouse; it does not make a dependent of another taxpayer an +# eligible individual (IRC 6428B(c)(2)). +- name: 2021 military joint return with one claimable spouse gets one ARPA rebate (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + is_military: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + rrc_arpa: 1400 + +- name: 2021 military joint return with one claimable spouse gets one ARPA rebate (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + is_military: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + rrc_arpa: 1400 + +- name: 2021 military joint return whose other spouse is claimable gets one ARPA rebate (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_military: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + rrc_arpa: 1400 + +- name: 2021 military joint return whose other spouse is claimable gets one ARPA rebate (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + is_military: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + rrc_arpa: 1400 + +- name: 2021 military joint return with both spouses claimable gets no ARPA rebate + period: 2021 + input: + people: + person_a: + age: 30 + is_military: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + rrc_arpa: 0 + +- name: 2021 military joint return with neither spouse claimable gets both ARPA rebates + period: 2021 + input: + people: + person_a: + age: 30 + is_military: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + rrc_arpa: 2800 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml index 4912f0155de..dd6bcd8d147 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml @@ -26,3 +26,93 @@ output: tuition_and_fees_deduction_eligible: false # Can't claim this deduction if the household has taken the American Oppportunity or Lifetime Learning Credit. + +# IRC 222(c)(3): no deduction to an individual for whom a section 151 deduction +# may be allowed to another taxpayer. +- name: 2020 single filer who can be claimed as a dependent is not eligible + period: 2020 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + households: + household: + members: [person_a] + state_code: TX + output: + tuition_and_fees_deduction_eligible: false + +- name: 2020 joint return with one claimable spouse is not eligible (the claimable spouse is the head) + period: 2020 + input: + people: + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + tuition_and_fees_deduction_eligible: false + +- name: 2020 joint return with one claimable spouse is not eligible (the claimable spouse is the spouse) + period: 2020 + input: + people: + person_b: + age: 22 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 20 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: TX + output: + tuition_and_fees_deduction_eligible: false + +- name: 2020 joint return with neither spouse claimable is eligible + period: 2020 + input: + people: + person_a: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 22 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: TX + output: + tuition_and_fees_deduction_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml index 6c7e83fb126..84ae85e14bf 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nm/tax/income/rebates/low_income_comprehensive/nm_low_income_comprehensive_tax_rebate.yaml @@ -305,20 +305,24 @@ # claimant (NMSA 7-2-14(A)). nm_low_income_comprehensive_tax_rebate: 0 -- name: 2025 joint return with one claimable spouse keeps the rebate with the couple's combined income +- name: 2025 joint return with one claimable spouse keeps the rebate with the couple's combined income (the claimable spouse is the head) period: 2025 input: people: person_a: age: 30 claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false person_b: age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true tax_units: tax_unit: members: [person_a, person_b] filing_status: JOINT - nm_modified_gross_income: 8_500 + nm_modified_gross_income: 8500 households: household: members: [person_a, person_b] @@ -326,3 +330,28 @@ output: # One exemption; the $8,000-$9,000 row of the one-exemption table. nm_low_income_comprehensive_tax_rebate: 212 + +- name: 2025 joint return with one claimable spouse keeps the rebate with the couple's combined income (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + nm_modified_gross_income: 8500 + households: + household: + members: [person_b, person_a] + state_code: NM + output: + nm_low_income_comprehensive_tax_rebate: 212 diff --git a/policyengine_us/tests/policy/contrib/states/ga/ctc/ga_ctc_reform.yaml b/policyengine_us/tests/policy/contrib/states/ga/ctc/ga_ctc_reform.yaml index 77fcd5db26d..c236fb692d2 100644 --- a/policyengine_us/tests/policy/contrib/states/ga/ctc/ga_ctc_reform.yaml +++ b/policyengine_us/tests/policy/contrib/states/ga/ctc/ga_ctc_reform.yaml @@ -127,3 +127,93 @@ # $10k liability fully absorbs the $250 nonrefundable credit, leaving # nothing unused to refund. ga_refundable_ctc: 0 + +# The refund cap counts the same children as ga_ctc_potential (Georgia's own +# IRC 24(c) child test), not children allowed the federal credit. +- name: Refund cap keeps the child of a filer who can be claimed as a dependent + period: 2026 + absolute_error_margin: 0.01 + reforms: policyengine_us.reforms.states.ga.ctc.ga_ctc_reform.ga_ctc_reform + input: + gov.contrib.states.ga.ctc.refundable.in_effect: true + people: + parent: + age: 30 + claimed_as_dependent_on_another_return: true + child: + age: 2 + tax_units: + tax_unit: + members: [parent, child] + filing_status: SINGLE + ga_income_tax_before_non_refundable_credits: 0 + households: + household: + members: [parent, child] + state_code: GA + output: + ctc_qualifying_child: [false, false] + ga_ctc_potential: 250 + ga_refundable_ctc: 250 + +- name: Refund cap keeps the child of a joint return with one claimable spouse (the claimable spouse is the head) + period: 2026 + absolute_error_margin: 0.01 + reforms: policyengine_us.reforms.states.ga.ctc.ga_ctc_reform.ga_ctc_reform + input: + gov.contrib.states.ga.ctc.refundable.in_effect: true + people: + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + ga_income_tax_before_non_refundable_credits: 0 + households: + household: + members: [person_a, person_b, child] + state_code: GA + output: + ga_ctc_potential: 250 + ga_refundable_ctc: 250 + +- name: Refund cap keeps the child of a joint return with one claimable spouse (the claimable spouse is the spouse) + period: 2026 + absolute_error_margin: 0.01 + reforms: policyengine_us.reforms.states.ga.ctc.ga_ctc_reform.ga_ctc_reform + input: + gov.contrib.states.ga.ctc.refundable.in_effect: true + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 2 + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + ga_income_tax_before_non_refundable_credits: 0 + households: + household: + members: [person_b, person_a, child] + state_code: GA + output: + ga_ctc_potential: 250 + ga_refundable_ctc: 250 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py index 20d82cc32e0..7e45d74c8f1 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py @@ -27,7 +27,9 @@ def formula(person, period, parameters): # A child qualifies only as the filer's dependent ("Your qualifying # child who is your dependent", Publication 503), and under IRC # 152(b)(1) a return on which the filer (or, if joint, either spouse) - # can be claimed as a dependent has no dependents. + # can be claimed as a dependent has no dependents. The model has no + # input for an IRC 152(e) release, so the 21(e)(5) child of a + # custodial parent who can be claimed is not kept. filer_is_dependent = person.tax_unit( "head_or_spouse_is_dependent_elsewhere", period ) diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py index 797f2bbcf39..501d124e5b6 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py @@ -19,6 +19,19 @@ def formula(tax_unit, period, parameters): # individual of any age — a disabled adult dependent or spouse # (care_expenses, a person-level yearly input). childcare = tax_unit("tax_unit_childcare_expenses", period) + # IRC 21(b)(2)(A)(ii) counts only expenses "for the care of a + # qualifying individual". A return on which a filer can be claimed as + # a dependent has no qualifying child under 13 unless the child is + # incapable of self-care, so its childcare expenses do not count. + p = parameters(period).gov.irs.credits.cdcc.eligibility + person = tax_unit.members + qualifying_child = person("is_cdcc_eligible", period) & ( + person("age", period) < p.child_age + ) + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + childcare = where( + dependent_filer & ~tax_unit.any(qualifying_child), 0, childcare + ) adult_care = add(tax_unit, period, ["care_expenses"]) expenses = childcare + adult_care cdcc_limit = tax_unit("cdcc_limit", period) diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py index bd478b60870..3bc002458d2 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py @@ -9,7 +9,7 @@ class ctc_qualifying_child(Variable): definition_period = YEAR defined_for = "is_tax_unit_dependent" reference = ( - "https://www.law.cornell.edu/uscode/text/26/24#a", + # Subsections (a) and (c). "https://www.law.cornell.edu/uscode/text/26/24#c", "https://www.law.cornell.edu/uscode/text/26/152#b_1", # Publication 501, Dependent Taxpayer Test. diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py index 70f5b877edd..23c93da7ba3 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py @@ -25,7 +25,7 @@ def formula(person, period, parameters): filer_meets_tin_requirement = person.tax_unit( "filer_meets_ctc_identification_requirements", period ) - # IRC 24(h)(4) gives the credit for "any dependent of the taxpayer"; + # IRC 24(h)(4)(A) gives the credit for "each dependent of the taxpayer"; # under IRC 152(b)(1) a return on which the filer (or, if joint, # either spouse) can be claimed as a dependent has no dependents. filer_is_dependent = person.tax_unit( diff --git a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py index c3a557b0d8f..91dd0cdffa3 100644 --- a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py @@ -18,11 +18,15 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE - # IRC 25A(g)(3): no credit to an individual claimed as a dependent by - # another taxpayer, and the Form 1040 instructions extend this to - # "your spouse if filing jointly", so either filer being claimed bars - # the return's credit. - filer_claimed = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # IRC 25A(g)(3): no credit to an individual for whom a section 151 + # deduction "is allowed to another taxpayer", and the Form 1040 + # instructions extend this to "your spouse if filing jointly", so + # either filer being claimed bars the return's credit. This turns on + # an actual claim (26 CFR 1.25A-1(f)), so it reads the claimed input + # rather than a claimability helper. + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimed_as_dependent_on_another_return", period) + filer_claimed = tax_unit.any(filer & claimed) requires_1098_t = aoc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_american_opportunity_credit_1098_t_or_exception", period) diff --git a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py index 75c2e133d15..0dba195cf08 100644 --- a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py @@ -19,11 +19,15 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE - # IRC 25A(g)(3): no credit to an individual claimed as a dependent by - # another taxpayer, and the Form 1040 instructions extend this to - # "your spouse if filing jointly", so either filer being claimed bars - # the return's credit. - filer_claimed = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # IRC 25A(g)(3): no credit to an individual for whom a section 151 + # deduction "is allowed to another taxpayer", and the Form 1040 + # instructions extend this to "your spouse if filing jointly", so + # either filer being claimed bars the return's credit. This turns on + # an actual claim (26 CFR 1.25A-1(f)), so it reads the claimed input + # rather than a claimability helper. + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimed_as_dependent_on_another_return", period) + filer_claimed = tax_unit.any(filer & claimed) requires_1098_t = llc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_lifetime_learning_credit_1098_t_or_exception", period) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py index 4e08716df78..7ef7231bb3b 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py @@ -13,7 +13,6 @@ class rrc_adult_count_with_valid_ssn(Variable): "https://www.law.cornell.edu/uscode/text/26/6428A#g_2", "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_A", "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_B", - "https://www.law.cornell.edu/uscode/text/26/6428#d_2", ) def formula(tax_unit, period, parameters): @@ -24,6 +23,6 @@ def formula(tax_unit, period, parameters): # An "eligible individual" excludes "any individual with respect to # whom a deduction under section 151 is allowable to another # taxpayer" (IRC 6428(d)(2), 6428A(d)(2)) or "who is a dependent of - # another taxpayer" (IRC 6428B(c)(2)). + # another taxpayer" (IRC 6428B(c)(2)): a claimability test. claimed = person("claimed_as_dependent_on_another_return", period) return tax_unit.sum(head_or_spouse & has_valid_ssn & ~claimed) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py index 252017448f7..2ea2796ed3c 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py @@ -26,9 +26,17 @@ def formula(tax_unit, period, parameters): ) is_joint = tax_unit("tax_unit_is_joint", period) adults_with_ssn = tax_unit("rrc_adult_count_with_valid_ssn", period) + # The exception waives the identification rule, not the exclusion of + # a spouse who is another taxpayer's dependent. + person = tax_unit.members + dependent_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimed_as_dependent_on_another_return", period) + ) count_adults = where( armed_forces_exception, - 2, # Joint filers always have 2 adults (structural constant) + # Joint filers always have 2 adults (structural constant) + max_(2 - dependent_filers, 0), where(is_joint, adults_with_ssn, min_(adults_with_ssn, 1)), ) max_payment = ( diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py index 577228072bb..cc704eb482b 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py @@ -23,9 +23,17 @@ def formula(tax_unit, period, parameters): "rrc_qualifies_for_armed_forces_exception", period ) is_joint = tax_unit("tax_unit_is_joint", period) + # The exception waives the identification rule, not the exclusion of + # a spouse who is another taxpayer's dependent. + person = tax_unit.members + dependent_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimed_as_dependent_on_another_return", period) + ) count_adults = where( armed_forces_exception, - 2, # Joint filers always have 2 adults (structural constant) + # Joint filers always have 2 adults (structural constant) + max_(2 - dependent_filers, 0), where(is_joint, adults_with_ssn, min_(adults_with_ssn, 1)), ) # Per 26 USC 6428A(g)(3), children count only if: diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py index bbbf38b4a21..be4a6d8c1c0 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py @@ -23,9 +23,17 @@ def formula(tax_unit, period, parameters): "rrc_qualifies_for_armed_forces_exception", period ) is_joint = tax_unit("tax_unit_is_joint", period) + # The exception waives the identification rule, not the exclusion of + # a spouse who is another taxpayer's dependent. + person = tax_unit.members + dependent_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimed_as_dependent_on_another_return", period) + ) count_adults = where( armed_forces_exception, - 2, # Joint filers always have 2 adults (structural constant) + # Joint filers always have 2 adults (structural constant) + max_(2 - dependent_filers, 0), where(is_joint, adults_with_ssn, min_(adults_with_ssn, 1)), ) # Per 26 USC 6428(g)(1)(C), children count only if: diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py index 3b45af22df5..fd1bcc2bab0 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/student_loan_interest/student_loan_interest_ald_eligible.py @@ -20,11 +20,14 @@ class student_loan_interest_ald_eligible(Variable): def formula(person, period, parameters): head_or_spouse = person("is_tax_unit_head_or_spouse", period) - # Per IRC § 221(c), no deduction for an individual claimed as a - # dependent by another taxpayer; the Form 1040 instructions require - # that "you, or your spouse if filing jointly, aren't claimed as a - # dependent", so either filer being claimed bars the return. - filer_claimed = person.tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # Per IRC § 221(c), no deduction for an individual for whom a section + # 151 deduction "is allowed to another taxpayer"; the Form 1040 + # instructions require that "you, or your spouse if filing jointly, + # aren't claimed as a dependent", so either filer being claimed bars + # the return. This turns on an actual claim, so it reads the claimed + # input rather than a claimability helper. + claimed = person("claimed_as_dependent_on_another_return", period) + filer_claimed = person.tax_unit.any(head_or_spouse & claimed) # Per IRC § 221(e)(2), taxpayer cannot file as Married Filing Separately filing_status = person.tax_unit("filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.py index ac202b88a48..0b169af9c13 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.py @@ -27,4 +27,9 @@ def formula(tax_unit, period, parameters): "lifetime_learning_credit_potential", ], ) - return ~separate & (aoc_llc == 0) + # IRC 222(c)(3): no deduction to an individual for whom a section 151 + # deduction "may be allowed to another taxpayer". Without this, a + # filer barred from the education credits for that reason would + # become eligible here. + dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + return ~separate & (aoc_llc == 0) & ~dependent_filer diff --git a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py new file mode 100644 index 00000000000..dae5a093b99 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py @@ -0,0 +1,29 @@ +from policyengine_us.model_api import * + + +class ga_ctc_eligible_child(Variable): + value_type = bool + entity = Person + label = "Eligible child for the Georgia Child Tax Credit" + definition_period = YEAR + defined_for = StateCode.GA + reference = ( + "https://legiscan.com/GA/text/HB136/id/3204611/Georgia-2025-HB136-Enrolled.pdf#page=2", + "https://www.law.cornell.edu/uscode/text/26/24#c", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.ga.tax.income.credits.ctc + age = person("age", period) + # Georgia gives "qualifying child" the meaning of IRC 24(c) only, so + # it is not limited to children for whom the federal credit is + # allowed (IRC 24(a), 152(b)(1)). Whether a filer who can be claimed + # as a dependent can claim it is not settled; this keeps the IRC + # 24(c) child test. + p_federal = parameters(period).gov.irs.credits.ctc + ctc_eligible_child = ( + person("is_tax_unit_dependent", period) + & (age < p_federal.amount.base.thresholds[-1]) + & person("meets_ctc_child_identification_requirements", period) + ) + return ctc_eligible_child & (age < p.age_threshold) diff --git a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py index 61195c8cb2c..52e40cca462 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_potential.py @@ -14,20 +14,6 @@ class ga_ctc_potential(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.credits.ctc - person = tax_unit.members - age = person("age", period) - # Georgia gives "qualifying child" the meaning of IRC 24(c) only, so - # it is not limited to children for whom the federal credit is - # allowed (IRC 24(a), 152(b)(1)). Whether a filer who can be claimed - # as a dependent can claim it is not settled; this keeps the IRC - # 24(c) child test. - p_federal = parameters(period).gov.irs.credits.ctc - ctc_eligible_child = ( - person("is_tax_unit_dependent", period) - & (age < p_federal.amount.base.thresholds[-1]) - & person("meets_ctc_child_identification_requirements", period) - ) - ga_child_age_eligible = age < p.age_threshold - eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible) + eligible_children = add(tax_unit, period, ["ga_ctc_eligible_child"]) return eligible_children * p.amount From e4b8f36f08479f3bf47598ae0d3c7ecd1a06da84 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 13:01:52 -0400 Subject: [PATCH 21/42] Separate dependency facts and fix Hawaii qualified-person counts --- .../head-dependent-elsewhere-swap.fixed.md | 2 +- .../watca/working_americans_tax_cut_act.py | 13 +- .../repeal_state_dependent_exemptions.py | 2 +- .../reforms/states/ct/hb5114/ct_hb5114.py | 11 +- ...t_elsewhere_head_spouse_swap_invariance.py | 211 ++++++++++- .../income/medicaid_is_tax_dependent.yaml | 49 +++ .../eitc_demographic_eligible.yaml | 78 +++- .../savers_credit_eligible_person.yaml | 22 ++ .../az_aged_exemption_eligible_person.yaml | 38 +- .../food_excise/hi_food_excise_credit.yaml | 339 ++++++++++++++++++ .../tax/income/credits/hi_act_115_rebate.yaml | 63 ++++ ...w_income_household_renters_tax_credit.yaml | 65 ++++ .../tax/income/exemptions/mi_exemptions.yaml | 89 +++++ .../oh_personal_exemptions_eligible.yaml | 11 + ...nter_relief_pre_one_claimant_eligible.yaml | 21 ++ ...imable_as_dependent_on_another_return.yaml | 34 ++ ...nt_elsewhere_without_filing_exception.yaml | 36 ++ ...nt_elsewhere_without_filing_exception.yaml | 119 ++++++ .../tests/policy/contrib/congress/watca.yaml | 33 +- .../contrib/states/ct/hb5114/ct_hb5114.yaml | 2 +- .../eligibility/eitc_demographic_eligible.py | 8 +- .../az_aged_exemption_eligible_person.py | 10 +- .../food_excise/hi_food_excise_credit.py | 5 +- ...hi_food_excise_credit_minor_child_count.py | 8 +- .../hi_food_excise_exemption_amount.py | 31 +- .../tax/income/credits/hi_act_115_rebate.py | 20 +- ...low_income_household_renters_tax_credit.py | 21 +- .../credits/me_affordability_payment.py | 4 +- .../mi/tax/income/exemptions/mi_exemptions.py | 15 +- ...omestead_credit_refund_household_income.py | 2 +- .../credits/nm_medical_expense_credit.py | 2 +- .../oh_personal_exemptions_eligible_person.py | 2 +- ...renter_relief_pre_one_claimant_eligible.py | 9 +- ...laimable_as_dependent_on_another_return.py | 23 ++ .../claimed_as_dependent_on_another_return.py | 11 +- .../dependent_claimant_filing_exception.py | 21 ++ ...dent_elsewhere_without_filing_exception.py | 22 ++ .../every_filer_is_dependent_elsewhere.py | 4 +- .../tax_unit/head_is_dependent_elsewhere.py | 8 +- .../head_or_spouse_is_dependent_elsewhere.py | 10 +- ...dent_elsewhere_without_filing_exception.py | 23 ++ ...ad_spouse_count_not_dependent_elsewhere.py | 8 +- .../tax_unit/spouse_is_dependent_elsewhere.py | 8 +- 43 files changed, 1399 insertions(+), 114 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml create mode 100644 policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml create mode 100644 policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py create mode 100644 policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py create mode 100644 policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py create mode 100644 policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py diff --git a/changelog.d/head-dependent-elsewhere-swap.fixed.md b/changelog.d/head-dependent-elsewhere-swap.fixed.md index 42a32945776..5222c01b901 100644 --- a/changelog.d/head-dependent-elsewhere-swap.fixed.md +++ b/changelog.d/head-dependent-elsewhere-swap.fixed.md @@ -1 +1 @@ -The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers; Hawaii now limits the standard deduction of a filer who can be claimed as a dependent; the federal EITC without a qualifying child now requires that no filer can be claimed as a dependent; and New Mexico's medical care credit requires a filer who is both 65 or older and not a dependent when a filer is a dependent elsewhere. +The federal standard deduction and the New York, Michigan, Oregon, Virginia, Missouri, New Mexico, Maine and Hawaii rules for a filer claimed as a dependent elsewhere no longer depend on which spouse is labelled head; each follows its jurisdiction's rule for a joint return where one spouse can be claimed. New York's $3,100 dependent standard deduction now applies only to single filers; Hawaii now limits the standard deduction of a filer who can be claimed as a dependent; the federal EITC without a qualifying child now requires that no filer can be claimed as a dependent; and New Mexico's medical care credit requires a filer who is both 65 or older and not a dependent when a filer is a dependent elsewhere. Separate actual dependency claims from claimability and the nonrequired claimant filing exception, preserving dependent deduction limits while restoring EITC and lawful own-dependent counts. Exclude externally claimable children from Hawaii food/excise ordinary exemptions. diff --git a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py index 860462a6ed5..27becb3a73f 100644 --- a/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py +++ b/policyengine_us/reforms/congress/watca/working_americans_tax_cut_act.py @@ -52,7 +52,11 @@ class watca_alternative_tax_eligible(Variable): label = "Eligible for WATCA alternative maximum tax" definition_period = YEAR reference = WATCA_REFERENCES - documentation = "Section 1A(b): MAGI < 175% of exemption, and not claimed as dependent on another return." + documentation = ( + "Section 1A(b): MAGI < 175% of exemption. Retains the reform's " + "modeled actual-dependent-claim restriction, which is not stated " + "in the cited bill's section 1A(b)(2)." + ) def formula(tax_unit, period, parameters): magi = tax_unit("watca_alternative_tax_magi", period) @@ -61,9 +65,10 @@ def formula(tax_unit, period, parameters): exemption_amount = p.amount[filing_status] income_limit = exemption_amount * p.income_limit_multiple income_eligible = magi < income_limit - head_dependent = tax_unit("head_is_dependent_elsewhere", period) - spouse_dependent = tax_unit("spouse_is_dependent_elsewhere", period) - not_dependent = ~head_dependent & ~spouse_dependent + person = tax_unit.members + claimed = person("claimed_as_dependent_on_another_return", period) + filer = person("is_tax_unit_head_or_spouse", period) + not_dependent = ~tax_unit.any(claimed & filer) return income_eligible & not_dependent class watca_alternative_max_tax(Variable): diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index 55765f89e4b..d05820bfb6b 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -93,7 +93,7 @@ def formula(person, period, parameters): # if they are not claimed as a dependent elsewhere head_or_spouse = person("is_tax_unit_head_or_spouse", period) dependent_on_another_return = person( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) # The personal exemption is also provided to dependents return ~dependent_on_another_return & head_or_spouse diff --git a/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py b/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py index 91d44eb6f65..c95f0f8a032 100644 --- a/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py +++ b/policyengine_us/reforms/states/ct/hb5114/ct_hb5114.py @@ -19,11 +19,12 @@ def formula(tax_unit, period, parameters): rent = add(tax_unit, period, ["rent"]) pays_rent = rent > 0 - # Must not be claimed as a dependent on another return - head_is_dependent_elsewhere = tax_unit( - "head_is_dependent_elsewhere", period - ) - not_dependent = ~head_is_dependent_elsewhere + # Preserve the modeled actual-claim gate; the cited bill does + # not state this restriction. + person = tax_unit.members + claimed = person("claimed_as_dependent_on_another_return", period) + head = person("is_tax_unit_head", period) + not_dependent = ~tax_unit.any(claimed & head) # Income eligibility filing_status = tax_unit("filing_status", period) diff --git a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py index 4e681240eb5..4d5f97ae469 100644 --- a/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py +++ b/policyengine_us/tests/core/test_dependent_elsewhere_head_spouse_swap_invariance.py @@ -13,7 +13,8 @@ - Either spouse limits the return (the IRC 63(c)(5) worksheet): the federal standard deduction, Oregon's and Hawaii's standard deductions, and Virginia's credit for low-income individuals. The federal EITC without a - qualifying child now also requires that neither spouse can be claimed. + qualifying child also requires that neither spouse is claimable outside the + nonrequired-claimant filing exception. - Only single filers: New York's dependent standard deduction. - Each spouse separately: Michigan's exemptions and Hawaii's food/excise, renters and Act 115 credits count only the filers who cannot be claimed. @@ -26,7 +27,8 @@ Hypothesis draws batches of married couples, with and without dependents, in the eight states with such a rule and one without, for 2021-2026, with either, -both or neither spouse claimed elsewhere, and checks 1 and 3. A seeded +both or neither spouse claimed or claimable elsewhere, independently of the +nonrequired-claimant filing exception, and checks 1 and 3. A seeded population and crafted cases check 1, 2 and 3 in every year, and two seeded couples plus the review case in every state check 1 for federal and state income tax in 2026. Each batch is one vectorized simulation. For each couple: @@ -41,8 +43,20 @@ 3. The helper identities: `head_or_spouse_is_dependent_elsewhere` equals `head_is_dependent_elsewhere | spouse_is_dependent_elsewhere`, `every_filer_is_dependent_elsewhere` equals their conjunction for a - couple, and `head_spouse_count_not_dependent_elsewhere` plus the claimed + couple, and `head_spouse_count_not_dependent_elsewhere` plus the claimable filers equals the number of filers. + +The 2025 seeded population also compares legacy input only with explicit +claimability equal to that input, protecting the new input's fallback. A +separate vectorized population holds actual claims and claimability fixed +while toggling the exception: general claimability rules and Medicaid stay +unchanged, childless EITC eligibility is restored, and Michigan/Hawaii may +count the filer's own dependents. Benefits derived from the restored federal +EITC may also change: Missouri's working family credit eligibility must track +positive federal EITC for this population's joint returns with no investment +income. Its separate claimability gate stays fixed. YAML examples establish +the corresponding legal amounts, including the limited $2,450 federal +standard deduction. """ import numpy as np @@ -62,10 +76,11 @@ "state_income_tax", "state_income_tax_before_refundable_credits", ] -# The formulas that read whether a filer is claimed elsewhere. +# The formulas that read a filer's claimability or dependent-taxpayer status. CONSUMERS = [ "basic_standard_deduction", "eitc", + "eitc_demographic_eligible", "ny_standard_deduction", "mi_exemptions", "or_standard_deduction", @@ -81,11 +96,32 @@ "hi_deductions", "hi_act_115_rebate", "hi_food_excise_exemption_amount", + "hi_food_excise_credit_minor_child_count", "hi_food_excise_credit", "hi_tax_credit_for_low_income_household_renters_eligible", "hi_tax_credit_for_low_income_household_renters", ] CLAIM_PATTERNS = [(True, False), (False, True), (True, True), (False, False)] +DEPENDENT_TAXPAYER_TEST_OUTPUTS = { + "mi_exemptions", + "hi_act_115_rebate", + "hi_food_excise_exemption_amount", + "hi_food_excise_credit", + "hi_tax_credit_for_low_income_household_renters", +} +# Federal EITC eligibility is restored directly. The federal amount and +# Missouri eligibility can change as a consequence, without changing their +# general claimability gates. +EITC_RELATED_OUTPUTS = { + "eitc_demographic_eligible", + "eitc", + "mo_wftc_eligible", +} +EXCEPTION_SENSITIVE = DEPENDENT_TAXPAYER_TEST_OUTPUTS | EITC_RELATED_OUTPUTS +ACTUAL_CLAIM_OUTPUTS = [ + "medicaid_is_tax_dependent", + "medicaid_uses_missing_claimant_fallback", +] def money(high): @@ -123,11 +159,17 @@ def dependents(draw): @st.composite def couples(draw): + claimable = draw(st.sampled_from(CLAIM_PATTERNS)) + claimed = tuple(can_claim and draw(st.booleans()) for can_claim in claimable) return { "state": draw(st.sampled_from(AFFECTED)), "adults": [draw(adults()), draw(adults())], "dependents": draw(st.lists(dependents(), max_size=2)), - "claimed": draw(st.sampled_from(CLAIM_PATTERNS)), + "claimed": claimed, + "claimable": claimable, + "exception": tuple( + not is_claimed and draw(st.booleans()) for is_claimed in claimed + ), } @@ -214,7 +256,7 @@ def _crafted_cases(states, review_case_only=False): return cases -def _situation(units, year, variants): +def _situation(units, year, variants, explicit_claimability=False): # Each unit appears once per variant: (head is the first adult, first # adult claimed, second adult claimed). People keep their order and every # other input in every copy. @@ -227,11 +269,20 @@ def _situation(units, year, variants): "households": {}, } for i, unit in enumerate(units): - for copy, (first_is_head, claim_a, claim_b) in enumerate(variants(unit)): + for copy, variant in enumerate(variants(unit)): + first_is_head, claim_a, claim_b = variant[:3] + claimable = unit.get("claimable") + exception = unit.get("exception") + if len(variant) == 5: + claimable, exception = variant[3:] + if explicit_claimability: + claimable = (claim_a, claim_b) a, b = f"a_{i}_{copy}", f"b_{i}_{copy}" - for name, amounts, is_head, claimed in ( - (a, unit["adults"][0], first_is_head, claim_a), - (b, unit["adults"][1], not first_is_head, claim_b), + for adult_index, (name, amounts, is_head, claimed) in enumerate( + ( + (a, unit["adults"][0], first_is_head, claim_a), + (b, unit["adults"][1], not first_is_head, claim_b), + ) ): people[name] = { **amounts, @@ -240,6 +291,14 @@ def _situation(units, year, variants): "is_tax_unit_dependent": False, "claimed_as_dependent_on_another_return": claimed, } + if claimable is not None: + people[name]["claimable_as_dependent_on_another_return"] = ( + claimable[adult_index] + ) + if exception is not None: + people[name]["dependent_claimant_filing_exception"] = exception[ + adult_index + ] members = [a, b] groups["marital_units"][f"couple_{i}_{copy}"] = {"members": [a, b]} for j, amounts in enumerate(unit["dependents"]): @@ -250,6 +309,10 @@ def _situation(units, year, variants): "is_tax_unit_spouse": False, "is_tax_unit_dependent": True, } + if explicit_claimability: + people[child]["claimable_as_dependent_on_another_return"] = ( + amounts.get("claimed_as_dependent_on_another_return", False) + ) members.append(child) groups["marital_units"][f"single_{i}_{copy}_{j}"] = {"members": [child]} for group, prefix in ( @@ -278,7 +341,7 @@ def _calc(sim, name, year): return np.asarray(sim.calculate(name, year), dtype=float) -def _check_swap(units, year, outputs): +def _check_swap(units, year, outputs, capture_fallback=False): sim = Simulation(situation=_situation(units, year, _swap_variants)) states = np.array([u["state"] for u in units]) for name in outputs: @@ -304,6 +367,125 @@ def _check_swap(units, year, outputs): np.testing.assert_array_equal(independent + head + spouse, 2) every = _calc(sim, "every_filer_is_dependent_elsewhere", year) np.testing.assert_array_equal(every, np.minimum(head, spouse)) + blocked = _calc(sim, "is_dependent_elsewhere_without_filing_exception", year) + filers = _calc(sim, "is_tax_unit_head", year) + _calc( + sim, "is_tax_unit_spouse", year + ) + np.testing.assert_array_equal( + _calc( + sim, "head_or_spouse_is_dependent_elsewhere_without_filing_exception", year + ), + sim.populations["tax_unit"].any(blocked.astype(bool) & filers.astype(bool)), + ) + if capture_fallback: + # Keep only arrays so the compatibility comparison can release this + # simulation before constructing the next full model. + return { + name: _calc(sim, name, year) + for name in TAX_UNIT_OUTPUTS + CONSUMERS + ACTUAL_CLAIM_OUTPUTS + } + + +def _check_fallback(units, year, legacy_outputs): + # The absence of a new input is simulation-wide: compare separate + # simulations so Core does not fill omitted people with input defaults. + explicit_sim = Simulation( + situation=_situation(units, year, _swap_variants, explicit_claimability=True) + ) + for name in TAX_UNIT_OUTPUTS + CONSUMERS + ACTUAL_CLAIM_OUTPUTS: + np.testing.assert_allclose( + legacy_outputs[name], + _calc(explicit_sim, name, year), + atol=TOLERANCE, + rtol=0, + err_msg=f"{name} changes when fallback claimability is made explicit", + ) + + +def _exception_variants(unit): + claimable = unit["claimed"] + return [ + (True, False, False, claimable, (False, False)), + (False, False, False, claimable, (False, False)), + (True, False, False, claimable, (True, True)), + (False, False, False, claimable, (True, True)), + ] + + +def _check_exception_scope(): + year = 2025 + units = [ + { + "state": state, + "adults": [{"age": 26, "employment_income": 2_000}, {"age": 30}], + "dependents": children, + "claimed": claimed, + } + for state in AFFECTED + # Isolate ordinary dependents: publicly supported minors have a + # separate Hawaii qualification that does not use this exception. + for children in ( + [], + [{"age": 5, "hi_food_excise_credit_child_receiving_public_support": False}], + ) + for claimed in CLAIM_PATTERNS + ] + sim = Simulation(situation=_situation(units, year, _exception_variants)) + for name in CONSUMERS + ACTUAL_CLAIM_OUTPUTS: + values = _calc(sim, name, year) + if name in ACTUAL_CLAIM_OUTPUTS: + # Person outputs have two filers and optionally one child per unit. + start = 0 + for unit in units: + size = 2 + len(unit["dependents"]) + copies = values[start : start + 4 * size].reshape(4, size) + np.testing.assert_array_equal(copies[0:2], copies[2:4]) + start += 4 * size + continue + copies = values.reshape(-1, 4) + np.testing.assert_allclose( + copies[:, 0::2], copies[:, 1::2], atol=TOLERANCE, rtol=0 + ) + if name not in EXCEPTION_SENSITIVE: + np.testing.assert_allclose( + copies[:, :2], + copies[:, 2:], + atol=TOLERANCE, + rtol=0, + err_msg=f"The filing exception changes general claimability rule {name}", + ) + eligibility = _calc(sim, "eitc_demographic_eligible", year).reshape(-1, 4) + childless = np.array([not unit["dependents"] for unit in units]) + claimable = np.array([any(unit["claimed"]) for unit in units]) + assert not eligibility[childless & claimable, :2].any() + assert eligibility[childless, 2:].all() + # RSMo 143.177.2 requires federal EITC. These units file jointly, so the + # separate dependent-filer stop does not apply, and their zero investment + # income meets Missouri's limit. The exception's Missouri effect must + # therefore follow positive federal EITC, rather than alter claimability. + missouri = np.array([unit["state"] == "MO" for unit in units]) + federal_eitc = _calc(sim, "eitc", year).reshape(-1, 4) + missouri_eligible = _calc(sim, "mo_wftc_eligible", year).reshape(-1, 4) + np.testing.assert_array_equal( + missouri_eligible[missouri].astype(bool), federal_eitc[missouri] > 0 + ) + direct_claimability = _calc( + sim, "head_or_spouse_is_dependent_elsewhere", year + ).reshape(-1, 4) + np.testing.assert_array_equal( + direct_claimability, np.repeat(claimable[:, None], 4, axis=1) + ) + for name, state in (("mi_exemptions", "MI"), ("hi_food_excise_credit", "HI")): + values = _calc(sim, name, year).reshape(-1, 4) + restores_child = np.array( + [ + unit["state"] == state + and bool(unit["dependents"]) + and sum(unit["claimed"]) == 1 + for unit in units + ] + ) + assert (values[restores_child, 2:] > values[restores_child, :2]).all() def _monotone_variants(unit): @@ -355,7 +537,12 @@ def test_claimed_spouse_does_not_depend_on_head_label(year, units): @pytest.mark.parametrize("year", YEARS) def test_seeded_population(year): units = _seeded_couples(AFFECTED, per_state=2) + _crafted_cases(AFFECTED) - _check_swap(units, year, TAX_UNIT_OUTPUTS + CONSUMERS) + legacy_outputs = _check_swap( + units, year, TAX_UNIT_OUTPUTS + CONSUMERS, capture_fallback=year == 2025 + ) + if year == 2025: + _check_fallback(units, year, legacy_outputs) + _check_exception_scope() _check_monotone(units, year) diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml new file mode 100644 index 00000000000..c04ad8732f3 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_is_tax_dependent.yaml @@ -0,0 +1,49 @@ +- name: Claimability alone does not create an expected Medicaid tax-dependent claim + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + # 42 CFR 435.603(f)(1)-(2) uses expected actual claims, not claimability. + medicaid_is_tax_dependent: false + medicaid_uses_missing_claimant_fallback: false + medicaid_uses_non_filer_rules: false + +- name: An expected actual claim invokes Medicaid's dependent and missing-claimant rules + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + medicaid_is_tax_dependent: true + medicaid_uses_missing_claimant_fallback: true + medicaid_uses_non_filer_rules: true + +- name: The tax filing exception leaves Medicaid's expected actual claim unchanged + period: 2025 + input: + age: 18 + is_tax_unit_head: true + is_tax_unit_spouse: false + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + medicaid_has_known_claiming_tax_unit: false + tax_unit_is_filer: true + output: + medicaid_is_tax_dependent: true + medicaid_uses_missing_claimant_fallback: true + medicaid_uses_non_filer_rules: true diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml index b922b61cb47..584e066b887 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_demographic_eligible.yaml @@ -258,7 +258,7 @@ # qualifying child. eitc_demographic_eligible: true -- name: 2025 childless single filer whose parent files only to claim a refund is not a dependent +- name: 2025 claimable childless filer qualifies when nonrequired parent files only for a withholding refund period: 2025 input: people: @@ -266,6 +266,8 @@ age: 26 employment_income: 2_000 claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true tax_units: tax_unit: members: [head] @@ -274,7 +276,75 @@ members: [head] state_code: CA output: - # Publication 596 (2025, p.18): the credit is allowed when the person who can claim - # you files only to claim a refund; such a person is not a dependent, so the - # input is false. + # Pub. 596 PDF p.18: nonrequired parent files only for a refund of income tax + # withheld. Pub. 501 PDF p.24: claimability still limits the standard deduction. eitc_demographic_eligible: true + basic_standard_deduction: 2_450 + +- name: 2025 claimable childless filer qualifies when nonrequired parent files no return + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF p.18 also covers a nonrequired claimant who files no return. + # The exception changes EITC, while Pub. 501 PDF p.24 still limits deductions. + eitc_demographic_eligible: true + basic_standard_deduction: 2_450 + +- name: 2025 unclaimed childless filer remains ineligible when claimable by a parent required to file + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF p.18 Example 2: choosing not to claim the filer is insufficient. + eitc_demographic_eligible: false + basic_standard_deduction: 2_450 + +- name: 2025 unclaimed childless filer remains ineligible when parent files for refundable credits + period: 2025 + input: + people: + head: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + # Pub. 596 PDF pp.18-19: filing for a credit is not filing solely for a refund + # of withholding or estimated payments, even if no return was required. + eitc_demographic_eligible: false + basic_standard_deduction: 2_450 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml index 7309a5c6dc6..c070b58f18f 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/retirement_saving/savers_credit_eligible_person.yaml @@ -17,3 +17,25 @@ is_full_time_student: false output: savers_credit_eligible_person: false + +- name: Savers credit admits a claimable filer who is not actually claimed + period: 2025 + input: + age: 26 + is_full_time_student: false + is_tax_unit_head_or_spouse: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + savers_credit_eligible_person: true + +- name: Savers credit excludes an actually claimed filer + period: 2025 + input: + age: 26 + is_full_time_student: false + is_tax_unit_head_or_spouse: true + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + savers_credit_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml index a4d945380ab..1bb415b0fed 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.yaml @@ -4,13 +4,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true tax_units: tax_unit: az_filing_status: JOINT - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: true members: [person1, person2] household: members: [person1, person2] @@ -24,13 +24,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: true person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true tax_units: tax_unit: az_filing_status: JOINT - head_is_dependent_elsewhere: true - spouse_is_dependent_elsewhere: true members: [person1, person2] household: members: [person1, person2] @@ -44,13 +44,13 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false tax_units: tax_unit: az_filing_status: SEPARATE - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: false members: [person1, person2] household: members: [person1, person2] @@ -64,19 +64,41 @@ people: person1: is_tax_unit_head: true + claimed_as_dependent_on_another_return: false person2: is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false person3: is_tax_unit_head: false is_tax_unit_spouse: false tax_units: tax_unit: az_filing_status: SEPARATE - head_is_dependent_elsewhere: false - spouse_is_dependent_elsewhere: false members: [person1, person2, person3] household: members: [person1, person2, person3] state_code: AZ output: az_aged_exemption_eligible_person: [true, false, false] + +- name: Arizona aged exemption retains an unclaimed claimable filer + period: 2025 + input: + state_code: AZ + age: 70 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + az_aged_exemption_eligible_person: true + +- name: Arizona aged exemption excludes an actually claimed filer + period: 2025 + input: + state_code: AZ + age: 70 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + az_aged_exemption_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml index 260651381eb..c4daec08bc0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.yaml @@ -595,3 +595,342 @@ output: # Only the spouse who cannot be claimed counts: 1 x $220. hi_food_excise_credit: 220 + +# N-311 (2022 and 2025), PDF p.1 lines 2-3, excludes externally claimable +# persons from both ordinary qualified persons and publicly supported minors. +# At $4,000 AGI, 2022 lines 7-10 give 2 x $110; 2025 lines 7-9 give 2 x $220. +- name: 2022 independent joint filers exclude externally claimable publicly supported child when person_a is head + period: 2022 + input: + people: + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 independent joint filers exclude externally claimable publicly supported child when person_b is head + period: 2022 + input: + people: + person_b: + age: 45 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2025 independent joint filers exclude externally claimable publicly supported child when person_a is head + period: 2025 + input: + people: + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2025 independent joint filers exclude externally claimable publicly supported child when person_b is head + period: 2025 + input: + people: + person_b: + age: 45 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 40 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 joint return filing exception restores own ordinary dependent but excludes claimable head + period: 2022 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + +- name: 2022 joint return filing exception restores own ordinary dependent but excludes claimable spouse + period: 2022 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 220 + hi_food_excise_exemption_amount: 220 + +- name: 2025 joint return filing exception restores own ordinary dependent but excludes claimable head + period: 2025 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + +- name: 2025 joint return filing exception restores own ordinary dependent but excludes claimable spouse + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_food_excise_credit_minor_child_count: 0 + hi_food_excise_credit: 440 + hi_food_excise_exemption_amount: 440 + +# A publicly supported minor filer belongs to N-311 line 2, not line 3. +# At $16,000 AGI in 2022, 2 ordinary persons x $70 = $140. +- name: 2022 publicly supported minor filer is only an ordinary qualified person when labelled head + period: 2022 + input: + people: + person_a: + age: 17 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: true + is_tax_unit_spouse: false + hi_food_excise_credit_child_receiving_public_support: true + person_b: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + adjusted_gross_income: 16000 + households: + household: + members: [person_a, person_b] + state_code: HI + output: + hi_food_excise_credit: 140 + hi_food_excise_credit_minor_child_count: 0 + +- name: 2022 publicly supported minor filer is only an ordinary qualified person when labelled spouse + period: 2022 + input: + people: + person_b: + age: 20 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 17 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + is_tax_unit_head: false + is_tax_unit_spouse: true + hi_food_excise_credit_child_receiving_public_support: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + adjusted_gross_income: 16000 + households: + household: + members: [person_b, person_a] + state_code: HI + output: + hi_food_excise_credit: 140 + hi_food_excise_credit_minor_child_count: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml index 7c4030e8e4c..124ffe32e48 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_act_115_rebate.yaml @@ -252,3 +252,66 @@ state_code: HI output: hi_act_115_rebate: 0 + +# Act 115 sec.3(a)-(b), HRS 235-54(a), and Pub. 501 PDF p.11. +- name: Joint return filing exception restores own dependent but excludes claimable head from Act 115 + period: 2021 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_act_115_rebate: 600 + +- name: Joint return filing exception restores own dependent but excludes claimable spouse from Act 115 + period: 2021 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_act_115_rebate: 600 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml index 2dd4cd51e42..25fa5e14ce5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml @@ -101,3 +101,68 @@ # Schedule X lines 8-10: the spouse who cannot be claimed, plus their age # 65 exemption. 2 x $50. hi_tax_credit_for_low_income_household_renters: 100 + +# Schedule X lines 8-10; Pub. 501 filing exception restores only dependents. +- name: Joint return filing exception restores own dependent without claimable head personal or aged exemption + period: 2021 + input: + people: + person_a: + age: 66 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 66 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 150 + +- name: Joint return filing exception restores own dependent without claimable spouse personal or aged exemption + period: 2021 + input: + people: + person_b: + age: 66 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 66 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 150 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml index eee9da8f35d..e67f331c532 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/tax/income/exemptions/mi_exemptions.yaml @@ -445,3 +445,92 @@ # A joint return on which a spouse can be claimed claims no dependents # (IRS Publication 501), so the child adds no exemption. mi_exemptions: 7_300 + +# Pub. 501 (2025), Dependent Taxpayer Test, PDF p.11; MCL 206.30(4). +- name: 2025 joint return filing exception restores own dependent while claimable head keeps the dependent allowance + period: 2025 + input: + people: + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_a, person_b, child_1] + state_code: MI + output: + mi_exemptions: 13100 + +- name: 2025 joint return filing exception restores own dependent while claimable spouse keeps the dependent allowance + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 26 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + households: + household: + members: [person_b, person_a, child_1] + state_code: MI + output: + mi_exemptions: 13100 + +- name: 2025 single filer with a nonrequired claimant who files no return may claim their own dependent + period: 2025 + input: + people: + filer: + age: 26 + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [filer, child] + filing_status: SINGLE + households: + household: + members: [filer, child] + state_code: MI + output: + # $1,500 claimable-filer allowance + $5,800 for their child. + mi_exemptions: 7_300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml index 97f70fe2376..d9fe76117ae 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml @@ -27,3 +27,14 @@ is_tax_unit_dependent: true output: oh_personal_exemptions_eligible_person: true + +- name: Ohio personal exemption excludes a claimable but unclaimed filer + period: 2025 + input: + state_code: OH + is_tax_unit_head_or_spouse: true + is_tax_unit_dependent: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + oh_personal_exemptions_eligible_person: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml new file mode 100644 index 00000000000..64ee21b5111 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.yaml @@ -0,0 +1,21 @@ +- name: Utah property relief retains an unclaimed claimable filer + period: 2025 + input: + state_code: UT + age: 66 + rent: 12_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + ut_homeowner_renter_relief_pre_one_claimant_eligible: true + +- name: Utah property relief excludes an actually claimed filer + period: 2025 + input: + state_code: UT + age: 66 + rent: 12_000 + claimed_as_dependent_on_another_return: true + claimable_as_dependent_on_another_return: true + output: + ut_homeowner_renter_relief_pre_one_claimant_eligible: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml b/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml new file mode 100644 index 00000000000..2530e578832 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/person/claimable_as_dependent_on_another_return.yaml @@ -0,0 +1,34 @@ +- name: An existing actual claim supplies the default claimability + period: 2025 + input: + claimed_as_dependent_on_another_return: true + output: + claimable_as_dependent_on_another_return: true + +- name: An existing false claim input preserves default independence + period: 2025 + input: + claimed_as_dependent_on_another_return: false + output: + claimable_as_dependent_on_another_return: false + +- name: No claim inputs preserves default independence + period: 2025 + input: + age: 26 + output: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: false + +- name: A parent can claim the filer without making or expecting a claim + period: 2025 + input: + age: 26 + employment_income: 2_000 + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + basic_standard_deduction: 2_450 + eitc_demographic_eligible: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml b/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml new file mode 100644 index 00000000000..4164a45064a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/person/is_dependent_elsewhere_without_filing_exception.yaml @@ -0,0 +1,36 @@ +- name: Unclaimed but claimable filer fails the dependent taxpayer test + period: 2025 + input: + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + output: + dependent_claimant_filing_exception: false + is_dependent_elsewhere_without_filing_exception: true + +- name: Nonrequired refund-only claimant restores dependent taxpayer eligibility + period: 2025 + input: + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + output: + # Publication 501 PDF p.11, Dependent Taxpayer Test exception. + is_dependent_elsewhere_without_filing_exception: false + claimable_as_dependent_on_another_return: true + +- name: Nonrequired claimant filing no return restores dependent taxpayer eligibility + period: 2025 + input: + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + output: + # Publication 596 PDF p.18 includes the nonrequired claimant who files no return. + is_dependent_elsewhere_without_filing_exception: false + claimable_as_dependent_on_another_return: true + +- name: The filing exception does not create claimability + period: 2025 + input: + claimable_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + output: + is_dependent_elsewhere_without_filing_exception: false diff --git a/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml new file mode 100644 index 00000000000..8b9e439d173 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.yaml @@ -0,0 +1,119 @@ +- name: Claimable unclaimed head prevents claiming the return's own dependents + period: 2025 + input: + people: + person_a: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + person_b: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: Claimable unclaimed spouse prevents claiming the return's own dependents + period: 2025 + input: + people: + person_b: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_a: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: false + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_b, person_a] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: The head's nonrequired claimant filing exception restores own-dependent eligibility + period: 2025 + input: + people: + person_a: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + person_b: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [person_a, person_b] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + +- name: The spouse's nonrequired claimant filing exception restores own-dependent eligibility + period: 2025 + input: + people: + person_b: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + person_a: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + tax_units: + tax_unit: + members: [person_b, person_a] + output: + head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false + +- name: One exception is insufficient when the other spouse has an ordinary claimant + period: 2025 + input: + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: false + tax_units: + tax_unit: + members: [head, spouse] + output: + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true + +- name: A claimable child does not apply the dependent taxpayer test to independent filers + period: 2025 + input: + people: + head: + age: 40 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + child: + age: 10 + is_tax_unit_head: false + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, child] + output: + head_or_spouse_is_dependent_elsewhere_without_filing_exception: false diff --git a/policyengine_us/tests/policy/contrib/congress/watca.yaml b/policyengine_us/tests/policy/contrib/congress/watca.yaml index 500ed7e863b..2b40103ff7f 100644 --- a/policyengine_us/tests/policy/contrib/congress/watca.yaml +++ b/policyengine_us/tests/policy/contrib/congress/watca.yaml @@ -440,7 +440,7 @@ gov.contrib.congress.watca.in_effect: true adjusted_gross_income: 30_000 filing_status: SINGLE - head_is_dependent_elsewhere: true + claimed_as_dependent_on_another_return: true output: # Even though income is below limit, not eligible because claimed as dependent watca_alternative_tax_eligible: false @@ -450,9 +450,18 @@ reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object input: gov.contrib.congress.watca.in_effect: true - adjusted_gross_income: 30_000 - filing_status: JOINT - spouse_is_dependent_elsewhere: true + people: + head: + is_tax_unit_head: true + claimed_as_dependent_on_another_return: false + spouse: + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + adjusted_gross_income: 30_000 + filing_status: JOINT output: watca_alternative_tax_eligible: false @@ -529,10 +538,18 @@ reforms: policyengine_us.reforms.congress.watca.working_americans_tax_cut_act.watca_reform_object input: gov.contrib.congress.watca.in_effect: true - adjusted_gross_income: 30_000 - filing_status: JOINT - head_is_dependent_elsewhere: true - spouse_is_dependent_elsewhere: true + people: + head: + is_tax_unit_head: true + claimed_as_dependent_on_another_return: true + spouse: + is_tax_unit_spouse: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [head, spouse] + adjusted_gross_income: 30_000 + filing_status: JOINT output: watca_alternative_tax_eligible: false diff --git a/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml b/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml index 9557ae1102e..bc07699ca08 100644 --- a/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml +++ b/policyengine_us/tests/policy/contrib/states/ct/hb5114/ct_hb5114.yaml @@ -427,11 +427,11 @@ age: 20 employment_income: 30_000 rent: 12_000 + claimed_as_dependent_on_another_return: true tax_units: tax_unit: members: [person1] filing_status: SINGLE - head_is_dependent_elsewhere: true households: household: members: [person1] diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py index 053ca2799c0..9cb742b764f 100644 --- a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_demographic_eligible.py @@ -33,8 +33,12 @@ def formula(tax_unit, period, parameters): # IRC § 32(c)(1)(A)(ii)(III): without a qualifying child, the filer # must not be a dependent of another taxpayer; on a joint return, # "neither you nor your spouse can be claimed as a dependent by another - # person" (IRS Publication 596). - dependent_elsewhere = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # person" (IRS Publication 596). A nonrequired claimant who files + # no return or only for a withholding/estimated-payment refund is + # excepted, without changing the standard-deduction limitation. + dependent_elsewhere = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) childless_eligible = ( tax_unit.any(meets_age_requirements & is_filer_or_spouse) & ~dependent_elsewhere diff --git a/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py b/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py index 6169e38bb5e..af27ae56bd4 100644 --- a/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py +++ b/policyengine_us/variables/gov/states/az/tax/income/exemptions/az_aged_exemption_eligible_person.py @@ -8,6 +8,7 @@ class az_aged_exemption_eligible_person(Variable): unit = USD definition_period = YEAR defined_for = StateCode.AZ + reference = "https://azdor.gov/sites/default/files/document/FORMS_INDIVIDUAL_2025_140i.pdf#page=6" def formula(person, period, parameters): head = person("is_tax_unit_head", period) @@ -17,9 +18,6 @@ def formula(person, period, parameters): filing_status = tax_unit("az_filing_status", period) separate = filing_status == filing_status.possible_values.SEPARATE - dependent_head = tax_unit("head_is_dependent_elsewhere", period) - - dependent_spouse = tax_unit("spouse_is_dependent_elsewhere", period) - spouse_eligible_condition = ~dependent_spouse * ~separate - - return (head & ~dependent_head) | (spouse & spouse_eligible_condition) + # Form 140, Box 8, excludes each spouse only when actually claimed. + claimed_elsewhere = person("claimed_as_dependent_on_another_return", period) + return ~claimed_elsewhere & (head | (spouse & ~separate)) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py index 220561807b8..a1c5b5ff4aa 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit.py @@ -16,8 +16,9 @@ class hi_food_excise_credit(Variable): def formula(tax_unit, period, parameters): # HRS 235-55.85(a): only a taxpayer who cannot be claimed as a # dependent may claim the credit, for each qualified exemption; the - # exemption amount counts only filers who cannot be claimed when - # either can. Minor children receiving public support count for this + # exemption amount excludes own ordinary dependents when either + # filer can be claimed without the Dependent Taxpayer Test exception. + # Minor children receiving public support count for this # credit under 235-55.85(c), so they keep their amount unless every # filer can be claimed. every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py index c4cc41a8d42..15cb4a0c8de 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_credit_minor_child_count.py @@ -7,6 +7,10 @@ class hi_food_excise_credit_minor_child_count(Variable): label = "Minor child's number for the Hawaii Food/Excise Tax Credit" definition_period = YEAR defined_for = StateCode.HI + reference = ( + "https://files.hawaii.gov/tax/forms/2022/n311_i.pdf#page=1", + "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.hi.tax.income.credits.food_excise_tax @@ -19,10 +23,10 @@ def formula(tax_unit, period, parameters): # Form N-311 line 3 lists minor children who, among other tests, # "Cannot be claimed as a dependent by another taxpayer"; the filers # are listed on line 2. - claimed_elsewhere = person("claimed_as_dependent_on_another_return", period) + claimable_elsewhere = person("claimable_as_dependent_on_another_return", period) filer = person("is_tax_unit_head_or_spouse", period) eligible_minor_child = ( - is_child & minor & public_support_over_half & ~claimed_elsewhere & ~filer + is_child & minor & public_support_over_half & ~claimable_elsewhere & ~filer ) return tax_unit.sum(eligible_minor_child) diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py index c597ba1352b..09127fcd2c1 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py @@ -9,6 +9,7 @@ class hi_food_excise_exemption_amount(Variable): defined_for = StateCode.HI reference = ( "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=50", + "https://files.hawaii.gov/tax/forms/2022/n311_i.pdf#page=1", "https://files.hawaii.gov/tax/forms/2025/n311_i.pdf#page=1", "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) @@ -18,7 +19,6 @@ def formula(tax_unit, period, parameters): income = tax_unit("adjusted_gross_income", period) - minor_child_include = p.minor_child.in_effect # Count exemptions as number of people # The legal code defines exemptions as Hawaii exemptions, # which normally include additional exemptions for aged and @@ -43,23 +43,34 @@ def formula(tax_unit, period, parameters): p.amount.surviving_spouse.calc(income), ], ) - exemptions = tax_unit("exemptions_count", period) + # Both 2022 and 2025 N-311 lines 2-3 exclude every person who can + # be claimed by another taxpayer, including children. In 2022, + # lines 8-10 price ordinary persons and publicly supported minors + # separately; 2025 lines 8-9 combine the two qualified counts. + externally_claimable = tax_unit.sum( + tax_unit.members("claimable_as_dependent_on_another_return", period) + ) + exemptions = max_( + tax_unit("exemptions_count", period) - externally_claimable, 0 + ) minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) if p.minor_child.in_effect: # Reduce number of exemptions by the number of minor children, # who get a fixed amount instead. - claimable_exemptions = exemptions - minor_children + claimable_exemptions = max_(exemptions - minor_children, 0) minor_child_exemptions = 0 else: claimable_exemptions = exemptions minor_child_exemptions = minor_children # Form N-311 counts only people who cannot be claimed as a dependent # by another taxpayer. A return on which a filer can be claimed - # generally claims no dependents (IRS Publication 501; its exception - # for a claimer who files only for a refund is not modeled), so it - # counts the filers who cannot be claimed and, through HRS - # 235-55.85(c), their minor children receiving public support. - dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # generally claims no dependents (IRS Publication 501, Dependent + # Taxpayer Test). Its filing exception restores ordinary dependent + # entries, but never a claimable person's own exemption. Publicly + # supported minors count independently under HRS 235-55.85(c). + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) @@ -67,6 +78,8 @@ def formula(tax_unit, period, parameters): independent_filers > 0, minor_child_exemptions, 0 ) qualified_exemptions = where( - dependent_filer, mixed_return_exemptions, claimable_exemptions + dependent_taxpayer, mixed_return_exemptions, claimable_exemptions ) + every_filer_dependent = tax_unit("every_filer_is_dependent_elsewhere", period) + qualified_exemptions = where(every_filer_dependent, 0, qualified_exemptions) return qualified_exemptions * amount_per_exemption diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py index 734e6305b38..adc6f987c58 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py @@ -13,6 +13,7 @@ class hi_act_115_rebate(Variable): "https://data.capitol.hawaii.gov/sessions/session2022/bills/SB514_CD2_.HTM", "https://files.hawaii.gov/tax/news/announce/ann22-03.pdf#page=2", "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=45", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) def formula(tax_unit, period, parameters): @@ -44,12 +45,23 @@ def formula(tax_unit, period, parameters): # taxpayer claims the refund for the exemptions they are entitled to. # A filer who can be claimed has no exemption (HRS 235-54(a)), and a # return on which a filer can be claimed generally claims no dependents - # (IRS Publication 501; its exception for a claimer who files only for - # a refund is not modeled), so such a return counts only the filers - # who cannot be claimed. + # (IRS Publication 501, Dependent Taxpayer Test). Its filing exception + # restores those dependents, but does not restore the claimable + # filer's personal exemption or qualification as a claimant. dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) - qualified_exemptions = where(dependent_filer, independent_filers, exemptions) + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + dependent_filers = filers - independent_filers + ordinary_exemptions = max_(exemptions - dependent_filers, 0) + qualified_exemptions = where( + dependent_taxpayer, independent_filers, ordinary_exemptions + ) + qualified_exemptions = where( + dependent_filer & (independent_filers == 0), 0, qualified_exemptions + ) return amount_per_exemption * qualified_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py index f60088206d8..a744ce369bf 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py @@ -11,6 +11,7 @@ class hi_tax_credit_for_low_income_household_renters(Variable): reference = ( "https://files.hawaii.gov/tax/legal/hrs/hrs_235.pdf#page=48", "https://files.hawaii.gov/tax/forms/2025/schx_i.pdf#page=1", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", ) def formula(tax_unit, period, parameters): @@ -19,9 +20,10 @@ def formula(tax_unit, period, parameters): # Schedule X lines 8-10 count only people who cannot be claimed as a # dependent by another taxpayer, with an extra exemption for each # such filer aged 65 or over. A return on which a filer can be - # claimed generally claims no dependents (IRS Publication 501; its - # exception for a claimer who files only for a refund is not modeled), - # so it counts only the filers who cannot be claimed. + # claimed generally claims no dependents (IRS Publication 501, + # Dependent Taxpayer Test). Its filing exception restores the + # return's own dependents, while claimable filers and their age + # additions remain excluded. aged_head = (tax_unit("age_head", period) >= p.aged_age_threshold) & ~tax_unit( "head_is_dependent_elsewhere", period ) @@ -30,15 +32,18 @@ def formula(tax_unit, period, parameters): ) & ~tax_unit("spouse_is_dependent_elsewhere", period) aged_exemptions = aged_head.astype(int) + aged_spouse.astype(int) - dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) - exemptions = where( - dependent_filer, - independent_filers, - tax_unit("exemptions_count", period), + filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) + dependent_filers = filers - independent_filers + ordinary_exemptions = max_( + tax_unit("exemptions_count", period) - dependent_filers, 0 ) + exemptions = where(dependent_taxpayer, independent_filers, ordinary_exemptions) total_exemptions = exemptions + aged_exemptions return p.amount * total_exemptions diff --git a/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py b/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py index b488cf00424..7f1a547ff0e 100644 --- a/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py +++ b/policyengine_us/variables/gov/states/me/tax/income/credits/me_affordability_payment.py @@ -14,7 +14,7 @@ class me_affordability_payment(Variable): "payment from a Special Revenue Fund rather than a refundable tax " "credit. PolicyEngine folds the payment into Maine's refundable " "credit aggregation to surface it in tax-unit-level outputs. " - "Sec. T-1, sub-§1(C)(3) excludes individuals claimed as a " + "Sec. T-1, sub-§1(C)(3) excludes individuals who may be claimed as a " "dependent on another taxpayer's return, evaluated per recipient." ) @@ -24,6 +24,6 @@ def formula(tax_unit, period, parameters): is_recipient = person("is_tax_unit_head", period) | person( "is_tax_unit_spouse", period ) - dependent_elsewhere = person("claimed_as_dependent_on_another_return", period) + dependent_elsewhere = person("claimable_as_dependent_on_another_return", period) eligible_recipients = tax_unit.sum(is_recipient & ~dependent_elsewhere) return eligible_recipients * p.amount diff --git a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py index 1940d6d1af8..7c9874052ea 100644 --- a/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py +++ b/policyengine_us/variables/gov/states/mi/tax/income/exemptions/mi_exemptions.py @@ -40,8 +40,9 @@ def formula(tax_unit, period, parameters): # individual, we apply it to each spouse, so the other keeps their own # personal exemption (and any stillbirth exemption). A joint return on # which either spouse can be claimed generally claims no dependents - # (IRS Publication 501, "Dependent Taxpayer Test"; its exception for a - # claimer who files only for a refund is not modeled). + # (IRS Publication 501, "Dependent Taxpayer Test"). The filing + # exception restores the return's own dependents, while each + # claimable filer keeps the $1,500 allowance. filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period @@ -53,11 +54,19 @@ def formula(tax_unit, period, parameters): ) * p.personal dependent_filer_exemptions = dependent_filers * p.dependent_on_other_return + dependent_taxpayer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + own_dependents = max_(tax_unit("tax_unit_size", period) - filers, 0) + dependent_exemptions = where(dependent_taxpayer, 0, own_dependents) * p.personal + # Total exemptions return ( where( dependent_filers > 0, - independent_filer_exemptions + dependent_filer_exemptions, + independent_filer_exemptions + + dependent_filer_exemptions + + dependent_exemptions, personal_exemption, ) + disabled_exemption diff --git a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py index ef104baa938..416d57eeb7a 100644 --- a/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py +++ b/policyengine_us/variables/gov/states/mn/tax/property/homestead_credit_refund/mn_homestead_credit_refund_household_income.py @@ -33,7 +33,7 @@ def formula(tax_unit, period, parameters): people = tax_unit.members is_dependent = people("is_tax_unit_dependent", period) | people( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) social_security = people("social_security", period) taxable_social_security = people("taxable_social_security", period) diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py index 1ee85c8bfae..5f7a3dbbec3 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/nm_medical_expense_credit.py @@ -26,7 +26,7 @@ def formula(tax_unit, period, parameters): # dependent claim it when the other spouse is. So when a filer is a # dependent elsewhere, a filer who is not must be 65 or older. filer = person("is_tax_unit_head_or_spouse", period) - claimed = person("claimed_as_dependent_on_another_return", period) + claimed = person("claimable_as_dependent_on_another_return", period) independent_aged_filer = tax_unit.any(filer & ~claimed & aged) dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) age_eligible = where( diff --git a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py index e222b7bf362..9a45b4bda79 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py @@ -16,7 +16,7 @@ def formula(person, period, parameters): # if they are not claimed as a dependent elsewhere head_or_spouse = person("is_tax_unit_head_or_spouse", period) dependent_on_another_return = person( - "claimed_as_dependent_on_another_return", period + "claimable_as_dependent_on_another_return", period ) # The personal exemption is also provided to dependents dependent = person("is_tax_unit_dependent", period) diff --git a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py index 28c3dbbdf03..146db78e423 100644 --- a/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py +++ b/policyengine_us/variables/gov/states/ut/tax/property/homeowner_renter_relief/ut_homeowner_renter_relief_pre_one_claimant_eligible.py @@ -36,9 +36,12 @@ def formula(tax_unit, period, parameters): claimant_is_tax_unit_dependent = tax_unit.any( claimants & tax_unit.members("is_tax_unit_dependent", period) ) - claimant_is_dependent_elsewhere = tax_unit( - "head_is_dependent_elsewhere", period - ) | tax_unit("spouse_is_dependent_elsewhere", period) + # Utah Code 59-2a-205(4) and 59-2a-305(2)(a) turn on another + # taxpayer actually claiming the exemption or dependent credit. + claimant_is_dependent_elsewhere = tax_unit.any( + claimants + & tax_unit.members("claimed_as_dependent_on_another_return", period) + ) claimant_is_dependent = ( claimant_is_tax_unit_dependent | claimant_is_dependent_elsewhere ) diff --git a/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py new file mode 100644 index 00000000000..eccd696bbec --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class claimable_as_dependent_on_another_return(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Can be claimed as a dependent elsewhere" + documentation = ( + "Whether another taxpayer can claim the person as a dependent: a " + "deduction under IRC section 151 for the person is allowable to " + "another taxpayer, whether or not claimed. Defaults to the actual " + "or expected claim input for compatibility with existing callers. " + "The dependent_claimant_filing_exception does not change this fact " + "or the dependent standard-deduction limit." + ) + reference = ( + "https://www.law.cornell.edu/uscode/text/26/151#c", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=24", + ) + + def formula(person, period, parameters): + return person("claimed_as_dependent_on_another_return", period) diff --git a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py index f941fcb3254..cfaa9d32825 100644 --- a/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py +++ b/policyengine_us/variables/household/demographic/person/claimed_as_dependent_on_another_return.py @@ -7,11 +7,8 @@ class claimed_as_dependent_on_another_return(Variable): definition_period = YEAR label = "Is claimed as a dependent elsewhere" documentation = ( - "Whether another taxpayer can claim the person as a dependent, that " - "is, a deduction under IRC section 151 for the person is allowable to " - "another taxpayer, whether or not it is claimed. A person whose " - "would-be claimer is not required to file and files only to claim a " - "refund is not such a dependent (IRS Publications 501 and 596). Rules " - "that turn on actually being claimed also read this input; the model " - "has no separate input for that." + "Whether the person is actually claimed, or expected to be claimed, " + "as a dependent in another tax unit. Claimability, whether or not " + "another taxpayer claims the person, is represented separately by " + "claimable_as_dependent_on_another_return." ) diff --git a/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py b/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py new file mode 100644 index 00000000000..d4acf034fba --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/dependent_claimant_filing_exception.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class dependent_claimant_filing_exception(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Dependent's would-be claimant meets the filing exception" + documentation = ( + "Whether the person who could claim this person (all such claimants " + "if more than one) is not required to file an income tax return and " + "either files no return or files solely to obtain a refund of income " + "tax withheld or estimated tax paid. A return filed to claim refundable " + "tax credits, including EITC, does not qualify. This fact permits EITC and claiming " + "one's own dependents under the Dependent Taxpayer Test; it does not " + "remove general claimability or its standard-deduction limitation." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) diff --git a/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py b/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py new file mode 100644 index 00000000000..79384c81b5e --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/is_dependent_elsewhere_without_filing_exception.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class is_dependent_elsewhere_without_filing_exception(Variable): + value_type = bool + entity = Person + definition_period = YEAR + label = "Claimable elsewhere without the claimant filing exception" + documentation = ( + "Whether the person is claimable as a dependent on another return " + "and the would-be claimant does not meet the nonrequired-filing " + "exception. Used for childless EITC and the Dependent Taxpayer Test." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) + + def formula(person, period, parameters): + return person("claimable_as_dependent_on_another_return", period) & ~person( + "dependent_claimant_filing_exception", period + ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py index 161339f0358..e2135a89019 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/every_filer_is_dependent_elsewhere.py @@ -5,9 +5,9 @@ class every_filer_is_dependent_elsewhere(Variable): value_type = bool entity = TaxUnit definition_period = YEAR - label = "Every head and spouse is a dependent elsewhere" + label = "Every head and spouse is claimable elsewhere" documentation = ( - "Whether the tax unit has a head or spouse claimed as a dependent in " + "Whether the tax unit has a head or spouse claimable as a dependent in " "another tax unit and none who is not. False for a tax unit with " "neither a head nor a spouse." ) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py index 04064b39948..b8d43c53e5e 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_is_dependent_elsewhere.py @@ -6,12 +6,12 @@ class head_is_dependent_elsewhere(Variable): entity = TaxUnit definition_period = YEAR label = "Is tax-unit head a dependent elsewhere" - documentation = "Whether the filer for this tax unit is claimed as a dependent in another tax unit." + documentation = "Whether the filer for this tax unit is claimable as a dependent in another tax unit." def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) is_head = person("is_tax_unit_head", period) - return tax_unit.any(claimed_on_another_return & is_head) + return tax_unit.any(claimable_on_another_return & is_head) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py index 493bf92c3c4..34f5593350b 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere.py @@ -5,17 +5,17 @@ class head_or_spouse_is_dependent_elsewhere(Variable): value_type = bool entity = TaxUnit definition_period = YEAR - label = "Head or spouse is a dependent elsewhere" + label = "Head or spouse is claimable elsewhere" documentation = ( - "Whether the head or the spouse of this tax unit is claimed as a " + "Whether the head or the spouse of this tax unit is claimable as a " "dependent in another tax unit. Unlike head_is_dependent_elsewhere, " "this does not depend on which spouse is labelled head." ) def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) filer = person("is_tax_unit_head_or_spouse", period) - return tax_unit.any(claimed_on_another_return & filer) + return tax_unit.any(claimable_on_another_return & filer) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py new file mode 100644 index 00000000000..26a1d3d3eb4 --- /dev/null +++ b/policyengine_us/variables/household/demographic/tax_unit/head_or_spouse_is_dependent_elsewhere_without_filing_exception.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class head_or_spouse_is_dependent_elsewhere_without_filing_exception(Variable): + value_type = bool + entity = TaxUnit + definition_period = YEAR + label = "Either filer claimable elsewhere without the claimant filing exception" + documentation = ( + "Whether a tax-unit head or spouse is claimable on another return " + "without the claimant filing exception. Symmetric Dependent Taxpayer " + "Test gate; other claimability rules use the ordinary helper." + ) + reference = ( + "https://www.irs.gov/pub/irs-prior/p596--2025.pdf#page=18", + "https://www.irs.gov/pub/irs-prior/p501--2025.pdf#page=11", + ) + + def formula(tax_unit, period, parameters): + person = tax_unit.members + dependent = person("is_dependent_elsewhere_without_filing_exception", period) + filer = person("is_tax_unit_head_or_spouse", period) + return tax_unit.any(dependent & filer) diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py index 348b7a993a2..3d040cf683d 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_spouse_count_not_dependent_elsewhere.py @@ -7,14 +7,14 @@ class head_spouse_count_not_dependent_elsewhere(Variable): definition_period = YEAR label = "Head and spouse count not dependent elsewhere" documentation = ( - "Number of tax unit heads and spouses who are not claimed as a " + "Number of tax unit heads and spouses who are not claimable as a " "dependent in another tax unit." ) def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) filer = person("is_tax_unit_head_or_spouse", period) - return tax_unit.sum(filer & ~claimed_on_another_return) + return tax_unit.sum(filer & ~claimable_on_another_return) diff --git a/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py b/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py index b8fb0bc9c5f..a8e0183e464 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py +++ b/policyengine_us/variables/household/demographic/tax_unit/spouse_is_dependent_elsewhere.py @@ -6,12 +6,12 @@ class spouse_is_dependent_elsewhere(Variable): entity = TaxUnit definition_period = YEAR label = "Tax-unit spouse is dependent elsewhere" - documentation = "Whether the spouse of the filer for this tax unit is claimed as a dependent in another tax unit." + documentation = "Whether the spouse of the filer for this tax unit is claimable as a dependent in another tax unit." def formula(tax_unit, period, parameters): person = tax_unit.members - claimed_on_another_return = person( - "claimed_as_dependent_on_another_return", period + claimable_on_another_return = person( + "claimable_as_dependent_on_another_return", period ) is_spouse = person("is_tax_unit_spouse", period) - return tax_unit.any(claimed_on_another_return & is_spouse) + return tax_unit.any(claimable_on_another_return & is_spouse) From c7f969f872be92689e1606f0b564cb3c8a121d16 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 13:16:55 -0400 Subject: [PATCH 22/42] Clarify claimability outside the current tax unit --- .../person/claimable_as_dependent_on_another_return.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py index eccd696bbec..5643c2eb30d 100644 --- a/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py +++ b/policyengine_us/variables/household/demographic/person/claimable_as_dependent_on_another_return.py @@ -7,7 +7,8 @@ class claimable_as_dependent_on_another_return(Variable): definition_period = YEAR label = "Can be claimed as a dependent elsewhere" documentation = ( - "Whether another taxpayer can claim the person as a dependent: a " + "Whether another taxpayer, in another tax unit, can claim the person " + "as a dependent: a " "deduction under IRC section 151 for the person is allowable to " "another taxpayer, whether or not claimed. Defaults to the actual " "or expected claim input for compatibility with existing callers. " From 3f8870261ca2ff4ec7c4b28e106d6c4cadcec935 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 13:39:43 -0400 Subject: [PATCH 23/42] Read the split dependency inputs from #10024 #10024 now separates three facts: is claimed, can be claimed, and the would-be claimant's filing exception. - IRC 152(b)(1) gates (child tax credit, credit for other dependents, under-13 care child and its expenses, dependents in exemptions_count, ARPA dependents, Ohio dependents, the Hawaii and Wisconsin reforms) read head_or_spouse_is_dependent_elsewhere_without_filing_exception, so a filer within the Publication 501 exception keeps their dependents. - Own exemptions, recovery rebates and the tuition deduction read claimability. - Education credits and student loan interest keep reading the claimed input. - Tests: the filing exception and a filer who can be claimed but is not. Co-Authored-By: Claude Opus 5.5 --- .../hi_dependent_exemption_reform.py | 4 +- .../wi_dependent_exemption_reform.py | 4 +- .../credits/cdcc/cdcc_relevant_expenses.yaml | 29 +++++ .../ctc/maximum/ctc_qualifying_child.yaml | 84 ++++++++++++++ ...gible_for_american_opportunity_credit.yaml | 26 ++++- .../recovery_rebate_credit.yaml | 19 +++ .../student_loan_interest_ald_eligible.yaml | 19 ++- .../tuition_and_fees_deduction_eligible.yaml | 18 +++ .../exemptions/exemptions_count.yaml | 109 ++++++++++++++++++ .../gov/irs/credits/cdcc/cdcc_eligible.py | 2 +- .../credits/cdcc/cdcc_relevant_expenses.py | 4 +- .../ctc/maximum/ctc_qualifying_child.py | 2 +- .../ctc_adult_individual_maximum.py | 2 +- .../rrc_adult_count_with_valid_ssn.py | 4 +- .../recovery_rebate_credit/rrc_arpa.py | 2 +- .../rrc_arpa_dependents_with_valid_ssn.py | 2 +- .../credits/recovery_rebate_credit/rrc_caa.py | 2 +- .../recovery_rebate_credit/rrc_cares.py | 2 +- .../exemptions/exemptions_count.py | 22 ++-- .../oh_personal_exemptions_eligible_person.py | 2 +- 20 files changed, 332 insertions(+), 26 deletions(-) diff --git a/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py b/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py index 3349f9a9f31..d1b0283f0b2 100644 --- a/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/hi/dependent_exemption/hi_dependent_exemption_reform.py @@ -18,7 +18,7 @@ def formula(tax_unit, period, parameters): # A return on which the filer (or, if joint, either spouse) can be # claimed as a dependent has no dependents (IRC 152(b)(1)). is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) # Apply age limit if in effect @@ -56,7 +56,7 @@ def formula(tax_unit, period, parameters): # A return on which the filer (or, if joint, either spouse) can be # claimed as a dependent has no dependents (IRC 152(b)(1)). is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( diff --git a/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py b/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py index 85e40eefe7f..0471bac9c9a 100644 --- a/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/wi/dependent_exemption/wi_dependent_exemption_reform.py @@ -18,7 +18,7 @@ def formula(tax_unit, period, parameters): # A return on which the filer (or, if joint, either spouse) can be # claimed as a dependent has no dependents (IRC 152(b)(1)). is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) # Apply age limit if in effect @@ -56,7 +56,7 @@ def formula(tax_unit, period, parameters): # A return on which the filer (or, if joint, either spouse) can be # claimed as a dependent has no dependents (IRC 152(b)(1)). is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml index 15dfeb1fe4a..11084acd2b8 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml @@ -209,3 +209,32 @@ output: count_cdcc_eligible: 2 cdcc_relevant_expenses: 3000 + +# Publication 501, Dependent Taxpayer Test: a filer whose would-be claimant is not +# required to file and files no return or only for a refund can claim dependents. +- name: 2025 childcare for the child of a claimable filer within the filing exception counts + period: 2025 + input: + people: + parent: + age: 30 + employment_income: 5000 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [parent, child] + state_code: TX + output: + count_cdcc_eligible: 1 + cdcc_relevant_expenses: 3000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml index 0a40c086281..fc96d52a969 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/ctc_qualifying_child.yaml @@ -159,3 +159,87 @@ output: ctc_qualifying_child: [false, false, true] ctc_maximum: 2_200 + +# Publication 501, Dependent Taxpayer Test: a filer whose would-be claimant is not +# required to file and files no return or only for a refund can claim dependents. +- name: 2025 claimable single filer within the filing exception keeps the CTC-qualifying child + period: 2025 + input: + people: + parent: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + ctc_qualifying_child: [false, true] + +- name: 2025 joint return whose claimable spouse is within the filing exception keeps the CTC-qualifying child (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + ctc_qualifying_child: [false, false, true] + +- name: 2025 joint return whose claimable spouse is within the filing exception keeps the CTC-qualifying child (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + ctc_qualifying_child: [false, false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml index 908d3d3c657..b932cf52198 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml @@ -178,7 +178,6 @@ output: is_eligible_for_american_opportunity_credit: false - - name: 2024 student claimed as a dependent elsewhere cannot claim the credit period: 2024 input: @@ -307,3 +306,28 @@ state_code: TX output: is_eligible_for_american_opportunity_credit: [true, true] + +# IRC 25A(g)(3) and 26 CFR 1.25A-1(f): the bar applies to a student who is +# claimed; a student who can be claimed but is not keeps the credit. +- name: 2024 student who can be claimed but is not claimed keeps the credit + period: 2024 + input: + people: + person_a: + age: 20 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + households: + household: + members: [person_a] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: true diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml index 045236d3234..2aa4b19bdf1 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml @@ -311,3 +311,22 @@ state_code: TX output: rrc_arpa: 2800 + +# IRC 6428B(c)(2): an individual "who is a dependent of another taxpayer" is not +# eligible, whether or not that taxpayer claims them. +- name: 2021 single filer who can be claimed but is not claimed gets no ARPA rebate + period: 2021 + input: + people: + person_a: + age: 20 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + households: + household: + members: [person_a] + state_code: TX + output: + rrc_arpa: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml index 97566eae089..e161077097f 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/student_loan_interest/student_loan_interest_ald_eligible.yaml @@ -38,7 +38,6 @@ output: student_loan_interest_ald_eligible: false - - name: 2024 single filer claimed as a dependent elsewhere cannot deduct student loan interest period: 2024 input: @@ -106,3 +105,21 @@ state_code: TX output: student_loan_interest_ald_eligible: [false, false] + +# IRC 221(c): the bar applies to an individual who is claimed. +- name: 2024 single filer who can be claimed but is not claimed can deduct student loan interest + period: 2024 + input: + people: + person_a: + age: 22 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + households: + household: + members: [person_a] + state_code: TX + output: + student_loan_interest_ald_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml index dd6bcd8d147..c55f696055c 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/tuition_and_fees_deduction_eligible.yaml @@ -116,3 +116,21 @@ state_code: TX output: tuition_and_fees_deduction_eligible: true + +- name: 2020 single filer who can be claimed but is not claimed is not eligible + period: 2020 + input: + people: + person_a: + age: 20 + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [person_a] + households: + household: + members: [person_a] + state_code: TX + output: + # IRC 222(c)(3): a section 151 deduction "may be allowed to another taxpayer". + tuition_and_fees_deduction_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml index 16c899b35cf..40ce1bf5142 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/exemptions/exemptions_count.yaml @@ -131,3 +131,112 @@ state_code: TX output: exemptions_count: 3 + +# Publication 501, Dependent Taxpayer Test: a filer whose would-be claimant is not +# required to file and files no return or only for a refund can claim dependents. +# The filer's own exemption is still zero (IRC 151(d)(2)). +- name: 2025 claimable single filer within the filing exception counts the child but not themselves + period: 2025 + input: + people: + parent: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + exemptions_count: 1 + +- name: 2025 joint return whose claimable spouse is within the filing exception counts the other spouse and the child (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + exemptions_count: 2 + +- name: 2025 joint return whose claimable spouse is within the filing exception counts the other spouse and the child (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + exemptions_count: 2 + +- name: 2025 filer who can be claimed but is not claimed has no own exemption and no dependents + period: 2025 + input: + people: + parent: + age: 30 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + # Exemptions turn on whether the filer can be claimed, not on an actual claim. + exemptions_count: 0 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py index 7e45d74c8f1..c7dbc104092 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py @@ -31,7 +31,7 @@ def formula(person, period, parameters): # input for an IRC 152(e) release, so the 21(e)(5) child of a # custodial parent who can be claimed is not kept. filer_is_dependent = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) qualifies_by_age = ~head_or_spouse & (age < p.child_age) & ~filer_is_dependent # Subsections (b)(1)(B) (dependent) and (b)(1)(C) (spouse). Subsection diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py index 501d124e5b6..fba13665ad0 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py @@ -28,7 +28,9 @@ def formula(tax_unit, period, parameters): qualifying_child = person("is_cdcc_eligible", period) & ( person("age", period) < p.child_age ) - dependent_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + dependent_filer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) childcare = where( dependent_filer & ~tax_unit.any(qualifying_child), 0, childcare ) diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py index 3bc002458d2..f8906588f5d 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/ctc_qualifying_child.py @@ -30,6 +30,6 @@ def formula(person, period, parameters): # having no dependents, and on a joint return neither spouse may be # claimable (Publication 501, Dependent Taxpayer Test). filer_is_dependent = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) return age_eligible & meets_identification_requirements & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py index 23c93da7ba3..13c2ffd4c52 100644 --- a/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py +++ b/policyengine_us/variables/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.py @@ -29,7 +29,7 @@ def formula(person, period, parameters): # under IRC 152(b)(1) a return on which the filer (or, if joint, # either spouse) can be claimed as a dependent has no dependents. filer_is_dependent = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) return ( is_adult diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py index 7ef7231bb3b..63612661479 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py @@ -24,5 +24,5 @@ def formula(tax_unit, period, parameters): # whom a deduction under section 151 is allowable to another # taxpayer" (IRC 6428(d)(2), 6428A(d)(2)) or "who is a dependent of # another taxpayer" (IRC 6428B(c)(2)): a claimability test. - claimed = person("claimed_as_dependent_on_another_return", period) - return tax_unit.sum(head_or_spouse & has_valid_ssn & ~claimed) + claimable = person("claimable_as_dependent_on_another_return", period) + return tax_unit.sum(head_or_spouse & has_valid_ssn & ~claimable) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py index 2ea2796ed3c..0ffd5e954b4 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py @@ -31,7 +31,7 @@ def formula(tax_unit, period, parameters): person = tax_unit.members dependent_filers = tax_unit.sum( person("is_tax_unit_head_or_spouse", period) - & person("claimed_as_dependent_on_another_return", period) + & person("claimable_as_dependent_on_another_return", period) ) count_adults = where( armed_forces_exception, diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py index e91e2ee9a48..45245ce16a2 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa_dependents_with_valid_ssn.py @@ -17,7 +17,7 @@ def formula(tax_unit, period, parameters): # under 152(b)(1) a return on which the filer (or, if joint, either # spouse) can be claimed as a dependent has none. dependent_filer = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) is_dependent = person("is_tax_unit_dependent", period) & ~dependent_filer has_valid_ssn = person("meets_eitc_identification_requirements", period) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py index cc704eb482b..b0ee925ced1 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py @@ -28,7 +28,7 @@ def formula(tax_unit, period, parameters): person = tax_unit.members dependent_filers = tax_unit.sum( person("is_tax_unit_head_or_spouse", period) - & person("claimed_as_dependent_on_another_return", period) + & person("claimable_as_dependent_on_another_return", period) ) count_adults = where( armed_forces_exception, diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py index be4a6d8c1c0..c61fca0c9a7 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py @@ -28,7 +28,7 @@ def formula(tax_unit, period, parameters): person = tax_unit.members dependent_filers = tax_unit.sum( person("is_tax_unit_head_or_spouse", period) - & person("claimed_as_dependent_on_another_return", period) + & person("claimable_as_dependent_on_another_return", period) ) count_adults = where( armed_forces_exception, diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py b/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py index 2fec52064f4..57eaacb22f8 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/exemptions/exemptions_count.py @@ -10,7 +10,8 @@ class exemptions_count(Variable): documentation = ( "Number of personal exemptions on the federal return: one for each " "filer who cannot be claimed as a dependent by another taxpayer, plus " - "one for each dependent unless a filer can be claimed." + "one for each dependent unless a filer can be claimed and the " + "would-be claimant does not meet the filing exception." ) reference = ( "https://www.law.cornell.edu/uscode/text/26/151#d_2", @@ -25,13 +26,16 @@ class exemptions_count(Variable): ) def formula(tax_unit, period, parameters): - # IRC 151(d)(2) zeroes the exemption of a filer whom another taxpayer - # can claim, and IRC 152(b)(1) treats a return on which the filer (or, - # if joint, either spouse) can be claimed as having no dependents. So - # such a return counts only its filers who cannot be claimed. tax_unit_size = tax_unit("tax_unit_size", period) - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) - independent_filers = tax_unit( - "head_spouse_count_not_dependent_elsewhere", period + # IRC 151(d)(2): no exemption for a filer whom another taxpayer can + # claim as a dependent. + claimable_filer = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + own = tax_unit("head_spouse_count_not_dependent_elsewhere", period) + # IRC 152(b)(1): such a filer has no dependents, unless the would-be + # claimant is not required to file and files no return or only for a + # refund (Publication 501, Dependent Taxpayer Test). + no_dependents = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) - return where(filer_is_dependent, independent_filers, tax_unit_size) + dependents = where(no_dependents, 0, tax_unit("tax_unit_dependents", period)) + return where(claimable_filer, own + dependents, tax_unit_size) diff --git a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py index d19c249a8e9..813a61fd698 100644 --- a/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py +++ b/policyengine_us/variables/gov/states/oh/tax/income/exemptions/oh_personal_exemptions_eligible_person.py @@ -22,7 +22,7 @@ def formula(person, period, parameters): # on which the filer (or, if joint, either spouse) can be claimed as a # dependent has none (IRC 152(b)(1)). dependent_filer = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) dependent = person("is_tax_unit_dependent", period) & ~dependent_filer return (~dependent_on_another_return & head_or_spouse) | dependent From b2771d2f15466f0850788b5577fe3f503681607f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 14:58:06 -0400 Subject: [PATCH 24/42] Follow the corrected exemption count in three Hawaii credits #10024's food/excise credit, Act 115 rebate and renters credit took claimable filers off exemptions_count themselves. exemptions_count now leaves them out (and, outside the filing exception, the return's dependents), so those credits read it as it is; the filing-exception cases were one exemption short. The renters credit also leaves out a dependent who can be claimed by another taxpayer (Schedule X counts only people who cannot be), as the food/excise credit already did. Co-Authored-By: Claude Opus 5.5 --- ...w_income_household_renters_tax_credit.yaml | 126 ++++++++++++++++++ .../hi_food_excise_exemption_amount.py | 10 +- .../tax/income/credits/hi_act_115_rebate.py | 14 +- ...low_income_household_renters_tax_credit.py | 20 ++- 4 files changed, 150 insertions(+), 20 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml index 25fa5e14ce5..cc6de0fb9ea 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.yaml @@ -166,3 +166,129 @@ state_code: HI output: hi_tax_credit_for_low_income_household_renters: 150 + +# Schedule X counts only people who cannot be claimed as a dependent by another +# taxpayer, so a child in the household who can be claimed elsewhere adds no +# exemption. +- name: Joint return with a child who can be claimed elsewhere counts the two filers + period: 2021 + input: + people: + person_a: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 100 + +- name: Joint return with a child who cannot be claimed elsewhere counts the child + period: 2021 + input: + people: + person_a: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 150 + +- name: Joint return within the filing exception with a child who can be claimed elsewhere counts only the other spouse (the claimable spouse is the head) + period: 2021 + input: + people: + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_a, person_b, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 50 + +- name: Joint return within the filing exception with a child who can be claimed elsewhere counts only the other spouse (the claimable spouse is the spouse) + period: 2021 + input: + people: + person_b: + age: 30 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child_1: + age: 5 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child_1] + filing_status: JOINT + adjusted_gross_income: 4000 + hi_tax_credit_for_low_income_household_renters_eligible: true + households: + household: + members: [person_b, person_a, child_1] + state_code: HI + output: + hi_tax_credit_for_low_income_household_renters: 50 diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py index 09127fcd2c1..39bbb252a55 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/food_excise/hi_food_excise_exemption_amount.py @@ -47,11 +47,15 @@ def formula(tax_unit, period, parameters): # be claimed by another taxpayer, including children. In 2022, # lines 8-10 price ordinary persons and publicly supported minors # separately; 2025 lines 8-9 combine the two qualified counts. - externally_claimable = tax_unit.sum( - tax_unit.members("claimable_as_dependent_on_another_return", period) + # exemptions_count already leaves out a filer who can be claimed, so + # only the dependents who can be claimed elsewhere come off here. + person = tax_unit.members + externally_claimable_dependents = tax_unit.sum( + person("is_tax_unit_dependent", period) + & person("claimable_as_dependent_on_another_return", period) ) exemptions = max_( - tax_unit("exemptions_count", period) - externally_claimable, 0 + tax_unit("exemptions_count", period) - externally_claimable_dependents, 0 ) minor_children = tax_unit("hi_food_excise_credit_minor_child_count", period) if p.minor_child.in_effect: diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py index adc6f987c58..f0c3d84ac13 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_act_115_rebate.py @@ -52,16 +52,10 @@ def formula(tax_unit, period, parameters): independent_filers = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) - dependent_taxpayer = tax_unit( - "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period - ) - filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) - dependent_filers = filers - independent_filers - ordinary_exemptions = max_(exemptions - dependent_filers, 0) - qualified_exemptions = where( - dependent_taxpayer, independent_filers, ordinary_exemptions - ) + # exemptions_count applies these rules: it leaves out a claimable + # filer's own exemption and, unless the filing exception applies, the + # return's dependents. qualified_exemptions = where( - dependent_filer & (independent_filers == 0), 0, qualified_exemptions + dependent_filer & (independent_filers == 0), 0, exemptions ) return amount_per_exemption * qualified_exemptions diff --git a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py index a744ce369bf..d55ac2607ca 100644 --- a/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py +++ b/policyengine_us/variables/gov/states/hi/tax/income/credits/hi_low_income_household_renters/hi_low_income_household_renters_tax_credit.py @@ -32,18 +32,24 @@ def formula(tax_unit, period, parameters): ) & ~tax_unit("spouse_is_dependent_elsewhere", period) aged_exemptions = aged_head.astype(int) + aged_spouse.astype(int) + # exemptions_count applies these rules to the filers and to the + # return's dependents. A dependent who can be claimed by another + # taxpayer is also left out; a return without dependents has none to + # take off. dependent_taxpayer = tax_unit( "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) - independent_filers = tax_unit( - "head_spouse_count_not_dependent_elsewhere", period + person = tax_unit.members + externally_claimable_dependents = tax_unit.sum( + person("is_tax_unit_dependent", period) + & person("claimable_as_dependent_on_another_return", period) ) - filers = add(tax_unit, period, ["is_tax_unit_head_or_spouse"]) - dependent_filers = filers - independent_filers - ordinary_exemptions = max_( - tax_unit("exemptions_count", period) - dependent_filers, 0 + exemptions_count = tax_unit("exemptions_count", period) + exemptions = where( + dependent_taxpayer, + exemptions_count, + max_(exemptions_count - externally_claimable_dependents, 0), ) - exemptions = where(dependent_taxpayer, independent_filers, ordinary_exemptions) total_exemptions = exemptions + aged_exemptions return p.amount * total_exemptions From 163fcd4cfa98345d5ec0c358d2d35c8516789334 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 15:23:57 -0400 Subject: [PATCH 25/42] Address the second federal claimable-filer review - Education credits: a dependent student is not eligible on a return whose filer can be claimed outside the filing exception (IRC 25A(f)(1)(A)(iii), 152(b)(1)); the filer's own eligibility still turns on an actual claim. - Recovery rebates: no credit when every filer is a dependent of another taxpayer (IRC 6428B(a)); the CARES and CAA child prerequisite is a spouse with a valid SSN, whether or not that spouse is an eligible individual. - Child and dependent care: cdcc_qualifying_childcare_expenses holds the expense rule, and the single-parent work requirement reform and California's expenses read it. - References: Georgia HB 136 page 3; one entry per recovery rebate statute; quote IRC 24(h)(4)(A) exactly in the test comment. - Tests: dependent students, the two rebate cases, the reform and California expenses, and the Hawaii, Wisconsin and Treasury reforms with a claimable filer under both labellings. Co-Authored-By: Claude Opus 5.5 --- .../cdcc_single_parent_work_requirement.py | 2 +- .../ctc_adult_individual_maximum.yaml | 2 +- ...gible_for_american_opportunity_credit.yaml | 166 ++++++++++++++++++ .../recovery_rebate_credit.yaml | 96 ++++++++++ .../ca_cdcc_relevant_expenses.yaml | 61 +++++++ .../hi/dependent_exemption_reform_test.yaml | 54 ++++++ .../wi/dependent_exemption_reform_test.yaml | 54 ++++++ .../treasury/repeal_dependent_exemptions.yaml | 43 +++++ .../cdcc_single_parent_work_requirement.yaml | 57 ++++++ .../cdcc_qualifying_childcare_expenses.py | 32 ++++ .../credits/cdcc/cdcc_relevant_expenses.py | 20 +-- ...ligible_for_american_opportunity_credit.py | 8 + ...s_eligible_for_lifetime_learning_credit.py | 8 + .../rrc_adult_count_with_valid_ssn.py | 10 +- .../recovery_rebate_credit/rrc_arpa.py | 6 +- .../credits/recovery_rebate_credit/rrc_caa.py | 10 +- .../recovery_rebate_credit/rrc_cares.py | 10 +- .../credits/ca_cdcc_relevant_expenses.py | 2 +- .../credits/ctc/ga_ctc_eligible_child.py | 2 +- 19 files changed, 615 insertions(+), 28 deletions(-) create mode 100644 policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py diff --git a/policyengine_us/reforms/cdcc/cdcc_single_parent_work_requirement.py b/policyengine_us/reforms/cdcc/cdcc_single_parent_work_requirement.py index b83cb1ab43d..a84182005de 100644 --- a/policyengine_us/reforms/cdcc/cdcc_single_parent_work_requirement.py +++ b/policyengine_us/reforms/cdcc/cdcc_single_parent_work_requirement.py @@ -30,7 +30,7 @@ class cdcc_relevant_expenses(Variable): ) def formula(tax_unit, period, parameters): - expenses = tax_unit("tax_unit_childcare_expenses", period) + expenses = tax_unit("cdcc_qualifying_childcare_expenses", period) cdcc_limit = tax_unit("cdcc_limit", period) eligible_capped_expenses = min_(expenses, cdcc_limit) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml index 9c46915a88e..79cfb1685d3 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/ctc/maximum/individual/ctc_adult_individual_maximum.yaml @@ -61,7 +61,7 @@ members: [filer, sibling] state_code: TX output: - # IRC 24(h)(4) "any dependent of the taxpayer"; IRC 152(b)(1). + # IRC 24(h)(4)(A) "each dependent of the taxpayer"; IRC 152(b)(1). ctc_adult_individual_maximum: [0, 0] - name: 2025 joint return with one claimable spouse gets no credit for other dependents (the claimable spouse is the head) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml index b932cf52198..5544205c3d5 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.yaml @@ -331,3 +331,169 @@ state_code: TX output: is_eligible_for_american_opportunity_credit: true + +# IRC 25A(f)(1)(A)(iii): a dependent's tuition counts only if the taxpayer is +# allowed a section 151 deduction for the dependent, and a return on which a filer +# can be claimed has no dependents (IRC 152(b)(1)), unless the would-be claimant +# meets the filing exception (Publication 501). +- name: 2024 dependent student of a filer who can be claimed is not eligible + period: 2024 + input: + people: + parent: + age: 45 + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + student: + age: 19 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, student] + households: + household: + members: [parent, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, false] + +- name: 2024 dependent student of a claimable filer within the filing exception is eligible + period: 2024 + input: + people: + parent: + age: 45 + has_tin: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + student: + age: 19 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, student] + households: + household: + members: [parent, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, true] + +- name: 2024 dependent student of a filer who cannot be claimed is eligible + period: 2024 + input: + people: + parent: + age: 45 + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + student: + age: 19 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, student] + households: + household: + members: [parent, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, true] + +- name: 2024 dependent student of a joint return with one claimable spouse is not eligible (the claimable spouse is the head) + period: 2024 + input: + people: + person_a: + age: 45 + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: true + student: + age: 19 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, student] + filing_status: JOINT + households: + household: + members: [person_a, person_b, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, false, false] + +- name: 2024 dependent student of a joint return with one claimable spouse is not eligible (the claimable spouse is the spouse) + period: 2024 + input: + people: + person_b: + age: 45 + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 45 + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + student: + age: 19 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, student] + filing_status: JOINT + households: + household: + members: [person_b, person_a, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml index 2aa4b19bdf1..a1e77b517ef 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml @@ -330,3 +330,99 @@ state_code: TX output: rrc_arpa: 0 + +# IRC 6428B(a) allows the credit to an eligible individual, so a return whose only +# filer is a dependent of another taxpayer gets none, even when the filing +# exception lets that filer claim the child. +- name: 2021 claimable single filer within the filing exception gets no ARPA rebate for the child + period: 2021 + input: + people: + parent: + age: 30 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: TX + output: + rrc_arpa: 0 + +# IRC 6428(g)(1)(C) and 6428A(g)(3): children count when a spouse has a valid SSN, +# whether or not that spouse is an eligible individual. Here the spouse with the +# SSN can be claimed (within the filing exception) and serves in the Armed +# Forces; the other spouse has no SSN. +- name: 2020 child payments need a spouse with an SSN, not an eligible spouse with one (the claimable spouse is the head) + period: 2020 + input: + people: + person_a: + age: 30 + is_military: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 30 + meets_eitc_identification_requirements: false + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, child] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child] + state_code: TX + output: + # One adult payment under the Armed Forces exception plus the child's. + rrc_cares: 1700 + rrc_caa: 1200 + +- name: 2020 child payments need a spouse with an SSN, not an eligible spouse with one (the claimable spouse is the spouse) + period: 2020 + input: + people: + person_b: + age: 30 + meets_eitc_identification_requirements: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 30 + is_military: true + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + is_tax_unit_head: false + is_tax_unit_spouse: true + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, child] + filing_status: JOINT + households: + household: + members: [person_b, person_a, child] + state_code: TX + output: + rrc_cares: 1700 + rrc_caa: 1200 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/child_dependent_care/ca_cdcc_relevant_expenses.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/child_dependent_care/ca_cdcc_relevant_expenses.yaml index 41bb2e8353e..e415e951776 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/child_dependent_care/ca_cdcc_relevant_expenses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/child_dependent_care/ca_cdcc_relevant_expenses.yaml @@ -117,3 +117,64 @@ dependent_care_assistance_exclusion: 4_000 # min(10_000 expenses, $6,000 two-child cap - $4,000 exclusion, 38_000) = 2_000. ca_cdcc_relevant_expenses: 2_000 + +# FTB 3506 counts expenses for qualifying persons. The child of a filer who can be +# claimed as a dependent is not one (IRC 152(b)(1)); the incapacitated relative is. +- name: 2025 childcare for the child of a claimable filer does not count + period: 2025 + input: + people: + parent: + age: 30 + employment_income: 5000 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child, relative] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [parent, child, relative] + state_code: CA + output: + ca_cdcc_relevant_expenses: 0 + +- name: 2025 the same household with a filer who cannot be claimed counts the childcare + period: 2025 + input: + people: + parent: + age: 30 + employment_income: 5000 + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 5 + is_tax_unit_head: false + is_tax_unit_spouse: false + relative: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child, relative] + tax_unit_childcare_expenses: 3000 + households: + household: + members: [parent, child, relative] + state_code: CA + output: + ca_cdcc_relevant_expenses: 3000 diff --git a/policyengine_us/tests/policy/contrib/states/hi/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/hi/dependent_exemption_reform_test.yaml index 8a11f290155..398865a7a4d 100644 --- a/policyengine_us/tests/policy/contrib/states/hi/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/hi/dependent_exemption_reform_test.yaml @@ -150,3 +150,57 @@ # exemptions from 3,432 to 1,144, widening taxable income and raising HI # income tax from 2,855.97 to 3,029.86 (about +173.89). output: {hi_income_tax: 3029.86} + +# As in the baseline, a filer who can be claimed as a dependent has no exemption of +# their own and, outside the claimant filing exception, no dependents. +- name: HI dependent exemption reform - claimable single filer has no exemptions + period: 2025 + reforms: policyengine_us.reforms.states.hi.dependent_exemption.hi_dependent_exemption_reform.hi_dependent_exemption_reform + input: + gov.contrib.states.hi.dependent_exemption.in_effect: true + people: + parent: {age: 40, employment_income: 60_000, claimable_as_dependent_on_another_return: true} + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child1, child2], filing_status: SINGLE}} + households: {household: {members: [parent, child1, child2], state_name: HI}} + output: {hi_regular_exemptions: 0} + +- name: HI dependent exemption reform - the filing exception keeps the dependents only + period: 2025 + reforms: policyengine_us.reforms.states.hi.dependent_exemption.hi_dependent_exemption_reform.hi_dependent_exemption_reform + input: + gov.contrib.states.hi.dependent_exemption.in_effect: true + people: + parent: {age: 40, employment_income: 60_000, claimable_as_dependent_on_another_return: true, dependent_claimant_filing_exception: true} + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child1, child2], filing_status: SINGLE}} + households: {household: {members: [parent, child1, child2], state_name: HI}} + output: {hi_regular_exemptions: 2_288} + +- name: HI dependent exemption reform - joint return with one claimable spouse keeps the other spouse only (the claimable spouse is the head) + period: 2025 + reforms: policyengine_us.reforms.states.hi.dependent_exemption.hi_dependent_exemption_reform.hi_dependent_exemption_reform + input: + gov.contrib.states.hi.dependent_exemption.in_effect: true + people: + person_a: {age: 40, claimable_as_dependent_on_another_return: true, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_b: {age: 40, employment_income: 60_000, is_tax_unit_head: false, is_tax_unit_spouse: true} + child1: {age: 10, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_a, person_b, child1], filing_status: JOINT}} + households: {household: {members: [person_a, person_b, child1], state_name: HI}} + output: {hi_regular_exemptions: 1_144} + +- name: HI dependent exemption reform - joint return with one claimable spouse keeps the other spouse only (the claimable spouse is the spouse) + period: 2025 + reforms: policyengine_us.reforms.states.hi.dependent_exemption.hi_dependent_exemption_reform.hi_dependent_exemption_reform + input: + gov.contrib.states.hi.dependent_exemption.in_effect: true + people: + person_b: {age: 40, employment_income: 60_000, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_a: {age: 40, claimable_as_dependent_on_another_return: true, is_tax_unit_head: false, is_tax_unit_spouse: true} + child1: {age: 10, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_b, person_a, child1], filing_status: JOINT}} + households: {household: {members: [person_b, person_a, child1], state_name: HI}} + output: {hi_regular_exemptions: 1_144} diff --git a/policyengine_us/tests/policy/contrib/states/wi/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/wi/dependent_exemption_reform_test.yaml index 05709219f00..c6cfa3a680a 100644 --- a/policyengine_us/tests/policy/contrib/states/wi/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/wi/dependent_exemption_reform_test.yaml @@ -137,3 +137,57 @@ # 50,594. Wis. Stat. 71.06(1r): 3.5% x 14,680 + 4.4% x (50,480 - 14,680) # + 5.3% x (50,594 - 50,480) = 2,095.04 (baseline 2,032.42). output: {wi_income_tax: 2095.04} + +# As in the baseline, a filer who can be claimed as a dependent has no exemption of +# their own and, outside the claimant filing exception, no dependents. +- name: WI dependent exemption reform - claimable single filer has no exemptions + period: 2025 + reforms: policyengine_us.reforms.states.wi.dependent_exemption.wi_dependent_exemption_reform.wi_dependent_exemption_reform + input: + gov.contrib.states.wi.dependent_exemption.in_effect: true + people: + parent: {age: 40, employment_income: 60_000, claimable_as_dependent_on_another_return: true} + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child1, child2], filing_status: SINGLE}} + households: {household: {members: [parent, child1, child2], state_name: WI}} + output: {wi_base_exemption: 0} + +- name: WI dependent exemption reform - the filing exception keeps the dependents only + period: 2025 + reforms: policyengine_us.reforms.states.wi.dependent_exemption.wi_dependent_exemption_reform.wi_dependent_exemption_reform + input: + gov.contrib.states.wi.dependent_exemption.in_effect: true + people: + parent: {age: 40, employment_income: 60_000, claimable_as_dependent_on_another_return: true, dependent_claimant_filing_exception: true} + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child1, child2], filing_status: SINGLE}} + households: {household: {members: [parent, child1, child2], state_name: WI}} + output: {wi_base_exemption: 1_400} + +- name: WI dependent exemption reform - joint return with one claimable spouse keeps the other spouse only (the claimable spouse is the head) + period: 2025 + reforms: policyengine_us.reforms.states.wi.dependent_exemption.wi_dependent_exemption_reform.wi_dependent_exemption_reform + input: + gov.contrib.states.wi.dependent_exemption.in_effect: true + people: + person_a: {age: 40, claimable_as_dependent_on_another_return: true, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_b: {age: 40, employment_income: 60_000, is_tax_unit_head: false, is_tax_unit_spouse: true} + child1: {age: 10, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_a, person_b, child1], filing_status: JOINT}} + households: {household: {members: [person_a, person_b, child1], state_name: WI}} + output: {wi_base_exemption: 700} + +- name: WI dependent exemption reform - joint return with one claimable spouse keeps the other spouse only (the claimable spouse is the spouse) + period: 2025 + reforms: policyengine_us.reforms.states.wi.dependent_exemption.wi_dependent_exemption_reform.wi_dependent_exemption_reform + input: + gov.contrib.states.wi.dependent_exemption.in_effect: true + people: + person_b: {age: 40, employment_income: 60_000, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_a: {age: 40, claimable_as_dependent_on_another_return: true, is_tax_unit_head: false, is_tax_unit_spouse: true} + child1: {age: 10, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_b, person_a, child1], filing_status: JOINT}} + households: {household: {members: [person_b, person_a, child1], state_name: WI}} + output: {wi_base_exemption: 700} diff --git a/policyengine_us/tests/policy/contrib/treasury/repeal_dependent_exemptions.yaml b/policyengine_us/tests/policy/contrib/treasury/repeal_dependent_exemptions.yaml index ec4d5fb575f..42c59a9621b 100644 --- a/policyengine_us/tests/policy/contrib/treasury/repeal_dependent_exemptions.yaml +++ b/policyengine_us/tests/policy/contrib/treasury/repeal_dependent_exemptions.yaml @@ -63,3 +63,46 @@ output: exemptions_count: 2 exemptions: 0 + +# The personal exemptions that remain are the filers' own, and a filer who can be +# claimed as a dependent has none (IRC 151(d)(2)). +- name: Claimable single filer with a child has no personal exemption + period: 2022 + reforms: policyengine_us.reforms.treasury.repeal_dependent_exemptions.repeal_dependent_exemptions + input: + gov.contrib.treasury.repeal_dependent_exemptions: true + people: + parent: {age: 30, claimable_as_dependent_on_another_return: true} + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child]}} + households: {household: {members: [parent, child], state_code: TX}} + output: + exemptions_count: 0 + +- name: Joint return with one claimable spouse keeps the other spouse's exemption (the claimable spouse is the head) + period: 2022 + reforms: policyengine_us.reforms.treasury.repeal_dependent_exemptions.repeal_dependent_exemptions + input: + gov.contrib.treasury.repeal_dependent_exemptions: true + people: + person_a: {age: 30, claimable_as_dependent_on_another_return: true, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_b: {age: 30, is_tax_unit_head: false, is_tax_unit_spouse: true} + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_a, person_b, child], filing_status: JOINT}} + households: {household: {members: [person_a, person_b, child], state_code: TX}} + output: + exemptions_count: 1 + +- name: Joint return with one claimable spouse keeps the other spouse's exemption (the claimable spouse is the spouse) + period: 2022 + reforms: policyengine_us.reforms.treasury.repeal_dependent_exemptions.repeal_dependent_exemptions + input: + gov.contrib.treasury.repeal_dependent_exemptions: true + people: + person_b: {age: 30, is_tax_unit_head: true, is_tax_unit_spouse: false} + person_a: {age: 30, claimable_as_dependent_on_another_return: true, is_tax_unit_head: false, is_tax_unit_spouse: true} + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [person_b, person_a, child], filing_status: JOINT}} + households: {household: {members: [person_b, person_a, child], state_code: TX}} + output: + exemptions_count: 1 diff --git a/policyengine_us/tests/policy/reform/cdcc_single_parent_work_requirement.yaml b/policyengine_us/tests/policy/reform/cdcc_single_parent_work_requirement.yaml index b19d62d4314..5de44b287cf 100644 --- a/policyengine_us/tests/policy/reform/cdcc_single_parent_work_requirement.yaml +++ b/policyengine_us/tests/policy/reform/cdcc_single_parent_work_requirement.yaml @@ -152,3 +152,60 @@ min_head_spouse_earned: 0 # Reform: min($8,000, $3,000, max($0, $60,000)) = min($8,000, $3,000, $60,000) = $3,000 cdcc_relevant_expenses: 3_000 + +# The reform changes the earnings test only. Childcare for the child of a filer +# who can be claimed as a dependent still does not count (IRC 21(b)(2)(A)(ii)). +- name: Reform keeps out childcare for the child of a filer who can be claimed + period: 2024 + reforms: policyengine_us.reforms.cdcc.cdcc_single_parent_work_requirement.cdcc_single_parent_work_requirement + input: + people: + adult1: + age: 35 + is_tax_unit_head: true + employment_income: 50_000 + claimable_as_dependent_on_another_return: true + child: + age: 5 + is_tax_unit_dependent: true + relative: + age: 40 + is_tax_unit_dependent: true + is_incapable_of_self_care: true + tax_units: + tax_unit: + members: [adult1, child, relative] + tax_unit_childcare_expenses: 3_000 + households: + household: + members: [adult1, child, relative] + state_code: TX + output: + cdcc_relevant_expenses: 0 + +- name: Reform counts the childcare when the filer cannot be claimed + period: 2024 + reforms: policyengine_us.reforms.cdcc.cdcc_single_parent_work_requirement.cdcc_single_parent_work_requirement + input: + people: + adult1: + age: 35 + is_tax_unit_head: true + employment_income: 50_000 + child: + age: 5 + is_tax_unit_dependent: true + relative: + age: 40 + is_tax_unit_dependent: true + is_incapable_of_self_care: true + tax_units: + tax_unit: + members: [adult1, child, relative] + tax_unit_childcare_expenses: 3_000 + households: + household: + members: [adult1, child, relative] + state_code: TX + output: + cdcc_relevant_expenses: 3_000 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py new file mode 100644 index 00000000000..2162ccd2f87 --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * + + +class cdcc_qualifying_childcare_expenses(Variable): + value_type = float + entity = TaxUnit + label = "Childcare expenses that count for the child and dependent care credit" + unit = USD + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/21#b_2_A", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", + ) + + def formula(tax_unit, period, parameters): + childcare = tax_unit("tax_unit_childcare_expenses", period) + # IRC 21(b)(2)(A)(ii) counts only expenses "for the care of a + # qualifying individual". A return on which a filer can be claimed as + # a dependent (outside the claimant filing exception) has no + # qualifying child under 13 unless the child is incapable of + # self-care, so its childcare expenses do not count. The model records + # childcare as a tax-unit total, so it cannot leave out one child's + # share when another child qualifies. + p = parameters(period).gov.irs.credits.cdcc.eligibility + person = tax_unit.members + qualifying_child = person("is_cdcc_eligible", period) & ( + person("age", period) < p.child_age + ) + dependent_filer = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + return where(dependent_filer & ~tax_unit.any(qualifying_child), 0, childcare) diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py index fba13665ad0..aa020f78a20 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_relevant_expenses.py @@ -18,22 +18,10 @@ def formula(tax_unit, period, parameters): # (tax_unit_childcare_expenses) plus care for a disabled qualifying # individual of any age — a disabled adult dependent or spouse # (care_expenses, a person-level yearly input). - childcare = tax_unit("tax_unit_childcare_expenses", period) - # IRC 21(b)(2)(A)(ii) counts only expenses "for the care of a - # qualifying individual". A return on which a filer can be claimed as - # a dependent has no qualifying child under 13 unless the child is - # incapable of self-care, so its childcare expenses do not count. - p = parameters(period).gov.irs.credits.cdcc.eligibility - person = tax_unit.members - qualifying_child = person("is_cdcc_eligible", period) & ( - person("age", period) < p.child_age - ) - dependent_filer = tax_unit( - "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period - ) - childcare = where( - dependent_filer & ~tax_unit.any(qualifying_child), 0, childcare - ) + # cdcc_qualifying_childcare_expenses leaves out childcare for a child + # who is not a qualifying individual because a filer can be claimed + # as a dependent. + childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) expenses = childcare + adult_care cdcc_limit = tax_unit("cdcc_limit", period) diff --git a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py index 91dd0cdffa3..28572bd8f05 100644 --- a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py @@ -27,6 +27,13 @@ def formula(person, period, parameters): filer = person("is_tax_unit_head_or_spouse", period) claimed = person("claimed_as_dependent_on_another_return", period) filer_claimed = tax_unit.any(filer & claimed) + # IRC 25A(f)(1)(A)(iii) counts the tuition of a dependent only if the + # taxpayer "is allowed a deduction under section 151" for them, and a + # return on which a filer can be claimed as a dependent (outside the + # claimant filing exception) has no dependents (IRC 152(b)(1)). + dependent_student_barred = person("is_tax_unit_dependent", period) & tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) requires_1098_t = aoc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_american_opportunity_credit_1098_t_or_exception", period) @@ -76,4 +83,5 @@ def formula(person, period, parameters): ) & filing_status_eligible & ~filer_claimed + & ~dependent_student_barred ) diff --git a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py index 0dba195cf08..c269b7f6f96 100644 --- a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py @@ -28,6 +28,13 @@ def formula(person, period, parameters): filer = person("is_tax_unit_head_or_spouse", period) claimed = person("claimed_as_dependent_on_another_return", period) filer_claimed = tax_unit.any(filer & claimed) + # IRC 25A(f)(1)(A)(iii) counts the tuition of a dependent only if the + # taxpayer "is allowed a deduction under section 151" for them, and a + # return on which a filer can be claimed as a dependent (outside the + # claimant filing exception) has no dependents (IRC 152(b)(1)). + dependent_student_barred = person("is_tax_unit_dependent", period) & tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) requires_1098_t = llc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( person("has_lifetime_learning_credit_1098_t_or_exception", period) @@ -55,4 +62,5 @@ def formula(person, period, parameters): ) & filing_status_eligible & ~filer_claimed + & ~dependent_student_barred ) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py index 63612661479..81b908901fd 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_adult_count_with_valid_ssn.py @@ -7,12 +7,12 @@ class rrc_adult_count_with_valid_ssn(Variable): definition_period = YEAR label = "Count of tax unit head/spouse with valid SSN for RRC" reference = ( - "https://www.law.cornell.edu/uscode/text/26/6428#g_1_A", - "https://www.law.cornell.edu/uscode/text/26/6428#g_1_B", + # Subsections (d)(2) and (g)(1)(A)-(B). + "https://www.law.cornell.edu/uscode/text/26/6428#g_1", + # Subsections (d)(2) and (g)(1)-(2). "https://www.law.cornell.edu/uscode/text/26/6428A#g_1", - "https://www.law.cornell.edu/uscode/text/26/6428A#g_2", - "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_A", - "https://www.law.cornell.edu/uscode/text/26/6428B#e_2_B", + # Subsections (c)(2) and (e)(2)(A)-(B). + "https://www.law.cornell.edu/uscode/text/26/6428B#e_2", ) def formula(tax_unit, period, parameters): diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py index 0ffd5e954b4..e5f35eb2829 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_arpa.py @@ -47,4 +47,8 @@ def formula(tax_unit, period, parameters): excess = max_(0, agi - rrc.arpa.phase_out.threshold[filing_status]) payment_reduction_percent = excess / phase_out_length payment_reduction = max_payment * payment_reduction_percent - return max_(0, max_payment - payment_reduction) + # IRC 6428B(a) allows the credit to "an eligible individual", so a + # return whose every filer is a dependent of another taxpayer gets + # none, even for its own dependents. + no_eligible_filer = tax_unit("every_filer_is_dependent_elsewhere", period) + return where(no_eligible_filer, 0, max_(0, max_payment - payment_reduction)) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py index b0ee925ced1..7bd14989385 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_caa.py @@ -42,7 +42,15 @@ def formula(tax_unit, period, parameters): children_with_ssn = tax_unit( "rrc_cares_qualifying_children_with_valid_ssn", period ) - count_children = where(adults_with_ssn > 0, children_with_ssn, 0) + # This prerequisite is a spouse with a valid SSN, whether or not that + # spouse is an eligible individual; the credit itself needs a filer + # who is not a dependent of another taxpayer. + filer_has_ssn = tax_unit.any( + person("is_tax_unit_head_or_spouse", period) + & person("meets_eitc_identification_requirements", period) + ) + no_eligible_filer = tax_unit("every_filer_is_dependent_elsewhere", period) + count_children = where(filer_has_ssn & ~no_eligible_filer, children_with_ssn, 0) max_payment = ( rrc.caa.max.adult * count_adults + rrc.caa.max.child * count_children ) diff --git a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py index c61fca0c9a7..20868403722 100644 --- a/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py +++ b/policyengine_us/variables/gov/irs/credits/recovery_rebate_credit/rrc_cares.py @@ -42,7 +42,15 @@ def formula(tax_unit, period, parameters): children_with_ssn = tax_unit( "rrc_cares_qualifying_children_with_valid_ssn", period ) - count_children = where(adults_with_ssn > 0, children_with_ssn, 0) + # This prerequisite is a spouse with a valid SSN, whether or not that + # spouse is an eligible individual; the credit itself needs a filer + # who is not a dependent of another taxpayer. + filer_has_ssn = tax_unit.any( + person("is_tax_unit_head_or_spouse", period) + & person("meets_eitc_identification_requirements", period) + ) + no_eligible_filer = tax_unit("every_filer_is_dependent_elsewhere", period) + count_children = where(filer_has_ssn & ~no_eligible_filer, children_with_ssn, 0) max_payment = ( rrc.cares.max.adult * count_adults + rrc.cares.max.child * count_children ) diff --git a/policyengine_us/variables/gov/states/ca/tax/income/credits/ca_cdcc_relevant_expenses.py b/policyengine_us/variables/gov/states/ca/tax/income/credits/ca_cdcc_relevant_expenses.py index 25fe3bf1736..06462eec8cc 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/credits/ca_cdcc_relevant_expenses.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/credits/ca_cdcc_relevant_expenses.py @@ -20,7 +20,7 @@ def formula(tax_unit, period, parameters): # Qualifying expenses cover childcare plus care for a disabled # qualifying individual of any age (FTB 3506 instructions, # Section D). - childcare = tax_unit("tax_unit_childcare_expenses", period) + childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) expenses = childcare + adult_care # First, cap based on the number of eligible care receivers diff --git a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py index dae5a093b99..2017b09d4b3 100644 --- a/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py +++ b/policyengine_us/variables/gov/states/ga/tax/income/credits/ctc/ga_ctc_eligible_child.py @@ -8,7 +8,7 @@ class ga_ctc_eligible_child(Variable): definition_period = YEAR defined_for = StateCode.GA reference = ( - "https://legiscan.com/GA/text/HB136/id/3204611/Georgia-2025-HB136-Enrolled.pdf#page=2", + "https://legiscan.com/GA/text/HB136/id/3204611/Georgia-2025-HB136-Enrolled.pdf#page=3", "https://www.law.cornell.edu/uscode/text/26/24#c", ) From 3d0864f778d9e87250e717be0c0882361749a5b3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 15:51:52 -0400 Subject: [PATCH 26/42] Address the third federal claimable-filer review - Education credits: is_barred_from_education_credits_by_dependency holds the claimed-filer and dependent-student rules; both eligibility formulas and the reconciliation SSN reform's Lifetime Learning Credit read it, so the reform no longer turns a barred student's tuition into that credit. - Child and dependent care: childcare counts when any child under 18 is still a qualifying individual, so an incapacitated child aged 13 to 17 keeps it. Co-Authored-By: Claude Opus 5.5 --- .../reconciled_ssn_for_llc_and_aoc.py | 9 +- .../credits/cdcc/cdcc_relevant_expenses.yaml | 30 ++++ .../reconciled_ssn_for_llc_and_aoc.yaml | 162 ++++++++++++++++++ .../cdcc_qualifying_childcare_expenses.py | 14 +- ...ligible_for_american_opportunity_credit.py | 23 +-- ...ed_from_education_credits_by_dependency.py | 34 ++++ ...s_eligible_for_lifetime_learning_credit.py | 23 +-- 7 files changed, 253 insertions(+), 42 deletions(-) create mode 100644 policyengine_us/variables/gov/irs/credits/education/is_barred_from_education_credits_by_dependency.py diff --git a/policyengine_us/reforms/reconciliation/reconciled_ssn_for_llc_and_aoc.py b/policyengine_us/reforms/reconciliation/reconciled_ssn_for_llc_and_aoc.py index 86740f80b31..b145dbd15f7 100644 --- a/policyengine_us/reforms/reconciliation/reconciled_ssn_for_llc_and_aoc.py +++ b/policyengine_us/reforms/reconciliation/reconciled_ssn_for_llc_and_aoc.py @@ -25,8 +25,15 @@ def formula(tax_unit, period, parameters): is_aoc_eligible = person( "is_eligible_for_american_opportunity_credit", period ) + # This reform leaves the dependency rules of IRC 25A(g)(3) and + # 25A(f)(1)(A)(iii) in place. + barred_by_dependency = person( + "is_barred_from_education_credits_by_dependency", period + ) eligible_expenses = tax_unit.sum( - person("qualified_tuition_expenses", period) * ~is_aoc_eligible + person("qualified_tuition_expenses", period) + * ~is_aoc_eligible + * ~barred_by_dependency ) capped_expenses = min_(llc.expense_limit, eligible_expenses) maximum_amount = llc.rate * capped_expenses diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml index 11084acd2b8..d772de304e6 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_relevant_expenses.yaml @@ -238,3 +238,33 @@ output: count_cdcc_eligible: 1 cdcc_relevant_expenses: 3000 + +# IRC 21(b)(1)(B): a dependent incapable of self-care is a qualifying individual at +# any age, determined without regard to IRC 152(b)(1), so the childcare of an +# incapacitated 14-year-old counts on a return with a claimable filer. +- name: 2025 childcare for the incapacitated teenager of a claimable filer counts + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 5000 + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + child: + age: 14 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, child] + tax_unit_childcare_expenses: 1000 + households: + household: + members: [parent, child] + state_code: TX + output: + count_cdcc_eligible: 1 + cdcc_relevant_expenses: 1000 diff --git a/policyengine_us/tests/policy/contrib/reconciliation/reconciled_ssn_for_llc_and_aoc.yaml b/policyengine_us/tests/policy/contrib/reconciliation/reconciled_ssn_for_llc_and_aoc.yaml index 8a61f5667d5..4740dfe0d8e 100644 --- a/policyengine_us/tests/policy/contrib/reconciliation/reconciled_ssn_for_llc_and_aoc.yaml +++ b/policyengine_us/tests/policy/contrib/reconciliation/reconciled_ssn_for_llc_and_aoc.yaml @@ -73,3 +73,165 @@ output: american_opportunity_credit: 1_000 lifetime_learning_credit: 0 + +# The reform keeps the dependency rules: a dependent student's tuition does not +# count on a return whose filer can be claimed as a dependent (IRC +# 25A(f)(1)(A)(iii), 152(b)(1)), under either credit. +- name: Dependent student of a filer who can be claimed gets neither credit + period: 2026 + reforms: policyengine_us.reforms.reconciliation.reconciled_ssn_for_llc_and_aoc.reconciled_ssn_for_llc_and_aoc + input: + gov.contrib.reconciliation.ssn_for_llc_and_aoc.in_effect: true + people: + parent: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + student: + age: 19 + ssn_card_type: CITIZEN + has_tin: true + qualified_tuition_expenses: 4_000 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, student] + filing_status: HEAD_OF_HOUSEHOLD + households: + household: + members: [parent, student] + state_code: TX + output: + american_opportunity_credit: 0 + lifetime_learning_credit: 0 + +- name: Dependent student of a joint return with one claimable spouse gets neither credit (the claimable spouse is the head) + period: 2026 + reforms: policyengine_us.reforms.reconciliation.reconciled_ssn_for_llc_and_aoc.reconciled_ssn_for_llc_and_aoc + input: + gov.contrib.reconciliation.ssn_for_llc_and_aoc.in_effect: true + people: + person_a: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + is_tax_unit_head: false + is_tax_unit_spouse: true + student: + age: 19 + ssn_card_type: CITIZEN + has_tin: true + qualified_tuition_expenses: 4_000 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_a, person_b, student] + filing_status: JOINT + households: + household: + members: [person_a, person_b, student] + state_code: TX + output: + american_opportunity_credit: 0 + lifetime_learning_credit: 0 + +- name: Dependent student of a joint return with one claimable spouse gets neither credit (the claimable spouse is the spouse) + period: 2026 + reforms: policyengine_us.reforms.reconciliation.reconciled_ssn_for_llc_and_aoc.reconciled_ssn_for_llc_and_aoc + input: + gov.contrib.reconciliation.ssn_for_llc_and_aoc.in_effect: true + people: + person_b: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + claimable_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + student: + age: 19 + ssn_card_type: CITIZEN + has_tin: true + qualified_tuition_expenses: 4_000 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person_b, person_a, student] + filing_status: JOINT + households: + household: + members: [person_b, person_a, student] + state_code: TX + output: + american_opportunity_credit: 0 + lifetime_learning_credit: 0 + +- name: Dependent student of a filer who cannot be claimed is eligible for the American Opportunity Credit, not the Lifetime Learning Credit + period: 2026 + reforms: policyengine_us.reforms.reconciliation.reconciled_ssn_for_llc_and_aoc.reconciled_ssn_for_llc_and_aoc + input: + gov.contrib.reconciliation.ssn_for_llc_and_aoc.in_effect: true + people: + parent: + age: 45 + ssn_card_type: CITIZEN + has_tin: true + is_tax_unit_head: true + is_tax_unit_spouse: false + student: + age: 19 + ssn_card_type: CITIZEN + has_tin: true + qualified_tuition_expenses: 4_000 + is_pursuing_credential_for_american_opportunity_credit: true + attends_eligible_educational_institution_for_american_opportunity_credit: true + is_enrolled_at_least_half_time_for_american_opportunity_credit: true + has_american_opportunity_credit_1098_t_or_exception: true + has_american_opportunity_credit_institution_ein: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [parent, student] + filing_status: HEAD_OF_HOUSEHOLD + households: + household: + members: [parent, student] + state_code: TX + output: + is_eligible_for_american_opportunity_credit: [false, true] + lifetime_learning_credit: 0 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py index 2162ccd2f87..f7885005bbf 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_qualifying_childcare_expenses.py @@ -17,14 +17,14 @@ def formula(tax_unit, period, parameters): # IRC 21(b)(2)(A)(ii) counts only expenses "for the care of a # qualifying individual". A return on which a filer can be claimed as # a dependent (outside the claimant filing exception) has no - # qualifying child under 13 unless the child is incapable of - # self-care, so its childcare expenses do not count. The model records - # childcare as a tax-unit total, so it cannot leave out one child's - # share when another child qualifies. - p = parameters(period).gov.irs.credits.cdcc.eligibility + # qualifying child unless the child is incapable of self-care, so its + # childcare expenses do not count otherwise. Childcare expenses are + # those of the tax unit's children under 18, recorded as a tax-unit + # total, so one child's share cannot be left out when another child + # qualifies. person = tax_unit.members - qualifying_child = person("is_cdcc_eligible", period) & ( - person("age", period) < p.child_age + qualifying_child = person("is_cdcc_eligible", period) & person( + "is_child", period ) dependent_filer = tax_unit( "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period diff --git a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py index 28572bd8f05..b3d5406e196 100644 --- a/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/american_opportunity_credit/is_eligible_for_american_opportunity_credit.py @@ -18,21 +18,11 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE - # IRC 25A(g)(3): no credit to an individual for whom a section 151 - # deduction "is allowed to another taxpayer", and the Form 1040 - # instructions extend this to "your spouse if filing jointly", so - # either filer being claimed bars the return's credit. This turns on - # an actual claim (26 CFR 1.25A-1(f)), so it reads the claimed input - # rather than a claimability helper. - filer = person("is_tax_unit_head_or_spouse", period) - claimed = person("claimed_as_dependent_on_another_return", period) - filer_claimed = tax_unit.any(filer & claimed) - # IRC 25A(f)(1)(A)(iii) counts the tuition of a dependent only if the - # taxpayer "is allowed a deduction under section 151" for them, and a - # return on which a filer can be claimed as a dependent (outside the - # claimant filing exception) has no dependents (IRC 152(b)(1)). - dependent_student_barred = person("is_tax_unit_dependent", period) & tax_unit( - "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + # The dependency rules of IRC 25A(g)(3) and 25A(f)(1)(A)(iii): a + # claimed filer bars the return, and a dependent student needs the + # return to have dependents. + barred_by_dependency = person( + "is_barred_from_education_credits_by_dependency", period ) requires_1098_t = aoc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( @@ -82,6 +72,5 @@ def formula(person, period, parameters): period, ) & filing_status_eligible - & ~filer_claimed - & ~dependent_student_barred + & ~barred_by_dependency ) diff --git a/policyengine_us/variables/gov/irs/credits/education/is_barred_from_education_credits_by_dependency.py b/policyengine_us/variables/gov/irs/credits/education/is_barred_from_education_credits_by_dependency.py new file mode 100644 index 00000000000..6a08d71c198 --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/education/is_barred_from_education_credits_by_dependency.py @@ -0,0 +1,34 @@ +from policyengine_us.model_api import * + + +class is_barred_from_education_credits_by_dependency(Variable): + value_type = bool + entity = Person + label = "Barred from the education credits by the dependency rules" + definition_period = YEAR + reference = ( + # Subsections (f)(1)(A)(iii) and (g)(3). + "https://www.law.cornell.edu/uscode/text/26/25A#g_3", + "https://www.law.cornell.edu/uscode/text/26/152#b_1", + "https://www.ecfr.gov/current/title-26/section-1.25A-1", + ) + + def formula(person, period, parameters): + tax_unit = person.tax_unit + # IRC 25A(g)(3): no credit to an individual for whom a section 151 + # deduction "is allowed to another taxpayer", and the Form 1040 + # instructions extend this to "your spouse if filing jointly", so + # either filer being claimed bars the return's credit. This turns on + # an actual claim (26 CFR 1.25A-1(f)), so it reads the claimed input + # rather than a claimability helper. + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimed_as_dependent_on_another_return", period) + filer_claimed = tax_unit.any(filer & claimed) + # IRC 25A(f)(1)(A)(iii) counts the tuition of a dependent only if the + # taxpayer "is allowed a deduction under section 151" for them, and a + # return on which a filer can be claimed as a dependent (outside the + # claimant filing exception) has no dependents (IRC 152(b)(1)). + dependent_student_barred = person("is_tax_unit_dependent", period) & tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) + return filer_claimed | dependent_student_barred diff --git a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py index c269b7f6f96..1a7bbf0a847 100644 --- a/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py +++ b/policyengine_us/variables/gov/irs/credits/education/is_eligible_for_lifetime_learning_credit.py @@ -19,21 +19,11 @@ def formula(person, period, parameters): filing_status = tax_unit("filing_status", period) filing_status_values = filing_status.possible_values filing_status_eligible = filing_status != filing_status_values.SEPARATE - # IRC 25A(g)(3): no credit to an individual for whom a section 151 - # deduction "is allowed to another taxpayer", and the Form 1040 - # instructions extend this to "your spouse if filing jointly", so - # either filer being claimed bars the return's credit. This turns on - # an actual claim (26 CFR 1.25A-1(f)), so it reads the claimed input - # rather than a claimability helper. - filer = person("is_tax_unit_head_or_spouse", period) - claimed = person("claimed_as_dependent_on_another_return", period) - filer_claimed = tax_unit.any(filer & claimed) - # IRC 25A(f)(1)(A)(iii) counts the tuition of a dependent only if the - # taxpayer "is allowed a deduction under section 151" for them, and a - # return on which a filer can be claimed as a dependent (outside the - # claimant filing exception) has no dependents (IRC 152(b)(1)). - dependent_student_barred = person("is_tax_unit_dependent", period) & tax_unit( - "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + # The dependency rules of IRC 25A(g)(3) and 25A(f)(1)(A)(iii): a + # claimed filer bars the return, and a dependent student needs the + # return to have dependents. + barred_by_dependency = person( + "is_barred_from_education_credits_by_dependency", period ) requires_1098_t = llc.eligibility.requires_1098_t_or_exception has_1098_t_or_exception = ( @@ -61,6 +51,5 @@ def formula(person, period, parameters): period, ) & filing_status_eligible - & ~filer_claimed - & ~dependent_student_barred + & ~barred_by_dependency ) From 35a2cbcbb971f6b43d35db009d9bda41b76386a5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 10 Oct 2026 16:02:49 -0400 Subject: [PATCH 27/42] Give the spouse without an SSN a card type in the recovery rebate test Setting meets_eitc_identification_requirements on one person made it an input for every person, so the other spouse lost their SSN too. Co-Authored-By: Claude Opus 5.5 --- .../recovery_rebate_credit/recovery_rebate_credit.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml index a1e77b517ef..01ed4209665 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/recovery_rebate_credit/recovery_rebate_credit.yaml @@ -375,7 +375,7 @@ is_tax_unit_spouse: false person_b: age: 30 - meets_eitc_identification_requirements: false + ssn_card_type: NONE is_tax_unit_head: false is_tax_unit_spouse: true child: @@ -401,7 +401,7 @@ people: person_b: age: 30 - meets_eitc_identification_requirements: false + ssn_card_type: NONE is_tax_unit_head: true is_tax_unit_spouse: false person_a: From bb6c5472a1ae06d856d1b935d7c49b0043ed40fa Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 12:19:33 -0400 Subject: [PATCH 28/42] Align the six states' gates with the settled claimed/claimable inputs #10024 now separates claimability (claimable_as_dependent_on_another_return, read by head_or_spouse_is_dependent_elsewhere) from an actual claim (claimed_as_dependent_on_another_return), and the federal branch applies the Dependent Taxpayer Test (IRC 152(b)(1)) through head_or_spouse_is_dependent_elsewhere_without_filing_exception. So: - Dependent counts (RI rebate, SC exemption and under-six deduction, UT dependents, birth-year dependent, at-home parent child and contributed reform, VT dependent exemptions and CTC, WI homestead $500 deduction) use the Dependent Taxpayer Test helper, keeping dependents when the would-be claimant meets the filing exception. - Wisconsin's $250 exemption reads claimability: a filer who can be claimed is not "allowed the $700 exemption" even when no one claims them. - Vermont's renter credit bars a claimant actually claimed (6066(c)(2)). - Wisconsin's care subtraction and credit read cdcc_qualifying_childcare_expenses, so a claimable filer's childcare for a nonqualifying child drops out. Co-Authored-By: Claude Opus 5.5 --- .../ut_dependent_exemption_reform.py | 7 ++++--- .../tax/income/credits/rebates/ri_child_tax_rebate.py | 7 +++++-- .../tax/income/deductions/sc_young_child_deduction.py | 5 +++-- .../sc/tax/income/exemptions/sc_dependent_exemption.py | 7 +++++-- ...personal_exemption_additional_dependent_eligible.py | 5 +++-- .../credits/taxpayer_credit/ut_total_dependents.py | 7 +++++-- .../credits/ut_at_home_parent_credit_potential.py | 7 +++++-- .../gov/states/vt/tax/income/credits/ctc/vt_ctc.py | 7 +++++-- .../vt/tax/income/credits/renter/vt_renter_credit.py | 10 +++++++--- .../vt/tax/income/exemptions/vt_personal_exemptions.py | 7 +++++-- .../wi_childcare_expense_credit_potential.py | 5 ++++- .../income/credits/homestead/wi_homestead_income.py | 7 +++++-- .../tax/income/exemptions/wi_additional_exemption.py | 8 ++++---- .../subtractions/wi_childcare_expense_subtraction.py | 5 ++++- 14 files changed, 64 insertions(+), 30 deletions(-) diff --git a/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py b/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py index 7bb66db5fce..da642d21052 100644 --- a/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/ut/dependent_exemption/ut_dependent_exemption_reform.py @@ -34,10 +34,11 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) # A return on which the filer (or, if joint, either spouse) can be - # claimed as a dependent has no dependents (IRC 152(b)(1)), as in - # the baseline ut_total_dependents. + # claimed as a dependent has no dependents (IRC 152(b)(1)) outside + # the claimant filing exception, as in the baseline + # ut_total_dependents. dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) # Dependents under the age threshold (all dependents when the # age limit is off) take the contrib amount; older dependents diff --git a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py index 1fe1b39f701..75b23bceb44 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py @@ -21,8 +21,11 @@ def formula(tax_unit, period, parameters): # R.I. Gen. Laws 44-30-103(b)(1) pays the rebate for each child "whom # the eligible taxpayer validly claims as a dependent" on the 2021 # return. Under IRC 152(b)(1) a return on which the filer (or, if - # joint, either spouse) can be claimed as a dependent has none. - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # joint, either spouse) can be claimed as a dependent has none, unless + # the would-be claimant meets the filing exception (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) eligible_child = age_eligibility & dependent & ~filer_is_dependent child_count = tax_unit.sum(eligible_child) capped_children = min_(child_count, p.limit.child) diff --git a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py index 9844e435023..c1dff53f03b 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/deductions/sc_young_child_deduction.py @@ -23,9 +23,10 @@ def formula(tax_unit, period, parameters): # Section 12-6-1160 requires a "dependent eligible for the South # Carolina dependent exemption", which a return on which the filer (or, # if joint, either spouse) can be claimed as a dependent has none of - # (IRC 152(b)(1), incorporated by Section 12-6-1140(13)). + # (IRC 152(b)(1), incorporated by Section 12-6-1140(13)), unless the + # would-be claimant meets the filing exception (Publication 501). filer_is_dependent = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) eligible_dependent = ( meets_age_limit diff --git a/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py b/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py index a9fe09799bd..1a4a75cab4e 100644 --- a/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py +++ b/policyengine_us/variables/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.py @@ -20,8 +20,11 @@ def formula(tax_unit, period, parameters): # Section 12-6-1140(13): "each dependent must meet the eligibility # requirements of Section 151 and 152", and under IRC 152(b)(1) a # return on which the filer (or, if joint, either spouse) can be - # claimed as a dependent has no dependents. - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # claimed as a dependent has no dependents, unless the would-be + # claimant meets the filing exception (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) dependents = where( filer_is_dependent, 0, tax_unit("tax_unit_dependents", period) ) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py index a364e9adfa2..18d7646016b 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_personal_exemption_additional_dependent_eligible.py @@ -15,8 +15,9 @@ def formula(person, period, parameters): born_this_year = birth_year == period.start.year # The additional dependent must also be a dependent of the return, and # a return on which the filer (or, if joint, either spouse) can be - # claimed as a dependent has none (IRC 152(b)(1)). + # claimed as a dependent has none (IRC 152(b)(1)), unless the would-be + # claimant meets the filing exception (Publication 501). filer_is_dependent = person.tax_unit( - "head_or_spouse_is_dependent_elsewhere", period + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) return is_dependent & born_this_year & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py index 25eaa0048ed..8949b0840ca 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/taxpayer_credit/ut_total_dependents.py @@ -16,7 +16,10 @@ def formula(tax_unit, period, parameters): # "with respect to whom the claimant is allowed to claim a tax credit # under Section 24". Under IRC 152(b)(1) a return on which the filer # (or, if joint, either spouse) can be claimed as a dependent has no - # dependents, and so no such credit. - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # dependents, and so no such credit, unless the would-be claimant + # meets the filing exception (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) dependents = tax_unit("tax_unit_count_dependents", period) return where(filer_is_dependent, 0, dependents) diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py index 6568fdba57f..d48aded74e2 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py @@ -22,8 +22,11 @@ def formula(tax_unit, period, parameters): # Utah Code 59-10-1005 requires that the parent claim the qualifying # child as a dependent. Under IRC 152(b)(1) a return on which the filer # (or, if joint, either spouse) can be claimed as a dependent has no - # dependents. - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # dependents, unless the would-be claimant meets the filing exception + # (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) qualifying_child = (age < p.max_child_age) & is_dependent & ~filer_is_dependent count_qualifying_children = tax_unit.sum(qualifying_child) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index 887bc55968c..b4f04e555d9 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -17,12 +17,15 @@ def formula(tax_unit, period, parameters): # credit for each qualifying child under IRC 152(c) at or under the # age limit. The identification waiver does not reach IRC 152(b)(1): # a return on which the filer (or, if joint, either spouse) can be - # claimed as a dependent has no dependents and no federal credit. + # claimed as a dependent has no dependents and no federal credit, + # unless the would-be claimant meets the filing exception. person = tax_unit.members age = person("age", period) p = parameters(period).gov.states.vt.tax.income.credits.ctc dependent = person("is_tax_unit_dependent", period) - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) eligible = (age <= p.age_limit) & dependent & ~filer_is_dependent count_eligible = tax_unit.sum(eligible) # Get adjusted gross income. diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py index 9d6231f20e1..ec949227d2e 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py @@ -78,7 +78,11 @@ def formula(tax_unit, period, parameters): capped_credit = min_(credit_value, p.max_credit) unrounded = capped_credit * (1 - shared_residence_reduction) # 6066(c)(2): the claimant "may not be a person claimed as a dependent - # by any taxpayer". On a joint return either spouse may be the - # claimant, so only a return on which every filer is claimed loses it. - claimant_eligible = ~tax_unit("every_filer_is_dependent_elsewhere", period) + # by any taxpayer", an actual claim. On a joint return either spouse + # may be the claimant, so only a return on which every filer is + # claimed loses it. + person = tax_unit.members + filer = person("is_tax_unit_head_or_spouse", period) + claimed = person("claimed_as_dependent_on_another_return", period) + claimant_eligible = tax_unit.any(filer & ~claimed) return claimant_eligible * np.round(unrounded) diff --git a/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py b/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py index 7c1055b60df..484a862d1a5 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.py @@ -22,8 +22,11 @@ def formula(tax_unit, period, parameters): # 32 V.S.A. 5811(21)(C): "no exemption may be claimed" for a dependent # individual, and under IRC 152(b)(1) a return on which the filer (or, # if joint, either spouse) can be claimed as a dependent has no - # dependents. - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # dependents, unless the would-be claimant meets the filing exception + # (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) dependents = where( filer_is_dependent, 0, tax_unit("tax_unit_count_dependents", period) ) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py index 3df0c6e0477..749e29a9271 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py @@ -31,7 +31,10 @@ def formula(tax_unit, period, parameters): # limits, so the §21 employment-related expense definition # applies: childcare plus care for a disabled adult qualifying # individual (care_expenses). - childcare = tax_unit("tax_unit_childcare_expenses", period) + # Only childcare for a qualifying individual counts (IRC + # 21(b)(2)(A)(ii)): a return whose filer can be claimed has no + # qualifying child under 13 (152(b)(1)), so its childcare drops out. + childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) raw_expenses = childcare + adult_care # Cap to WI limit, then also cap to min head/spouse earned (§21(d)) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py index 6fec858fbc0..01e5cc637f9 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py @@ -20,8 +20,11 @@ def formula(tax_unit, period, parameters): disqualified_losses = tax_unit("wi_homestead_disqualified_losses", period) # The $500 deduction is for each dependent under IRC 152; a return on # which the claimant (or, if joint, either spouse) can be claimed as a - # dependent has none (IRC 152(b)(1)). - filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) + # dependent has none (IRC 152(b)(1)), unless the would-be claimant + # meets the filing exception (Publication 501). + filer_is_dependent = tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) dependents = where( filer_is_dependent, 0, tax_unit("tax_unit_dependents", period) ) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py b/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py index a8a3a4218f3..312251d4477 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/exemptions/wi_additional_exemption.py @@ -28,8 +28,8 @@ def formula(tax_unit, period, parameters): # Form 1 instructions, line 10b: the $250 exemption is for "you and/or # your spouse only if you and/or your spouse are 65 years of age or # older and are allowed the $700 exemption on line 10a", which a - # filer who can be claimed as a dependent is not. Each spouse is - # tested separately. - claimed = person("claimed_as_dependent_on_another_return", period) - eligible_filers = tax_unit.sum(filer & elderly & ~claimed) + # filer who can be claimed as a dependent is not, whether or not + # anyone claims that filer. Each spouse is tested separately. + claimable = person("claimable_as_dependent_on_another_return", period) + eligible_filers = tax_unit.sum(filer & elderly & ~claimable) return eligible_filers * p.exemption.extra diff --git a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py index 739f18d564c..f6b9b9e0d6d 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py @@ -19,7 +19,10 @@ def formula(tax_unit, period, parameters): # §71.05(6)(b)43. adopts the IRC §21 employment-related expense # definition, which covers childcare plus care for a disabled adult # qualifying individual (care_expenses). - childcare = tax_unit("tax_unit_childcare_expenses", period) + # Only childcare for a qualifying individual counts (IRC + # 21(b)(2)(A)(ii)): a return whose filer can be claimed has no + # qualifying child under 13 (152(b)(1)), so its childcare drops out. + childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) uncapped_expenses = childcare + adult_care eligible_dependents = tax_unit("count_cdcc_eligible", period) From 5709bd9c320d8fe2ed02b48a8c196f0f8dda65e1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 12:25:28 -0400 Subject: [PATCH 29/42] Move the four ungated fixes to #10104 and test the claimed/claimable split The Vermont student loan subtraction, the Vermont child tax credit per-child phase-out, Utah's at-home parent credit once per return and Wisconsin's homestead claimant tests change results for households where nobody is claimed, so they now go to main in PolicyEngine/policyengine-us#10104; this branch keeps the base formulas there. New cases: a filer who can be claimed but is not claimed (no Wisconsin $250 exemption; still a Rhode Island relief and Vermont renter credit claimant), and a would-be claimant who meets the filing exception (South Carolina, Utah, Vermont and West Virginia dependents survive). The property test now draws claimability, actual claims and the filing exception per spouse. Co-Authored-By: Claude Opus 5.5 --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 +- .../student_loan_interest/income_limit.yaml | 26 -- .../vt/tax/income/agi/subtractions.yaml | 12 +- ...able_filer_ri_sc_ut_vt_wv_wi_invariants.py | 61 ++- .../ri_property_tax_credit_eligible.yaml | 22 + .../exemptions/sc_dependent_exemption.yaml | 26 ++ .../credits/ut_at_home_parent_credit.yaml | 6 +- .../ut_at_home_parent_credit_potential.yaml | 27 -- .../income/credits/ut_total_dependents.yaml | 25 ++ .../vt_student_loan_interest_subtraction.yaml | 258 ----------- .../income/credits/rent/vt_renter_credit.yaml | 25 ++ .../states/vt/tax/income/credits/vt_ctc.yaml | 23 - .../exemptions/vt_personal_exemptions.yaml | 24 ++ .../tax/income/wi_additional_exemption.yaml | 71 ++++ .../wi/tax/income/wi_homestead_eligible.yaml | 401 +++--------------- .../exemptions/wv_personal_exemption.yaml | 23 + .../ut_at_home_parent_credit_potential.py | 19 +- .../vt_student_loan_interest_subtraction.py | 38 -- .../vt/tax/income/credits/ctc/vt_ctc.py | 11 +- .../homestead/wi_homestead_eligible.py | 45 +- 20 files changed, 347 insertions(+), 798 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml delete mode 100644 policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index 7d77d06cd1a..74f6c4c44c5 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1 +1 @@ -Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent (dependent exemptions and deductions, child credits, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, and the RI, VT and WI property tax and renter claimant tests). Also subtract only the Vermont student loan interest not already deducted federally, from 2022; phase out Vermont's child tax credit per child; and pay Utah's at-home parent credit once per qualifying child per return. +Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent: dependent exemptions, deductions and child credits on such a return, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, the Rhode Island and Vermont claimant tests for property tax relief and the renter credit, and Wisconsin's care subtraction and credit expenses. diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml deleted file mode 100644 index 5798498b482..00000000000 --- a/policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml +++ /dev/null @@ -1,26 +0,0 @@ -description: Vermont allows the student loan interest subtraction only to filers with federal adjusted gross income at or below this amount, by filing status. -metadata: - propagate_metadata_to_children: true - unit: currency-USD - period: year - label: Vermont student loan interest subtraction income limit - breakdown: - - filing_status - reference: - - title: 32 V.S.A. 5811(29)(B) - href: https://legislature.vermont.gov/statutes/section/32/151/05811 - - title: 2022 Act 138, Sec. 4 (effective January 1, 2022) - href: https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=4 - - title: 2025 Vermont income tax booklet, Schedule IN-112 line 16c - href: https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=33 -SINGLE: - 2022-01-01: 120_000 -HEAD_OF_HOUSEHOLD: - 2022-01-01: 120_000 -SEPARATE: - 2022-01-01: 120_000 -SURVIVING_SPOUSE: - # IN-112 line 16c: "All other filers, if AGI is greater than $120,000, enter -0-." - 2022-01-01: 120_000 -JOINT: - 2022-01-01: 200_000 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml index 9005a8af60c..720da356559 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml @@ -3,15 +3,7 @@ values: 2021-01-01: - us_govt_interest - vt_medical_expense_deduction - - vt_capital_gains_exclusion - - vt_retirement_income_exemption - - salt_refund_income - # 2022 Act 138 added the student loan interest subtraction from 2022 - # (32 V.S.A. 5811(21)(B)(vi)); the 2021 Schedule IN-112 has no such line. - 2022-01-01: - - us_govt_interest - - vt_medical_expense_deduction - - vt_student_loan_interest_subtraction + - student_loan_interest - vt_capital_gains_exclusion - vt_retirement_income_exemption - salt_refund_income @@ -19,8 +11,6 @@ metadata: unit: list label: Vermont subtractions reference: - - title: 2025 SCHEDULE IN-112 # Line 16a-16c, student loan interest - href: https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=23 - title: 2024 SCHEDULE IN-112 # PART 1 SUBTRACTIONS FROM FEDERAL ADJUSTED GROSS INCOME href: https://tax.vermont.gov/sites/tax/files/documents/IN-112-2024.pdf#page=1 - title: 2023 SCHEDULE IN-112 # PART 1 SUBTRACTIONS FROM FEDERAL ADJUSTED GROSS INCOME diff --git a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py index d76bd0dd0ca..b8dc2d6d410 100644 --- a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py @@ -2,24 +2,24 @@ rules for a filer who can be claimed as a dependent. A return on which the filer (or, if joint, either spouse) can be claimed has -no dependents (IRC 152(b)(1)), so the dependent-based amounts below are zero -on it. Claimant rules (RI property tax relief, VT renter credit, WI homestead -credit) and per-filer exemptions (WV, WI $250) test each spouse separately. +no dependents (IRC 152(b)(1)) unless the would-be claimant meets the filing +exception, so the dependent-based amounts below are zero on it. Claimant +rules that turn on an actual claim (RI property tax relief, VT renter credit) +and per-filer exemptions (WV, WI $250) test each spouse separately. -For couples drawn by Hypothesis and a seeded population in these states, -with either, both or neither spouse claimed: +For couples drawn by Hypothesis (claimable, actually claimed and filing +exception drawn per spouse) and a seeded population in these states: 1. Swap invariance: each output is the same under either head/spouse labelling. 2. Monotonicity: marking another filer as claimed never raises the amounts - in MONOTONE. The Vermont student loan subtraction, Wisconsin homestead - income and the West Virginia family credit are left out by design: the - first replaces the federal deduction a claimed filer loses, and the other - two rise when a dependent deduction or exemption goes away. -3. Identities: the dependent-based amounts are zero on a return with a - claimable filer, and a return whose every filer is claimed gets no RI - property tax relief or VT renter credit and the single $500 West Virginia - exemption. + in MONOTONE. Wisconsin homestead income and the West Virginia family + credit are left out by design: they rise when a dependent deduction or + exemption goes away. +3. Identities: the dependent-based amounts are zero on a return where the + Dependent Taxpayer Test applies; a return whose every filer is actually + claimed gets no RI relief or VT renter credit; and a West Virginia return + with no exemptions gets the single $500. """ import numpy as np @@ -62,9 +62,10 @@ "wi_standard_deduction", "wi_additional_exemption", "wi_homestead_credit", + "wi_childcare_expense_subtraction", + "wi_childcare_expense_credit_potential", ] OUTPUTS = MONOTONE + [ - "vt_student_loan_interest_subtraction", "wv_low_income_family_tax_credit", "wi_homestead_income", "wi_retirement_income_exclusion_tax", @@ -74,30 +75,46 @@ @st.composite def couples(draw): + claimable = draw(st.sampled_from(CLAIM_PATTERNS)) + claimed = tuple(can and draw(st.booleans()) for can in claimable) return { "state": draw(st.sampled_from(STATES)), "adults": [draw(adults()), draw(adults())], "dependents": draw(st.lists(dependents(), max_size=2)), - "claimed": draw(st.sampled_from(CLAIM_PATTERNS)), + "claimed": claimed, + "claimable": claimable, + "exception": tuple(not c and draw(st.booleans()) for c in claimed), } def _identities(sim, year): - either = _calc(sim, "head_or_spouse_is_dependent_elsewhere", year) > 0 - every = _calc(sim, "every_filer_is_dependent_elsewhere", year) > 0 + no_dependents = ( + _calc( + sim, "head_or_spouse_is_dependent_elsewhere_without_filing_exception", year + ) + > 0 + ) for name in DEPENDENT_BASED: values = _calc(sim, name, year) - assert not values[either].any(), ( - f"{name} in {year} is positive on a return with a claimable filer" + assert not values[no_dependents].any(), ( + f"{name} in {year} is positive on a return where the Dependent " + "Taxpayer Test applies" ) + person_claimed = np.asarray( + sim.calculate("claimed_as_dependent_on_another_return", year, map_to="tax_unit") + ) + filers = np.asarray( + sim.calculate("is_tax_unit_head_or_spouse", year, map_to="tax_unit") + ) + every_filer_claimed = (person_claimed >= filers) & (filers > 0) for name in ["ri_property_tax_credit", "vt_renter_credit"]: - assert not _calc(sim, name, year)[every].any(), ( + assert not _calc(sim, name, year)[every_filer_claimed].any(), ( f"{name} in {year} is positive when every filer is claimed" ) state = np.asarray(sim.calculate("state_code_str", year, map_to="tax_unit")) - wv_every = every & (state == "WV") + wv_none = (state == "WV") & (_calc(sim, "exemptions_count", year) == 0) np.testing.assert_array_equal( - _calc(sim, "wv_personal_exemption", year)[wv_every], 500 + _calc(sim, "wv_personal_exemption", year)[wv_none], 500 ) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml index 940a617cba9..d2ea7dffe07 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.yaml @@ -355,3 +355,25 @@ output: ri_property_tax_credit_eligible: true ri_property_tax_credit: 700 + +- name: 2025 single filer aged 65 who can be claimed but is not claimed is a Rhode Island property tax relief claimant + period: 2025 + input: + people: + filer: + age: 65 + real_estate_taxes: 1000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: RI + output: + # 44-33-3 excludes a person "claimed as a dependent by any taxpayer": + # an actual claim. min($1,000 - $0, $700) = $700. + ri_property_tax_credit_eligible: true + ri_property_tax_credit: 700 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml index 12d16796878..5c8be843c2e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/tax/income/exemptions/sc_dependent_exemption.yaml @@ -200,3 +200,29 @@ output: # One dependent x $4,930 (2025). sc_dependent_exemption: 4930 + +- name: 2025 single filer who can be claimed but whose would-be claimant meets the filing exception keeps the South Carolina dependent exemption + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: SC + output: + # Publication 501, Dependent Taxpayer Test: the bar does not apply when + # the would-be claimant is not required to file and files only for a + # refund. One child aged 4: $4,930 each. + sc_dependent_exemption: 4930 + sc_young_child_deduction: 4930 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml index e1cb5eeedd3..b582eaadba7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit.yaml @@ -55,7 +55,7 @@ state_code: UT output: ut_at_home_parent_credit: 0 -- name: 1 eligible parent with 2 children +- name: 2 eligible parents with 1 child period: 2022 input: people: @@ -123,6 +123,4 @@ - person4 state_code: UT output: - # Utah Code 59-10-1005(2): $100 for each qualifying child when the claimant - # or a joint-filing spouse is an at-home parent, once per return. - ut_at_home_parent_credit: 200 + ut_at_home_parent_credit: 400 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml index 42d4838f042..8b4660f1127 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -134,30 +134,3 @@ # One child under 1 x $100 x one parent earning $3,000 or less # (the other parent earns $4,000); AGI $6,000 is under $50,000. ut_at_home_parent_credit_potential: 100 - -- name: "2025 two at-home parents with one newborn: one Utah at-home parent credit per child on the return" - period: 2025 - input: - people: - head: - age: 30 - employment_income: 2000 - spouse: - age: 30 - employment_income: 1000 - is_tax_unit_spouse: true - newborn: - age: 0 - tax_units: - tax_unit: - members: [head, spouse, newborn] - filing_status: JOINT - households: - household: - members: [head, spouse, newborn] - state_code: UT - output: - # Utah Code 59-10-1005(2): $100 for each qualifying child if the claimant - # or a joint-filing spouse is an at-home parent; both spouses earning - # $3,000 or less does not double it. - ut_at_home_parent_credit_potential: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml index 928dcca4bdd..c4366419ade 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_total_dependents.yaml @@ -139,3 +139,28 @@ # One dependent x $2,111 (2025). ut_total_dependents: 1 ut_personal_exemption: 2111 + +- name: 2025 single filer whose would-be claimant meets the filing exception keeps Utah dependents + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 2 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: UT + output: + # The federal credit for the child is allowed (Dependent Taxpayer Test + # exception), so the child is a qualifying dependent: 1 x $2,111. + ut_total_dependents: 1 + ut_personal_exemption: 2111 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml deleted file mode 100644 index 4557dea7b9a..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml +++ /dev/null @@ -1,258 +0,0 @@ -- name: "2025 single filer who can be claimed as a dependent: Vermont subtracts the student loan interest not deducted federally" - period: 2025 - input: - people: - filer: - age: 24 - employment_income: 10000 - student_loan_interest: 1000 - claimed_as_dependent_on_another_return: true - tax_units: - tax_unit: - members: [filer] - households: - household: - members: [filer] - state_code: VT - output: - # A claimed filer gets no federal deduction (IRC 221(c)), so Schedule - # IN-112 line 16 subtracts all $1,000 of interest: $10,000 - $1,000. - student_loan_interest_ald: 0 - vt_student_loan_interest_subtraction: 1000 - vt_agi: 9000 - -- name: "2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the head)" - period: 2025 - input: - people: - person_a: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: VT - output: - # Neither spouse may be claimed for the federal deduction; Vermont - # subtracts the full $1,000. - student_loan_interest_ald: [0, 0] - vt_student_loan_interest_subtraction: 1000 - vt_agi: 9000 - -- name: "2025 joint return with one claimable spouse: Vermont subtracts the student loan interest not deducted federally (the claimable spouse is the spouse)" - period: 2025 - input: - people: - person_b: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - is_tax_unit_head: true - is_tax_unit_spouse: false - person_a: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_b, person_a] - filing_status: JOINT - households: - household: - members: [person_b, person_a] - state_code: VT - output: - student_loan_interest_ald: [0, 0] - vt_student_loan_interest_subtraction: 1000 - vt_agi: 9000 - -- name: "2025 joint return with both spouses claimable: Vermont subtracts the student loan interest not deducted federally" - period: 2025 - input: - people: - person_a: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: VT - output: - student_loan_interest_ald: [0, 0] - vt_student_loan_interest_subtraction: 1000 - vt_agi: 9000 - -- name: "2025 joint return with neither spouse claimable: Vermont subtracts the student loan interest not deducted federally" - period: 2025 - input: - people: - person_a: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 24 - employment_income: 5000 - student_loan_interest: 500 - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: VT - output: - # The federal deduction takes the $1,000 (line 16b), so line 16c is 0; - # federal AGI is already $9,000. Subtracting the raw interest again - # would deduct it twice. - student_loan_interest_ald: [500, 500] - vt_student_loan_interest_subtraction: 0 - vt_agi: 9000 - -- name: "2025 interest above the $2,500 federal cap: Vermont subtracts the excess" - period: 2025 - input: - people: - filer: - age: 30 - employment_income: 50000 - student_loan_interest: 4000 - tax_units: - tax_unit: - members: [filer] - households: - household: - members: [filer] - state_code: VT - output: - # IN-112 line 16c: $4,000 paid - $2,500 deducted federally. - student_loan_interest_ald: 2500 - vt_student_loan_interest_subtraction: 1500 - -- name: 2025 single filer with adjusted gross income above $120,000 gets no Vermont student loan interest subtraction - period: 2025 - input: - people: - filer: - age: 30 - employment_income: 125000 - student_loan_interest: 3000 - tax_units: - tax_unit: - members: [filer] - households: - household: - members: [filer] - state_code: VT - output: - # 5811(29)(B)(i): AGI must be at most $120,000 unless joint. - vt_student_loan_interest_subtraction: 0 - -- name: 2025 joint return with adjusted gross income under $200,000 subtracts the interest left after the federal phase-out - period: 2025 - input: - people: - head: - age: 30 - employment_income: 195000 - student_loan_interest: 3000 - spouse: - age: 30 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - households: - household: - members: [head, spouse] - state_code: VT - output: - # Federal: $2,500 x (1 - ($195,000 - $170,000) / $30,000) = $416.67. - # AGI $194,583 is within Vermont's $200,000 joint limit: - # $3,000 - $416.67 = $2,583.33. - student_loan_interest_ald: [416.6667, 0] - vt_student_loan_interest_subtraction: 2583.3333 - -- name: 2025 joint return with adjusted gross income over $200,000 gets no Vermont student loan interest subtraction - period: 2025 - input: - people: - head: - age: 30 - employment_income: 205000 - student_loan_interest: 3000 - spouse: - age: 30 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - households: - household: - members: [head, spouse] - state_code: VT - output: - # 5811(29)(B)(ii): AGI must be at most $200,000 if joint. - student_loan_interest_ald: [0, 0] - vt_student_loan_interest_subtraction: 0 - -- name: 2021 Vermont has no student loan interest subtraction - period: 2021 - input: - people: - filer: - age: 24 - employment_income: 10000 - student_loan_interest: 1000 - claimed_as_dependent_on_another_return: true - tax_units: - tax_unit: - members: [filer] - households: - household: - members: [filer] - state_code: VT - output: - # 2022 Act 138 added 5811(21)(B)(vi) from 2022; the 2021 Schedule - # IN-112 has no such line. - vt_subtractions: 0 - vt_agi: 10000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml index a5df0d28a46..4c1e16314ad 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml @@ -287,3 +287,28 @@ output: # Two exemptions: round($1,936 x 12 x 10%) = $2,323. vt_renter_credit: 2323 + +- name: 2025 single renter who can be claimed but is not claimed keeps the Vermont renter credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 1000 + rent: 12000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + # 6066(c)(2) bars a claimant "claimed as a dependent", an actual claim. + # A filer who can be claimed has no personal exemption; the fair market + # rent table starts at one exemption, so the model uses it: + # round($1,476 x 12 x 10%) = $1,771. + vt_renter_credit: 1771 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index 2d0f332495b..d741ea0fb98 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -84,29 +84,6 @@ output: vt_ctc: 500 -- name: "Two children halfway phased out at $150,000: each child's $1,000 is reduced by $500" - period: 2023 - input: - people: - parent: - age: 35 - child1: - age: 3 - child2: - age: 3 - tax_units: - tax_unit: - members: [parent, child1, child2] - adjusted_gross_income: 150_000 - households: - household: - members: [parent, child1, child2] - state_code: VT - output: - # 32 V.S.A. 5830f(b): the credit per child is reduced by $20 per $1,000 - # over $125,000: 2 x ($1,000 - 25 x $20) = $1,000. - vt_ctc: 1_000 - # S.51 (2025) tests for expanded age eligibility - name: S.51 - CTC includes 6-year-olds starting 2025 period: 2025 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml index b54ae76fac2..da3557099bb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml @@ -288,3 +288,27 @@ output: # 3 exemptions x $5,300 (2025). vt_personal_exemptions: 15900 + +- name: 2025 single filer who can be claimed but whose would-be claimant meets the filing exception gets Vermont dependent exemptions only + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 8000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: VT + output: + # No own exemption (the filer can be claimed); the child counts because + # the Dependent Taxpayer Test does not apply: 1 x $5,300. + vt_personal_exemptions: 5300 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml index b37c41341cd..6677c4c2e6f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_additional_exemption.yaml @@ -218,3 +218,74 @@ state_code: WI output: wi_additional_exemption: 0 + +- name: 2025 single filer aged 65 who can be claimed but is not claimed gets no Wisconsin $250 exemption + period: 2025 + input: + people: + filer: + age: 65 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [filer] + households: + household: + members: [filer] + state_code: WI + output: + # Line 10b requires the $700 exemption on line 10a, which a filer who + # can be claimed does not get whether or not anyone claims them. + wi_additional_exemption: 0 + +- name: 2025 joint return where one spouse aged 65 can be claimed but is not (the claimable spouse is the head) + period: 2025 + input: + people: + person_a: + age: 65 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + is_tax_unit_head: true + is_tax_unit_spouse: false + person_b: + age: 65 + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_a, person_b] + filing_status: JOINT + households: + household: + members: [person_a, person_b] + state_code: WI + output: + # Only the spouse who cannot be claimed gets $250. + wi_additional_exemption: 250 + +- name: 2025 joint return where one spouse aged 65 can be claimed but is not (the claimable spouse is the spouse) + period: 2025 + input: + people: + person_b: + age: 65 + is_tax_unit_head: true + is_tax_unit_spouse: false + person_a: + age: 65 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [person_b, person_a] + filing_status: JOINT + households: + household: + members: [person_b, person_a] + state_code: WI + output: + wi_additional_exemption: 250 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index abc93f23800..4802db8710f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -1,386 +1,97 @@ - name: wi_homestead_eligible unit test 1 + absolute_error_margin: 0.01 period: 2021 input: - people: - head: - age: 18 - employment_income: 8000 - spouse: - age: 17 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 18 + age_spouse: 17 + employment_income: 8_000 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 2 + absolute_error_margin: 0.01 period: 2021 input: - people: - head: - age: 17 - employment_income: 8000 - spouse: - age: 17 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 17 + age_spouse: 17 + employment_income: 8_000 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 3 + absolute_error_margin: 0.01 period: 2021 input: - people: - head: - age: 18 - employment_income: 0 - spouse: - age: 17 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 18 + age_spouse: 17 + employment_income: 0 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 4 + absolute_error_margin: 0.01 period: 2021 input: - people: - head: - age: 62 - employment_income: 0 - spouse: - age: 60 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 62 + age_spouse: 60 + employment_income: 0 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 5 + absolute_error_margin: 0.01 period: 2022 input: - people: - head: - age: 61 - employment_income: 0 - spouse: - age: 60 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 61 + age_spouse: 60 + employment_income: 0 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: false - name: wi_homestead_eligible unit test 6 + absolute_error_margin: 0.01 period: 2022 input: - people: - head: - age: 61 - employment_income: 0 - spouse: - age: 60 - is_disabled: true - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 9000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 61 + age_spouse: 60 + employment_income: 0 + head_is_disabled: false + spouse_is_disabled: true + wi_homestead_income: 9_000 + state_code: WI output: wi_homestead_eligible: true - name: wi_homestead_eligible unit test 7 + absolute_error_margin: 0.01 period: 2021 input: - people: - head: - age: 18 - employment_income: 8000 - spouse: - age: 17 - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [head, spouse] - filing_status: JOINT - wi_homestead_income: 25000 - households: - household: - members: [head, spouse] - state_code: WI + age_head: 18 + age_spouse: 17 + employment_income: 8_000 + head_is_disabled: false + spouse_is_disabled: false + wi_homestead_income: 25_000 + state_code: WI output: wi_homestead_eligible: false - -- name: "2025 single filer who can be claimed as a dependent: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" - period: 2025 - input: - people: - filer: - age: 30 - employment_income: 1000 - real_estate_taxes: 1000 - claimed_as_dependent_on_another_return: true - tax_units: - tax_unit: - members: [filer] - households: - household: - members: [filer] - state_code: WI - output: - # Wis. Stat. 71.53(2)(d): no claim by a claimant claimed as a dependent - # by another person, unless the claimant is 62 or older. - wi_homestead_eligible: false - wi_homestead_credit: 0 - -- name: "2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the head)" - period: 2025 - input: - people: - person_a: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: WI - output: - # The unclaimed spouse may be the claimant (Tax 14.02). Household - # income $1,000 is below the $8,060 phaseout start; property tax - # $1,000 x 80% = $800. - wi_homestead_eligible: true - wi_homestead_credit: 800 - -- name: "2025 joint return with one claimable spouse: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older (the claimable spouse is the spouse)" - period: 2025 - input: - people: - person_b: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - is_tax_unit_head: true - is_tax_unit_spouse: false - person_a: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_b, person_a] - filing_status: JOINT - households: - household: - members: [person_b, person_a] - state_code: WI - output: - wi_homestead_eligible: true - wi_homestead_credit: 800 - -- name: "2025 joint return with both spouses claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" - period: 2025 - input: - people: - person_a: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: WI - output: - wi_homestead_eligible: false - wi_homestead_credit: 0 - -- name: "2025 joint return with neither spouse claimable: Wisconsin homestead credit needs a claimant not claimed as a dependent unless 62 or older" - period: 2025 - input: - people: - person_a: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 30 - employment_income: 500 - real_estate_taxes: 500 - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: WI - output: - # Same as above: $1,000 x 80% = $800. - wi_homestead_eligible: true - wi_homestead_credit: 800 - -- name: "2025 joint return with both spouses claimable: Wisconsin homestead credit allows a claimant aged 62 or older even if claimed" - period: 2025 - input: - people: - person_a: - age: 62 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 62 - employment_income: 500 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: WI - output: - # The age-62 exception keeps both claimed spouses eligible: $800. - wi_homestead_eligible: true - wi_homestead_credit: 800 - -- name: "2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the head)" - period: 2025 - input: - people: - person_a: - age: 30 - employment_income: 1000 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: true - is_tax_unit_spouse: false - person_b: - age: 30 - real_estate_taxes: 500 - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_a, person_b] - filing_status: JOINT - households: - household: - members: [person_a, person_b] - state_code: WI - output: - # Schedule H instructions: the spouse with earned income must be the - # claimant, and a claimant under 62 may not be claimed as a dependent; - # the unclaimed spouse has no earned income and is not 62 or disabled. - wi_homestead_eligible: false - wi_homestead_credit: 0 - -- name: "2025 joint return where only the claimable spouse has earned income: no Wisconsin homestead claimant (the claimable spouse is the spouse)" - period: 2025 - input: - people: - person_b: - age: 30 - real_estate_taxes: 500 - is_tax_unit_head: true - is_tax_unit_spouse: false - person_a: - age: 30 - employment_income: 1000 - real_estate_taxes: 500 - claimed_as_dependent_on_another_return: true - is_tax_unit_head: false - is_tax_unit_spouse: true - tax_units: - tax_unit: - members: [person_b, person_a] - filing_status: JOINT - households: - household: - members: [person_b, person_a] - state_code: WI - output: - wi_homestead_eligible: false - wi_homestead_credit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml index d4a07b0daa7..557b240cfa2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml @@ -240,3 +240,26 @@ state_code: WV output: wv_personal_exemption: 6000 + +- name: 2025 single filer who can be claimed but whose would-be claimant meets the filing exception gets a West Virginia dependent exemption + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 16000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: WV + output: + # No own exemption; one dependent exemption x $2,000, so no $500. + wv_personal_exemption: 2000 diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py index d48aded74e2..6202e2f34bd 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py @@ -10,7 +10,6 @@ class ut_at_home_parent_credit_potential(Variable): defined_for = "ut_at_home_parent_credit_agi_eligible" reference = ( "https://le.utah.gov/xcode/Title59/Chapter10/59-10-S1005.html", - "https://le.utah.gov/xcode/Title59/Chapter10/C59-10-S1005_2026050620260506.pdf#page=1", "https://www.taxformfinder.org/forms/2021/2021-utah-tc-40-full-packet.pdf#page=23", ) @@ -30,17 +29,11 @@ def formula(tax_unit, period, parameters): qualifying_child = (age < p.max_child_age) & is_dependent & ~filer_is_dependent count_qualifying_children = tax_unit.sum(qualifying_child) - # 59-10-1005(2): $100 "for each qualifying child if ... the claimant or - # another claimant filing a joint individual income tax return with - # the claimant is an at-home parent", so one credit per child on the - # return however many spouses qualify. - has_at_home_parent = ( - add( - tax_unit, - period, - ["ut_at_home_parent_credit_earned_income_eligible_person"], - ) - > 0 + # Multiply by each qualifying parent; they can claim it separately. + income_eligible_person = add( + tax_unit, + period, + ["ut_at_home_parent_credit_earned_income_eligible_person"], ) - return p.amount * count_qualifying_children * has_at_home_parent + return p.amount * count_qualifying_children * income_eligible_person diff --git a/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py b/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py deleted file mode 100644 index a8faf6b6881..00000000000 --- a/policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py +++ /dev/null @@ -1,38 +0,0 @@ -from policyengine_us.model_api import * - - -class vt_student_loan_interest_subtraction(Variable): - value_type = float - entity = TaxUnit - label = "Vermont student loan interest subtraction" - unit = USD - definition_period = YEAR - reference = ( - "https://legislature.vermont.gov/statutes/section/32/151/05811", - "https://legislature.vermont.gov/Documents/2022/Docs/ACTS/ACT138/ACT138%20As%20Enacted.pdf#page=4", - # Schedule IN-112 lines 16a-16c, form and instructions - # PDF pages 23, 33 - "https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=23", - ) - defined_for = StateCode.VT - - def formula(tax_unit, period, parameters): - p = parameters(period).gov.states.vt.tax.income.agi.student_loan_interest - # 32 V.S.A. 5811(21)(B)(vi) subtracts the interest a qualified resident - # taxpayer paid on a qualified education loan. Schedule IN-112 line 16 - # subtracts the interest paid (16a) less the amount already deducted - # on federal Schedule 1 (16b), so interest is not deducted twice. The - # subtraction has no dependency test of its own: a filer who is - # claimed elsewhere loses the federal deduction (IRC 221(c)), and - # Vermont then subtracts the whole amount. - person = tax_unit.members - filer = person("is_tax_unit_head_or_spouse", period) - interest_paid = tax_unit.sum(filer * person("student_loan_interest", period)) - federal_deduction = add(tax_unit, period, ["student_loan_interest_ald"]) - remaining_interest = max_(interest_paid - federal_deduction, 0) - # 5811(29)(B): a qualified resident taxpayer's adjusted gross income is - # at most $200,000 if married filing jointly and $120,000 otherwise. - agi = tax_unit("adjusted_gross_income", period) - filing_status = tax_unit("filing_status", period) - income_eligible = agi <= p.income_limit[filing_status] - return income_eligible * remaining_interest diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index b4f04e555d9..a98deba7e14 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -28,12 +28,13 @@ def formula(tax_unit, period, parameters): ) eligible = (age <= p.age_limit) & dependent & ~filer_is_dependent count_eligible = tax_unit.sum(eligible) + # Get maximum credit amount. + max_credit = p.amount * count_eligible # Get adjusted gross income. agi = tax_unit("adjusted_gross_income", period) - # 5830f(b): "the amount of the credit per child" is reduced, but not - # below zero, by $20 for each $1,000 of AGI over $125,000. + # Reduce credit amount over the phaseout range. excess_agi = max_(agi - p.reduction.start, 0) increments = np.ceil(excess_agi / p.reduction.increment) - reduction_per_child = p.reduction.amount * increments - credit_per_child = max_(p.amount - reduction_per_child, 0) - return count_eligible * credit_per_child + total_reduction = p.reduction.amount * increments + # Return reduced credit amount. + return max_(max_credit - total_reduction, 0) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py index a99a39f3a0d..c616d3e4e0e 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py @@ -6,34 +6,29 @@ class wi_homestead_eligible(Variable): entity = TaxUnit label = "Wisconsin homestead credit eligibility status" definition_period = YEAR - reference = ( - "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7", - # Schedule H instructions, Steps 1 and 2 and questions 1c-1d - # PDF pages 4, 10 - "https://www.revenue.wi.gov/TaxForms2025/2025-ScheduleH-inst.pdf#page=4", - ) + reference = "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7" defined_for = StateCode.WI def formula(tax_unit, period, parameters): - p = parameters(period).gov.states.wi.tax.income.credits.homestead.eligible - person = tax_unit.members - filer = person("is_tax_unit_head_or_spouse", period) - age = person("age", period) - elderly = age >= p.min_elderly_age - # The claimant must be 18 or older and must be 62 or older, disabled - # or have earned income; the Schedule H instructions add, for a - # married couple, "The spouse that meets this definition must be the - # claimant." - earnings = add(person, period, p.earnings_sources) - disabled = person("is_disabled", period) - qualifies = (age >= p.min_age) & (elderly | disabled | (earnings > 0)) - # Wis. Stat. 71.53(2)(d) and Schedule H Step 2a: no claim by a - # claimant who was or will be claimed as a dependent on another - # person's federal return, unless the claimant is 62 or older. - claimed = person("claimed_as_dependent_on_another_return", period) - eligible_claimant = filer & qualifies & (~claimed | elderly) + p = parameters(period).gov.states.wi.tax.income.credits + # minimum age eligibility + head_age = tax_unit("age_head", period) + spouse_age = tax_unit("age_spouse", period) + min_age = p.homestead.eligible.min_age + age_eligible = (head_age >= min_age) | (spouse_age >= min_age) + # earnings eligibility + earnings = add(tax_unit, period, p.homestead.eligible.earnings_sources) + has_positive_earnings = earnings > 0 + min_elderly_age = p.homestead.eligible.min_elderly_age + head_is_elderly = head_age >= min_elderly_age + spouse_is_elderly = spouse_age >= min_elderly_age + is_elderly = head_is_elderly | spouse_is_elderly + head_is_disabled = tax_unit("head_is_disabled", period) + spouse_is_disabled = tax_unit("spouse_is_disabled", period) + is_disabled = head_is_disabled | spouse_is_disabled + earnings_eligible = has_positive_earnings | is_elderly | is_disabled # income eligibility homestead_income = tax_unit("wi_homestead_income", period) - income_eligible = homestead_income < p.max_income + income_eligible = homestead_income < p.homestead.eligible.max_income # overall eligibility - return tax_unit.any(eligible_claimant) & income_eligible + return age_eligible & earnings_eligible & income_eligible From cabd885a5dd3698034f23771bf41360adeb03ac2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 22:37:56 -0400 Subject: [PATCH 30/42] Fix mixed-population claimable-filer calculations Project return-level dependency flags onto people in the RI rebate, UT at-home parent credit and VT child credit. Mask non-Vermont counties before state table lookups and project household state strings directly in the invariant helper. The existing five vectorized invariant tests pass. --- .../test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py | 2 +- .../ri/tax/income/credits/rebates/ri_child_tax_rebate.py | 2 +- .../income/credits/ut_at_home_parent_credit_potential.py | 2 +- .../gov/states/vt/tax/income/credits/ctc/vt_ctc.py | 2 +- .../states/vt/tax/income/credits/renter/vt_renter_credit.py | 6 +++++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py index b8dc2d6d410..7f5452a42a6 100644 --- a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py @@ -111,7 +111,7 @@ def _identities(sim, year): assert not _calc(sim, name, year)[every_filer_claimed].any(), ( f"{name} in {year} is positive when every filer is claimed" ) - state = np.asarray(sim.calculate("state_code_str", year, map_to="tax_unit")) + state = np.asarray(sim.populations["tax_unit"].household("state_code_str", year)) wv_none = (state == "WV") & (_calc(sim, "exemptions_count", year) == 0) np.testing.assert_array_equal( _calc(sim, "wv_personal_exemption", year)[wv_none], 500 diff --git a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py index 75b23bceb44..2536eef604d 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.py @@ -23,7 +23,7 @@ def formula(tax_unit, period, parameters): # return. Under IRC 152(b)(1) a return on which the filer (or, if # joint, either spouse) can be claimed as a dependent has none, unless # the would-be claimant meets the filing exception (Publication 501). - filer_is_dependent = tax_unit( + filer_is_dependent = person.tax_unit( "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) eligible_child = age_eligibility & dependent & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py index 6202e2f34bd..235bb11ebde 100644 --- a/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py +++ b/policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py @@ -23,7 +23,7 @@ def formula(tax_unit, period, parameters): # (or, if joint, either spouse) can be claimed as a dependent has no # dependents, unless the would-be claimant meets the filing exception # (Publication 501). - filer_is_dependent = tax_unit( + filer_is_dependent = person.tax_unit( "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) qualifying_child = (age < p.max_child_age) & is_dependent & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index a98deba7e14..4562395aa9c 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -23,7 +23,7 @@ def formula(tax_unit, period, parameters): age = person("age", period) p = parameters(period).gov.states.vt.tax.income.credits.ctc dependent = person("is_tax_unit_dependent", period) - filer_is_dependent = tax_unit( + filer_is_dependent = person.tax_unit( "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period ) eligible = (age <= p.age_limit) & dependent & ~filer_is_dependent diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py index 93174e5bdad..0800d12f74b 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py @@ -20,7 +20,11 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.vt.tax.income.credits.renter # get tax unit size and county. tax_unit_size = tax_unit("tax_unit_size", period) - county = tax_unit.household("county", period) + county = tax_unit.household("county_str", period) + # defined_for masks results after evaluation, so every row needs a + # valid Vermont county while indexing the state-only parameter tables. + in_vt = tax_unit.household("state_code_str", period) == "VT" + county = where(in_vt, county, "ADDISON_COUNTY_VT") # 32 V.S.A. 6066(b)(2): the fair market rent is for "a number of # bedrooms equal to the number of personal exemptions" under # 5811(21)(C), which allows none for a filer who can be claimed as a From bc63f0cbae1006feedd6d7515a0468c507c31e1e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 22:43:05 -0400 Subject: [PATCH 31/42] Use filing-exception gate in Vermont exemption fixtures The scalar cases mock filer claimability and dependent counts directly, so mock the Dependent Taxpayer Test helper consumed by the formula. Preserve all expected values; all 20 cases pass. --- .../income/exemptions/vt_personal_exemptions.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml index da3557099bb..7cffbef37a2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/exemptions/vt_personal_exemptions.yaml @@ -4,7 +4,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: true - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 0 state_code: VT output: @@ -16,7 +16,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: true - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 2 state_code: VT output: @@ -29,7 +29,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: false - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 0 state_code: VT output: @@ -41,7 +41,7 @@ filing_status: JOINT head_is_dependent_elsewhere: true spouse_is_dependent_elsewhere: false - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 2 state_code: VT output: @@ -75,7 +75,7 @@ input: filing_status: SINGLE head_is_dependent_elsewhere: true - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 0 state_code: VT output: @@ -86,7 +86,7 @@ input: filing_status: SINGLE head_is_dependent_elsewhere: true - head_or_spouse_is_dependent_elsewhere: true + head_or_spouse_is_dependent_elsewhere_without_filing_exception: true tax_unit_count_dependents: 2 state_code: VT output: From 0891442052d6106142275e48436675f15b908a1a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 22:49:27 -0400 Subject: [PATCH 32/42] Repair Wisconsin claimable-filer test fixtures Represent the empty filer as an object and explicitly set TANF to zero in the five wage-only homestead deduction examples. Preserve expected values and main's loss add-back cases; both files pass. --- .../states/wi/tax/income/wi_exemption.yaml | 2 +- .../wi/tax/income/wi_homestead_income.yaml | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml index 2efa15ea6b6..17307b8dd1c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_exemption.yaml @@ -2,7 +2,7 @@ period: 2021 input: people: - head: + head: {} tax_units: tax_unit: members: [head] diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 2bee48c0198..f6b09980a59 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -100,6 +100,7 @@ # add-back is the 5,000 non-SSTB loss. wi_homestead_income: 5_000 +# Hold TANF at zero to isolate the $500 dependent deduction on wage income. - name: "2025 single filer who can be claimed as a dependent: no Wisconsin homestead $500 dependent deduction" period: 2025 input: @@ -111,6 +112,10 @@ claimed_as_dependent_on_another_return: true child: age: 4 + spm_units: + spm_unit: + members: [filer, child] + tanf: 0 tax_units: tax_unit: members: [filer, child] @@ -144,6 +149,10 @@ is_tax_unit_spouse: true child: age: 4 + spm_units: + spm_unit: + members: [person_a, person_b, child] + tanf: 0 tax_units: tax_unit: members: [person_a, person_b, child] @@ -176,6 +185,10 @@ is_tax_unit_spouse: true child: age: 4 + spm_units: + spm_unit: + members: [person_b, person_a, child] + tanf: 0 tax_units: tax_unit: members: [person_b, person_a, child] @@ -208,6 +221,10 @@ is_tax_unit_spouse: true child: age: 4 + spm_units: + spm_unit: + members: [person_a, person_b, child] + tanf: 0 tax_units: tax_unit: members: [person_a, person_b, child] @@ -238,6 +255,10 @@ is_tax_unit_spouse: true child: age: 4 + spm_units: + spm_unit: + members: [person_a, person_b, child] + tanf: 0 tax_units: tax_unit: members: [person_a, person_b, child] From 3471fafd46ece211fcf4a6348c4fc7561a031109 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 00:59:47 -0400 Subject: [PATCH 33/42] Preserve claimability rules in contributed dependent exemptions --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 + .../repeal_state_dependent_exemptions.py | 8 +- .../sc_dependent_exemption_reform.py | 6 +- .../vt_dependent_exemption_reform.py | 11 ++- .../wv_dependent_exemption_reform.py | 36 +++++---- .../repeal_state_dependent_exemptions.yaml | 43 +++++++++++ .../sc/dependent_exemption_reform_test.yaml | 64 +++++++++++++++ .../vt/dependent_exemption_reform_test.yaml | 77 +++++++++++++++++++ .../wv/dependent_exemption_reform_test.yaml | 77 +++++++++++++++++++ 9 files changed, 306 insertions(+), 18 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index 74f6c4c44c5..83470e1c54a 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1 +1,3 @@ Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent: dependent exemptions, deductions and child credits on such a return, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, the Rhode Island and Vermont claimant tests for property tax relief and the renter credit, and Wisconsin's care subtraction and credit expenses. + +Preserve claimable-filer rules in the contributed South Carolina, Vermont and West Virginia dependent exemption reforms and the state dependent exemption repeal. diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index d05820bfb6b..eaf11854a5e 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -215,9 +215,13 @@ class wv_personal_exemption(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wv.tax.income.exemptions - tax_unit_size = tax_unit("head_spouse_count", period) + # Repealing dependent exemptions does not restore a claimable + # filer's own exemption. Retain IT-140's $500 zero-count amount. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) return where( - tax_unit_size == 0, p.base_personal, p.personal * tax_unit_size + own_exemptions == 0, p.base_personal, p.personal * own_exemptions ) class ca_exemptions(Variable): diff --git a/policyengine_us/reforms/states/sc/dependent_exemption/sc_dependent_exemption_reform.py b/policyengine_us/reforms/states/sc/dependent_exemption/sc_dependent_exemption_reform.py index ccb3db4039e..f227705ede8 100644 --- a/policyengine_us/reforms/states/sc/dependent_exemption/sc_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/sc/dependent_exemption/sc_dependent_exemption_reform.py @@ -21,7 +21,11 @@ def formula(tax_unit, period, parameters): period ).gov.states.sc.tax.income.deductions.dependent_exemption.amount person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # Apply the dependent taxpayer test before either age path, + # preserving the claimant filing exception (IRC 152(b)(1)). + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) # Age gate: when in effect, only dependents under the chosen age # are re-priced to the reform amount; older dependents keep the # baseline exemption. Otherwise every dependent is re-priced, diff --git a/policyengine_us/reforms/states/vt/dependent_exemption/vt_dependent_exemption_reform.py b/policyengine_us/reforms/states/vt/dependent_exemption/vt_dependent_exemption_reform.py index 35ba6e2a210..69440c7d77b 100644 --- a/policyengine_us/reforms/states/vt/dependent_exemption/vt_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/vt/dependent_exemption/vt_dependent_exemption_reform.py @@ -15,7 +15,10 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) - is_dependent = person("is_tax_unit_dependent", period) + # Retain the baseline dependent taxpayer test and filing exception. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) # Apply age limit if in effect if p.age_limit.in_effect: @@ -49,7 +52,11 @@ class vt_older_dependents_count(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # Mask the fallback too, so disallowed children cannot reappear + # as older dependents after the eligible count is reduced. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( "vt_eligible_dependents_count", period diff --git a/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py b/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py index 933e8deed81..3af28ffb130 100644 --- a/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py @@ -15,7 +15,10 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) - is_dependent = person("is_tax_unit_dependent", period) + # Retain the baseline dependent taxpayer test and filing exception. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) # Apply age limit if in effect if p.age_limit.in_effect: @@ -49,7 +52,10 @@ class wv_older_dependents_count(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # Apply the same gate to the older-dependent fallback. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( "wv_eligible_dependents_count", period @@ -67,22 +73,26 @@ class wv_personal_exemption(Variable): def formula(tax_unit, period, parameters): p_base = parameters(period).gov.states.wv.tax.income.exemptions - # Personal exemptions exclude the dependent portion (head + spouse + - # any over-age dependents that fall back to the personal count). - tax_unit_size = tax_unit("tax_unit_size", period) - dependents = tax_unit("tax_unit_dependents", period) - older_dependents = tax_unit("wv_older_dependents_count", period) - personal_count = tax_unit_size - dependents + older_dependents - personal_amount = where( - tax_unit_size == 0, - p_base.base_personal, - p_base.personal * personal_count, + # Claimable filers have no own exemption, even when the filing + # exception permits them to claim a dependent. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period ) + older_dependents = tax_unit("wv_older_dependents_count", period) + personal_count = own_exemptions + older_dependents + personal_amount = p_base.personal * personal_count # Add separated dependent exemption. dependent_exemption_amount = tax_unit("wv_dependent_exemption", period) - return personal_amount + dependent_exemption_amount + # IT-140 line 6 grants $500 once when there are no exemptions. + # Test the exemption count, independent of the reform's amount. + exemptions = tax_unit("exemptions_count", period) + return where( + exemptions == 0, + p_base.base_personal, + personal_amount + dependent_exemption_amount, + ) class reform(Reform): def apply(self): diff --git a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml index 2c9f3842667..ee04221c61d 100644 --- a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml +++ b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml @@ -151,3 +151,46 @@ output: # Baseline keeps the dependent add: 7,600 + 1 x 1,000 + 6,800 = 15,400. ma_income_tax_exemption_threshold: 15_400 + +- name: WV dependent exemption repeal - claimed filer with a child keeps the zero-exemption amount + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: WV}} + output: {wv_personal_exemption: 500} + +- name: WV dependent exemption repeal - claimed childless filer keeps the zero-exemption amount + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [parent], filing_status: SINGLE}} + households: {household: {members: [parent], state_name: WV}} + output: {wv_personal_exemption: 500} + +- name: WV dependent exemption repeal - unclaimed but claimable childless filer keeps the zero-exemption amount + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: {tax_unit: {members: [parent], filing_status: SINGLE}} + households: {household: {members: [parent], state_name: WV}} + output: {wv_personal_exemption: 500} diff --git a/policyengine_us/tests/policy/contrib/states/sc/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/sc/dependent_exemption_reform_test.yaml index b9dd61a79e2..ef737773dc1 100644 --- a/policyengine_us/tests/policy/contrib/states/sc/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/sc/dependent_exemption_reform_test.yaml @@ -89,3 +89,67 @@ households: {household: {members: [parent, child1], state_name: SC}} # Dependent one year under the limit (12 < 13) is re-priced to the reform amount (0). output: {sc_dependent_exemption: 0} + +# IRC 152(b)(1) applies inside the reform as well as in the baseline. +- name: SC dependent exemption reform - claimed filer cannot claim a child at default parameters + period: 2025 + reforms: policyengine_us.reforms.states.sc.dependent_exemption.sc_dependent_exemption_reform.sc_dependent_exemption_reform + input: + gov.contrib.states.sc.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: SC}} + output: {sc_dependent_exemption: 0} + +- name: SC dependent exemption reform - unclaimed but claimable filer cannot claim a child + period: 2025 + reforms: policyengine_us.reforms.states.sc.dependent_exemption.sc_dependent_exemption_reform.sc_dependent_exemption_reform + input: + gov.contrib.states.sc.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: SC}} + output: {sc_dependent_exemption: 0} + +- name: SC dependent exemption reform - claimable filer cannot recover an older dependent exemption + period: 2025 + reforms: policyengine_us.reforms.states.sc.dependent_exemption.sc_dependent_exemption_reform.sc_dependent_exemption_reform + input: + gov.contrib.states.sc.dependent_exemption.in_effect: true + gov.contrib.states.sc.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.sc.dependent_exemption.age_limit.threshold: 13 + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 15, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: SC}} + output: {sc_dependent_exemption: 0} + +- name: SC dependent exemption reform - filing exception permits the child exemption + period: 2025 + reforms: policyengine_us.reforms.states.sc.dependent_exemption.sc_dependent_exemption_reform.sc_dependent_exemption_reform + input: + gov.contrib.states.sc.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: SC}} + output: {sc_dependent_exemption: 4_930} diff --git a/policyengine_us/tests/policy/contrib/states/vt/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/vt/dependent_exemption_reform_test.yaml index e87d6f00982..36c9d083418 100644 --- a/policyengine_us/tests/policy/contrib/states/vt/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/vt/dependent_exemption_reform_test.yaml @@ -252,3 +252,80 @@ # VT income tax from 1,221.08 to 1,576.18 (about +355.10). output: vt_income_tax: 1576.18 + +# The dependent taxpayer test masks both reform and older-dependent paths. + +- name: VT dependent exemption reform - claimed filer cannot claim a child at default parameters + period: 2025 + reforms: policyengine_us.reforms.states.vt.dependent_exemption.vt_dependent_exemption_reform.vt_dependent_exemption_reform + input: + gov.contrib.states.vt.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: VT}} + output: + vt_eligible_dependents_count: 0 + vt_older_dependents_count: 0 + vt_personal_exemptions: 0 + +- name: VT dependent exemption reform - unclaimed but claimable filer cannot claim a child + period: 2025 + reforms: policyengine_us.reforms.states.vt.dependent_exemption.vt_dependent_exemption_reform.vt_dependent_exemption_reform + input: + gov.contrib.states.vt.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: VT}} + output: + vt_eligible_dependents_count: 0 + vt_older_dependents_count: 0 + vt_personal_exemptions: 0 + +- name: VT dependent exemption reform - claimable filer cannot recover an older dependent exemption + period: 2025 + reforms: policyengine_us.reforms.states.vt.dependent_exemption.vt_dependent_exemption_reform.vt_dependent_exemption_reform + input: + gov.contrib.states.vt.dependent_exemption.in_effect: true + gov.contrib.states.vt.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.vt.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: VT}} + output: + vt_eligible_dependents_count: 0 + vt_older_dependents_count: 0 + vt_personal_exemptions: 0 + +- name: VT dependent exemption reform - filing exception permits the child exemption without an own exemption + period: 2025 + reforms: policyengine_us.reforms.states.vt.dependent_exemption.vt_dependent_exemption_reform.vt_dependent_exemption_reform + input: + gov.contrib.states.vt.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: VT}} + output: + vt_eligible_dependents_count: 1 + vt_older_dependents_count: 0 + vt_personal_exemptions: 5_300 diff --git a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml index 69a598e05a0..d29f7f86de2 100644 --- a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml @@ -136,3 +136,80 @@ # exemption from $6,000 to $2,000 ($4,000 of taxable income added), raising WV # income tax from 1,787.10 to 1,964.70 (about +177.60). output: {wv_income_tax: 1964.70} + +# The dependent taxpayer test masks both reform and older-dependent paths. + +- name: WV dependent exemption reform - claimed filer cannot claim a child at default parameters + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: WV}} + output: + wv_personal_exemption: 500 + wv_eligible_dependents_count: 0 + wv_older_dependents_count: 0 + +- name: WV dependent exemption reform - unclaimed but claimable filer cannot claim a child + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: WV}} + output: + wv_personal_exemption: 500 + wv_eligible_dependents_count: 0 + wv_older_dependents_count: 0 + +- name: WV dependent exemption reform - claimable filer cannot recover an older dependent exemption + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + gov.contrib.states.wv.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.wv.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: WV}} + output: + wv_personal_exemption: 500 + wv_eligible_dependents_count: 0 + wv_older_dependents_count: 0 + +- name: WV dependent exemption reform - filing exception permits the child exemption without an own exemption + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + people: + parent: + age: 40 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: WV}} + output: + wv_personal_exemption: 2_000 + wv_eligible_dependents_count: 1 + wv_older_dependents_count: 0 From 5ac48d7686bc5b9ca8689b654b1d03a553114ec6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 01:02:25 -0400 Subject: [PATCH 34/42] Use payroll-adjusted earnings for Wisconsin dependent deductions --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 + .../wi_retirement_income_exclusion_tax.yaml | 28 ++++++++++ .../wi/tax/income/wi_standard_deduction.yaml | 55 +++++++++++++++++++ .../wi/tax/income/wi_standard_deduction.py | 19 +++++-- 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index 83470e1c54a..af8bdb674f7 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1,3 +1,5 @@ Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent: dependent exemptions, deductions and child credits on such a return, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, the Rhode Island and Vermont claimant tests for property tax relief and the renter credit, and Wisconsin's care subtraction and credit expenses. Preserve claimable-filer rules in the contributed South Carolina, Vermont and West Virginia dependent exemption reforms and the state dependent exemption repeal. + +Exclude pretax payroll deductions from Wisconsin dependent standard deduction earnings on both tax paths. diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml index 2ec7fa9cf4d..7d5c11c3d92 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml @@ -95,6 +95,7 @@ # Regression for issue #8817: the Line 16 subtraction must reduce WI income # BEFORE the standard-deduction phaseout, so a large exclusion recovers # standard deduction that the pre-subtraction income had phased out. + - name: E2E - Joint 67+ with retirement income above the standard-deduction phaseout absolute_error_margin: 1 period: 2025 @@ -131,6 +132,7 @@ # Below the single phaseout start ($19,550, 2025) the standard deduction is at # its max for both full and exclusion-reduced income, so the extra deduction the # fix unlocks is $0 (no regression vs. a plain taxinc - line16 subtraction). + - name: Exclusion path - below phaseout start, extra standard deduction is zero (no regression) absolute_error_margin: 0.01 period: 2025 @@ -150,6 +152,7 @@ # Fully phased-out top end (age 85, very high income): SD = $0 for both full and # reduced income, so extra_SD = 0 and the exclusion is a plain subtraction. + - name: Exclusion path - fully phased-out top end, extra standard deduction is zero absolute_error_margin: 0.01 period: 2025 @@ -170,6 +173,7 @@ # Partial-unlock boundary: full income ($140k) is past the single full-phaseout # point (SD_full = 0) while reduced income ($116k) lands INSIDE the phaseout, so # the fix unlocks a positive partial deduction. + - name: Exclusion path - partial unlock (full income past phaseout end, reduced income inside it) absolute_error_margin: 1 period: 2025 @@ -190,6 +194,7 @@ # Joint with only ONE spouse age 67+: the exclusion uses the per-person single # cap ($24,000), NOT the $48,000 both-eligible joint pool. + - name: Exclusion path - joint with one spouse 67+, uses the $24,000 per-person cap absolute_error_margin: 0.01 period: 2025 @@ -219,3 +224,26 @@ # tax = 6,000 * 0.035 = 210. wi_retirement_income_exclusion_amount: 24_000 wi_retirement_income_exclusion_tax: 210 + +# $34,000 WI income - $24,000 exclusion - $4,450 dependent deduction +# - $0 exemption = $5,550, taxed at 3.5% = $194.25. Pension and IRA both qualify. + +- name: 2025 claimable but unclaimed retiree uses the dependent deduction on the positive retirement exclusion + path + period: 2025 + absolute_error_margin: 0.01 + input: + age: 70 + employment_income: 4000 + taxable_pension_income: 20000 + taxable_ira_distributions: 10000 + is_tax_unit_head: true + filing_status: SINGLE + state_code: WI + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + output: + wi_retirement_income_exclusion_amount: 24000 + wi_standard_deduction: 4450 + wi_exemption: 0 + wi_retirement_income_exclusion_tax: 194.25 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml index 4aca346ed9e..e3a7516b372 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_standard_deduction.yaml @@ -298,3 +298,58 @@ output: # 2023 Form 1 worksheet: max($1,250, $3,000 + $400) = $3,400. wi_standard_deduction: 3400 + + +- name: Wisconsin dependent deduction uses wages included in Wisconsin income after pretax retirement contributions + period: 2025 + input: + people: + filer: + age: 22 + is_full_time_student: true + employment_income: 10_000 + traditional_401k_contributions: 5_000 + taxable_interest_income: 10_000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: [filer] + spm_units: + spm_unit: + members: [filer] + tanf: 0 + households: + household: + members: [filer] + state_code: WI + output: + # 2025 Form 1 dependent worksheet: WI-included earnings $5,000 + $450. + irs_employment_income: 5_000 + wi_agi: 15_000 + wi_standard_deduction: 5_450 + wi_taxable_income: 9_550 + +- name: Wisconsin caps an unclaimed but claimable filer's standard deduction + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 1_000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: [filer] + spm_units: + spm_unit: + members: [filer] + tanf: 0 + households: + household: + members: [filer] + state_code: WI + output: + # Claimability, regardless of whether anyone actually claims the filer. + wi_standard_deduction: 1_450 diff --git a/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py b/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py index fe0d2008d2d..0a4d1dc6f88 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/wi_standard_deduction.py @@ -40,7 +40,20 @@ def wi_dependent_standard_deduction_limit(tax_unit, period, parameters): # joint return) can be claimed", using the return's earned income. # Returns infinity when no filer can be claimed. p = parameters(period).gov.irs.deductions.standard.dependent - earned_income = max_(tax_unit("tax_unit_earned_income", period), 0) + # Worksheet line 1 uses earnings included in Wisconsin income: wages + # after pretax payroll deductions, plus net self-employment income. + # Non-W-2 scholarship/grant income is excluded from these sources. + earned_income = add( + tax_unit.members, + period, + [ + "irs_employment_income", + "self_employment_income", + "sstb_self_employment_income", + ], + ) + is_filer = ~tax_unit.members("is_tax_unit_dependent", period) + earned_income = max_(tax_unit.sum(earned_income * is_filer), 0) limit = max_(p.amount, earned_income + p.additional_earned_income) filer_is_dependent = tax_unit("head_or_spouse_is_dependent_elsewhere", period) return where(filer_is_dependent, limit, np.inf) @@ -58,10 +71,8 @@ class wi_standard_deduction(Variable): "https://www.revenue.wi.gov/TaxForms2022/2022-Form1f.pdf", "https://www.revenue.wi.gov/TaxForms2022/2022-Form1-Inst.pdf", "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2023/0002_individual_income_tax_informational_paper_2.pdf", - # Standard Deduction Table (keyed to WI income, line 7) and the statute, corroborating the phaseout: - "https://www.revenue.wi.gov/TaxForms2025/2025-Form1-inst.pdf#page=35", "https://docs.legis.wisconsin.gov/statutes/statutes/71/i/05/22", - # Standard Deduction Worksheet for Dependents + # Dependent worksheet and standard deduction table, PDF pages 15, 35-37 "https://www.revenue.wi.gov/TaxForms2025/2025-Form1-Inst.pdf#page=15", ) defined_for = StateCode.WI From 0d036d21976db6dae9b6f63e5931dd6b7da59c9c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 01:06:34 -0400 Subject: [PATCH 35/42] Add verified Vermont zero-bedroom fair market rents --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 + .../credits/renter/fair_market_rent.yaml | 108 +++++- .../income/credits/rent/vt_renter_credit.yaml | 330 +++++++++++++++++- .../income/credits/renter/vt_renter_credit.py | 2 +- 4 files changed, 434 insertions(+), 8 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index af8bdb674f7..30525e4d025 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -3,3 +3,5 @@ Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wiscons Preserve claimable-filer rules in the contributed South Carolina, Vermont and West Virginia dependent exemption reforms and the state dependent exemption repeal. Exclude pretax payroll deductions from Wisconsin dependent standard deduction earnings on both tax paths. + +Use HUD zero-bedroom rents for zero-exemption Vermont renters in every stored fair market rent year (2021–2025). diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml index 6ddecf03a2c..d782bfb4d03 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml @@ -1,12 +1,12 @@ -description: Vermont sets these fair market rent amounts, based on the county and household size. +description: Vermont sets these fair market rent amounts, based on the county and the number of bedrooms allowed by personal exemptions. metadata: propagate_metadata_to_children: true unit: currency-USD period: month breakdown: - - list(range(1,9)) + - list(range(0,9)) breakdown_labels: - - Household size + - Bedrooms (personal exemptions) label: Vermont fair market rent amount reference: # The values are derived from the renter credit computation tables which represent 10% of the @@ -43,9 +43,19 @@ metadata: href: https://www.huduser.gov/portal/datasets/fmr/fmr2022/FY22_FMRs.xlsx - title: HUD FY2021 Fair Market Rents href: https://www.huduser.gov/portal/datasets/fmr/fmr2021/FY21_4050_FMRs_rev.xlsx + - title: "HUD FY2021 Final Fair Market Rent Schedule #page 48-49" + href: https://www.huduser.gov/portal/datasets/fmr/fmr2021/FY2021-ScheduleB.pdf#page=48 + - title: "HUD FY2022 Fair Market Rent Schedule #page 48-50" + href: https://www.huduser.gov/portal/datasets/fmr/fmr2022/FY2022_FMR_Schedule.pdf#page=48 + - title: "HUD FY2023 Fair Market Rent Schedule #page 49-50" + href: https://www.huduser.gov/portal/datasets/fmr/fmr2023/FY2023_FMR_Schedule.pdf#page=49 + - title: "HUD FY2024 Fair Market Rent Schedule #page 51-52" + href: https://www.huduser.gov/portal/datasets/fmr/fmr2024/FY2024_FMR_Schedule.pdf#page=51 + - title: "HUD FY2025 Fair Market Rent Schedule #page 51-52" + href: https://www.huduser.gov/portal/datasets/fmr/fmr2025/FY2025_FMR_Schedule.pdf#page=51 # Each county's value is HUD's fair market rent for the fiscal year that includes # June 30 of the taxable year (FY2025 for 2025), for the county's HUD area, with -# household size n using the min(n, 4)-bedroom rent. Chittenden's 2,083 is the +# exemption count n using the min(n, 4)-bedroom rent. Chittenden's 2,083 is the # rent at which 10% x 12 reaches the $2,500 maximum the department applies to # the credit amount (2,500 / 1.2 = 2,083.33). # @@ -64,6 +74,96 @@ metadata: # - 2023 reconstruction: the average with the Burlington four-bedroom rent # (2,170) held to 2,083.33 (all 12 unadjusted give 1,913, not the published 1,904). # - 2025: the unrounded average (1,057.75 x 1.2 = 1,269.3, published 1,269). +# Zero-bedroom rents below are the published HUD 0 BR amounts. Franklin and +# Grand Isle use the unrounded arithmetic mean of all 12 Vermont HUD FMR areas, +# following the state-average rule in 6061(7)(C). This is the averaging convention +# that reproduces the department's unrounded 2025 rents. These zero-bedroom +# values are calculated from HUD schedules rather than department credit tables. +0: + ADDISON_COUNTY_VT: + 2021-01-01: 751 + 2022-01-01: 779 + 2023-01-01: 842 + 2024-01-01: 1_017 + 2025-01-01: 1_096 + BENNINGTON_COUNTY_VT: + 2021-01-01: 710 + 2022-01-01: 763 + 2023-01-01: 849 + 2024-01-01: 936 + 2025-01-01: 936 + CALEDONIA_COUNTY_VT: + 2021-01-01: 708 + 2022-01-01: 741 + 2023-01-01: 810 + 2024-01-01: 899 + 2025-01-01: 905 + CHITTENDEN_COUNTY_VT: + 2021-01-01: 1_091 + 2022-01-01: 1_033 + 2023-01-01: 1_139 + 2024-01-01: 1_341 + 2025-01-01: 1_380 + ESSEX_COUNTY_VT: + 2021-01-01: 560 + 2022-01-01: 585 + 2023-01-01: 730 + 2024-01-01: 812 + 2025-01-01: 823 + FRANKLIN_COUNTY_VT: + 2021-01-01: 736.0833333333334 + 2022-01-01: 766 + 2023-01-01: 849.0833333333334 + 2024-01-01: 976.3333333333334 + 2025-01-01: 1_007.6666666666666 + GRAND_ISLE_COUNTY_VT: + 2021-01-01: 736.0833333333334 + 2022-01-01: 766 + 2023-01-01: 849.0833333333334 + 2024-01-01: 976.3333333333334 + 2025-01-01: 1_007.6666666666666 + LAMOILLE_COUNTY_VT: + 2021-01-01: 788 + 2022-01-01: 801 + 2023-01-01: 874 + 2024-01-01: 913 + 2025-01-01: 883 + ORANGE_COUNTY_VT: + 2021-01-01: 740 + 2022-01-01: 787 + 2023-01-01: 852 + 2024-01-01: 976 + 2025-01-01: 1_013 + ORLEANS_COUNTY_VT: + 2021-01-01: 592 + 2022-01-01: 638 + 2023-01-01: 706 + 2024-01-01: 831 + 2025-01-01: 845 + RUTLAND_COUNTY_VT: + 2021-01-01: 764 + 2022-01-01: 814 + 2023-01-01: 904 + 2024-01-01: 991 + 2025-01-01: 992 + WASHINGTON_COUNTY_VT: + 2021-01-01: 717 + 2022-01-01: 760 + 2023-01-01: 839 + 2024-01-01: 985 + 2025-01-01: 1_075 + WINDHAM_COUNTY_VT: + 2021-01-01: 697 + 2022-01-01: 734 + 2023-01-01: 803 + 2024-01-01: 976 + 2025-01-01: 1_020 + WINDSOR_COUNTY_VT: + 2021-01-01: 715 + 2022-01-01: 757 + 2023-01-01: 841 + 2024-01-01: 1_039 + 2025-01-01: 1_124 1: ADDISON_COUNTY_VT: 2021-01-01: 846 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml index 49479381e56..f8c65d7e5c3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml @@ -309,9 +309,9 @@ output: # 6066(c)(2) bars a claimant "claimed as a dependent", an actual claim. # A filer who can be claimed has no personal exemption; the fair market - # rent table starts at one exemption, so the model uses it: - # round($1,476 x 12 x 10%) = $1,771. - vt_renter_credit: 1771 + # rent uses the zero-bedroom HUD schedule under 6066(b)(2): + # round($1,380 x 12 x 10%) = $1,656. + vt_renter_credit: 1656 # Franklin and Grand Isle use the state-average fair market rent (32 V.S.A. # 6061(7)(C)). Expected values are the department's Renter Credit Website @@ -663,3 +663,327 @@ # Full limit 31,050 = HUD FY2021 extremely low-income limit (PE had 31,040). # Full credit: ROUND(2,083 x 12 x 0.1, 0) = ROUND(2,499.6, 0) = 2,500. vt_renter_credit: 2_500 + +- name: 2021 zero-exemption renter uses HUD zero-bedroom rents in all Vermont counties + period: 2021 + input: + state_code: + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + county: + - ADDISON_COUNTY_VT + - BENNINGTON_COUNTY_VT + - CALEDONIA_COUNTY_VT + - CHITTENDEN_COUNTY_VT + - ESSEX_COUNTY_VT + - FRANKLIN_COUNTY_VT + - GRAND_ISLE_COUNTY_VT + - LAMOILLE_COUNTY_VT + - ORANGE_COUNTY_VT + - ORLEANS_COUNTY_VT + - RUTLAND_COUNTY_VT + - WASHINGTON_COUNTY_VT + - WINDHAM_COUNTY_VT + - WINDSOR_COUNTY_VT + tax_unit_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + exemptions_count: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vt_renter_credit_income: [5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000] + rent_is_shared_with_another_tax_unit: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + housing_assistance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + output: + # Each HUD monthly 0BR amount x 12 x 10%, rounded half up. + # Franklin and Grand Isle use the average of Vermont's 12 HUD areas. + vt_renter_credit: + - 901 # ADDISON_COUNTY_VT + - 852 # BENNINGTON_COUNTY_VT + - 850 # CALEDONIA_COUNTY_VT + - 1309 # CHITTENDEN_COUNTY_VT + - 672 # ESSEX_COUNTY_VT + - 883 # FRANKLIN_COUNTY_VT + - 883 # GRAND_ISLE_COUNTY_VT + - 946 # LAMOILLE_COUNTY_VT + - 888 # ORANGE_COUNTY_VT + - 710 # ORLEANS_COUNTY_VT + - 917 # RUTLAND_COUNTY_VT + - 860 # WASHINGTON_COUNTY_VT + - 836 # WINDHAM_COUNTY_VT + - 858 # WINDSOR_COUNTY_VT + +- name: 2022 zero-exemption renter uses HUD zero-bedroom rents in all Vermont counties + period: 2022 + input: + state_code: + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + county: + - ADDISON_COUNTY_VT + - BENNINGTON_COUNTY_VT + - CALEDONIA_COUNTY_VT + - CHITTENDEN_COUNTY_VT + - ESSEX_COUNTY_VT + - FRANKLIN_COUNTY_VT + - GRAND_ISLE_COUNTY_VT + - LAMOILLE_COUNTY_VT + - ORANGE_COUNTY_VT + - ORLEANS_COUNTY_VT + - RUTLAND_COUNTY_VT + - WASHINGTON_COUNTY_VT + - WINDHAM_COUNTY_VT + - WINDSOR_COUNTY_VT + tax_unit_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + exemptions_count: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vt_renter_credit_income: [5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000] + rent_is_shared_with_another_tax_unit: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + housing_assistance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + output: + # Each HUD monthly 0BR amount x 12 x 10%, rounded half up. + # Franklin and Grand Isle use the average of Vermont's 12 HUD areas. + vt_renter_credit: + - 935 # ADDISON_COUNTY_VT + - 916 # BENNINGTON_COUNTY_VT + - 889 # CALEDONIA_COUNTY_VT + - 1240 # CHITTENDEN_COUNTY_VT + - 702 # ESSEX_COUNTY_VT + - 919 # FRANKLIN_COUNTY_VT + - 919 # GRAND_ISLE_COUNTY_VT + - 961 # LAMOILLE_COUNTY_VT + - 944 # ORANGE_COUNTY_VT + - 766 # ORLEANS_COUNTY_VT + - 977 # RUTLAND_COUNTY_VT + - 912 # WASHINGTON_COUNTY_VT + - 881 # WINDHAM_COUNTY_VT + - 908 # WINDSOR_COUNTY_VT + +- name: 2023 zero-exemption renter uses HUD zero-bedroom rents in all Vermont counties + period: 2023 + input: + state_code: + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + county: + - ADDISON_COUNTY_VT + - BENNINGTON_COUNTY_VT + - CALEDONIA_COUNTY_VT + - CHITTENDEN_COUNTY_VT + - ESSEX_COUNTY_VT + - FRANKLIN_COUNTY_VT + - GRAND_ISLE_COUNTY_VT + - LAMOILLE_COUNTY_VT + - ORANGE_COUNTY_VT + - ORLEANS_COUNTY_VT + - RUTLAND_COUNTY_VT + - WASHINGTON_COUNTY_VT + - WINDHAM_COUNTY_VT + - WINDSOR_COUNTY_VT + tax_unit_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + exemptions_count: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vt_renter_credit_income: [5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000] + rent_is_shared_with_another_tax_unit: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + housing_assistance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + output: + # Each HUD monthly 0BR amount x 12 x 10%, rounded half up. + # Franklin and Grand Isle use the average of Vermont's 12 HUD areas. + vt_renter_credit: + - 1010 # ADDISON_COUNTY_VT + - 1019 # BENNINGTON_COUNTY_VT + - 972 # CALEDONIA_COUNTY_VT + - 1367 # CHITTENDEN_COUNTY_VT + - 876 # ESSEX_COUNTY_VT + - 1019 # FRANKLIN_COUNTY_VT + - 1019 # GRAND_ISLE_COUNTY_VT + - 1049 # LAMOILLE_COUNTY_VT + - 1022 # ORANGE_COUNTY_VT + - 847 # ORLEANS_COUNTY_VT + - 1085 # RUTLAND_COUNTY_VT + - 1007 # WASHINGTON_COUNTY_VT + - 964 # WINDHAM_COUNTY_VT + - 1009 # WINDSOR_COUNTY_VT + +- name: 2024 zero-exemption renter uses HUD zero-bedroom rents in all Vermont counties + period: 2024 + input: + state_code: + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + county: + - ADDISON_COUNTY_VT + - BENNINGTON_COUNTY_VT + - CALEDONIA_COUNTY_VT + - CHITTENDEN_COUNTY_VT + - ESSEX_COUNTY_VT + - FRANKLIN_COUNTY_VT + - GRAND_ISLE_COUNTY_VT + - LAMOILLE_COUNTY_VT + - ORANGE_COUNTY_VT + - ORLEANS_COUNTY_VT + - RUTLAND_COUNTY_VT + - WASHINGTON_COUNTY_VT + - WINDHAM_COUNTY_VT + - WINDSOR_COUNTY_VT + tax_unit_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + exemptions_count: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vt_renter_credit_income: [5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000] + rent_is_shared_with_another_tax_unit: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + housing_assistance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + output: + # Each HUD monthly 0BR amount x 12 x 10%, rounded half up. + # Franklin and Grand Isle use the average of Vermont's 12 HUD areas. + vt_renter_credit: + - 1220 # ADDISON_COUNTY_VT + - 1123 # BENNINGTON_COUNTY_VT + - 1079 # CALEDONIA_COUNTY_VT + - 1609 # CHITTENDEN_COUNTY_VT + - 974 # ESSEX_COUNTY_VT + - 1172 # FRANKLIN_COUNTY_VT + - 1172 # GRAND_ISLE_COUNTY_VT + - 1096 # LAMOILLE_COUNTY_VT + - 1171 # ORANGE_COUNTY_VT + - 997 # ORLEANS_COUNTY_VT + - 1189 # RUTLAND_COUNTY_VT + - 1182 # WASHINGTON_COUNTY_VT + - 1171 # WINDHAM_COUNTY_VT + - 1247 # WINDSOR_COUNTY_VT + +- name: 2025 zero-exemption renter uses HUD zero-bedroom rents in all Vermont counties + period: 2025 + input: + state_code: + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + - VT + county: + - ADDISON_COUNTY_VT + - BENNINGTON_COUNTY_VT + - CALEDONIA_COUNTY_VT + - CHITTENDEN_COUNTY_VT + - ESSEX_COUNTY_VT + - FRANKLIN_COUNTY_VT + - GRAND_ISLE_COUNTY_VT + - LAMOILLE_COUNTY_VT + - ORANGE_COUNTY_VT + - ORLEANS_COUNTY_VT + - RUTLAND_COUNTY_VT + - WASHINGTON_COUNTY_VT + - WINDHAM_COUNTY_VT + - WINDSOR_COUNTY_VT + tax_unit_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + exemptions_count: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + vt_renter_credit_income: [5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000, 5_000] + rent_is_shared_with_another_tax_unit: [false, false, false, false, false, false, false, false, false, false, false, false, false, false] + housing_assistance: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + output: + # Each HUD monthly 0BR amount x 12 x 10%, rounded half up. + # Franklin and Grand Isle use the average of Vermont's 12 HUD areas. + vt_renter_credit: + - 1315 # ADDISON_COUNTY_VT + - 1123 # BENNINGTON_COUNTY_VT + - 1086 # CALEDONIA_COUNTY_VT + - 1656 # CHITTENDEN_COUNTY_VT + - 988 # ESSEX_COUNTY_VT + - 1209 # FRANKLIN_COUNTY_VT + - 1209 # GRAND_ISLE_COUNTY_VT + - 1060 # LAMOILLE_COUNTY_VT + - 1216 # ORANGE_COUNTY_VT + - 1014 # ORLEANS_COUNTY_VT + - 1190 # RUTLAND_COUNTY_VT + - 1290 # WASHINGTON_COUNTY_VT + - 1224 # WINDHAM_COUNTY_VT + - 1349 # WINDSOR_COUNTY_VT + +- name: 2025 Vermont renter income limits count both resident spouses when one spouse is claimed + period: 2025 + input: + people: + unclaimed_spouse: + age: 30 + rent: 6_000 + is_tax_unit_head: true + is_tax_unit_spouse: false + claimed_spouse: + age: 30 + rent: 6_000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + tax_units: + tax_unit: + members: [unclaimed_spouse, claimed_spouse] + filing_status: JOINT + vt_renter_credit_income: 62_000 + households: + household: + members: [unclaimed_spouse, claimed_spouse] + state_code: VT + county: CHITTENDEN_COUNTY_VT + output: + exemptions_count: 1 + tax_unit_size: 2 + # Resident-family income limits use two people; the fair market rent + # uses one personal exemption. The alternative of one-person income + # limits would deny the credit at this income. + vt_renter_credit: 270 diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py index 0800d12f74b..6c0c1c606bb 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/renter/vt_renter_credit.py @@ -29,7 +29,7 @@ def formula(tax_unit, period, parameters): # bedrooms equal to the number of personal exemptions" under # 5811(21)(C), which allows none for a filer who can be claimed as a # dependent and no dependents on a return where either filer can be. - exemptions = clip(tax_unit("exemptions_count", period), 1, 8) + exemptions = clip(tax_unit("exemptions_count", period), 0, 8) # locate the values by family size and county full_credit_income_limit = p.income_limit_ami.thirty_percent[tax_unit_size][ county From 71b1238d026fefca0f5c208846af75a8f14202f0 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 01:28:10 -0400 Subject: [PATCH 36/42] Complete claimable-filer coverage and reference corrections --- ...able_filer_ri_sc_ut_vt_wv_wi_invariants.py | 27 ++++- .../credits/rebates/ri_child_tax_rebate.yaml | 27 +++++ .../ut_at_home_parent_credit_potential.yaml | 83 ++++++++++++++ .../states/vt/tax/income/credits/vt_ctc.yaml | 28 +++++ ...wi_childcare_expense_credit_potential.yaml | 102 +++++++++++++++++ .../wi_childcare_expense_subtraction.yaml | 103 ++++++++++++++++++ .../wi/tax/income/wi_homestead_income.yaml | 31 ++++++ .../ut_dependent_exemption.yaml | 29 +++++ .../ri_property_tax_credit_eligible.py | 4 +- .../wi_childcare_expense_credit_potential.py | 5 +- .../wi_childcare_expense_subtraction.py | 5 +- 11 files changed, 437 insertions(+), 7 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_credit_potential.yaml diff --git a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py index 7f5452a42a6..a4807088d67 100644 --- a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py @@ -38,6 +38,7 @@ _seeded_couples, adults, dependents, + money, ) STATES = ["RI", "SC", "UT", "VT", "WV", "WI"] @@ -73,13 +74,27 @@ ] +@st.composite +def adults_with_retirement_income(draw): + # Exercise the positive Wisconsin retirement-exclusion path as well as + # earnings-only returns, without changing the other states' generators. + return { + **draw(adults()), + "taxable_pension_income": draw(money(40_000)), + "taxable_ira_distributions": draw(money(20_000)), + } + + @st.composite def couples(draw): claimable = draw(st.sampled_from(CLAIM_PATTERNS)) claimed = tuple(can and draw(st.booleans()) for can in claimable) return { "state": draw(st.sampled_from(STATES)), - "adults": [draw(adults()), draw(adults())], + "adults": [ + draw(adults_with_retirement_income()), + draw(adults_with_retirement_income()), + ], "dependents": draw(st.lists(dependents(), max_size=2)), "claimed": claimed, "claimable": claimable, @@ -132,5 +147,15 @@ def test_ri_sc_ut_vt_wv_wi_claimable_filer_rules_are_label_free(year, units): @pytest.mark.parametrize("year", YEARS) def test_ri_sc_ut_vt_wv_wi_claimable_filer_rules_seeded_population(year): units = _seeded_couples(STATES, per_state=2) + _crafted_cases(STATES) + rng = np.random.default_rng(20261019) + for unit in units: + unit["adults"] = [ + { + **adult, + "taxable_pension_income": float(rng.choice([0, 8_000, 30_000])), + "taxable_ira_distributions": float(rng.choice([0, 4_000, 12_000])), + } + for adult in unit["adults"] + ] check_swap(units, year, OUTPUTS, _identities) check_monotone(units, year, MONOTONE, _identities) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml index c59f55ea156..95f0cd279d6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ri/tax/income/credits/rebates/ri_child_tax_rebate.yaml @@ -338,3 +338,30 @@ output: # One child aged 4: min(1, 3) x $250. ri_child_tax_rebate: 250 + +# Publication 501 claimant filing exception restores the valid dependent. + +- name: 2021 claimable but unclaimed filer whose claimant meets the filing exception keeps the Rhode Island + child rebate + period: 2021 + input: + people: + filer: + age: 22 + employment_income: 8000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: RI + output: + ri_child_tax_rebate: 250 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml index 8b4660f1127..1ca288b7054 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.yaml @@ -134,3 +134,86 @@ # One child under 1 x $100 x one parent earning $3,000 or less # (the other parent earns $4,000); AGI $6,000 is under $50,000. ut_at_home_parent_credit_potential: 100 + +- name: '2025 joint return with both spouses claimable: no Utah at-home parent credit (head and spouse + labels swapped)' + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 2000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: false + is_tax_unit_spouse: true + person_b: + age: 24 + employment_income: 4000 + claimed_as_dependent_on_another_return: true + is_tax_unit_head: true + is_tax_unit_spouse: false + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + ut_at_home_parent_credit_potential: 0 + +- name: '2025 joint return with neither spouse claimable (control): Utah at-home parent credit (head and + spouse labels swapped)' + period: 2025 + input: + people: + person_a: + age: 22 + employment_income: 2000 + is_tax_unit_head: false + is_tax_unit_spouse: true + person_b: + age: 24 + employment_income: 4000 + is_tax_unit_head: true + is_tax_unit_spouse: false + newborn: + age: 0 + tax_units: + tax_unit: + members: [person_a, person_b, newborn] + filing_status: JOINT + households: + household: + members: [person_a, person_b, newborn] + state_code: UT + output: + ut_at_home_parent_credit_potential: 100 + +- name: 2025 claimable but unclaimed filer whose claimant meets the filing exception keeps the Utah at-home + parent credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 2000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 0 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: UT + output: + ut_at_home_parent_credit_potential: 100 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index d741ea0fb98..83bce6849c1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -85,6 +85,7 @@ vt_ctc: 500 # S.51 (2025) tests for expanded age eligibility + - name: S.51 - CTC includes 6-year-olds starting 2025 period: 2025 input: @@ -319,3 +320,30 @@ output: # 5830f counts qualifying children under IRC 152(c), who are dependents. vt_ctc: 0 + +# IRC 152(b)(1) does not bar dependents under the claimant filing exception. + +- name: 2025 claimable but unclaimed filer whose claimant meets the filing exception keeps the Vermont + child tax credit + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 2000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 2 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: VT + output: + vt_ctc: 1000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_credit_potential.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_credit_potential.yaml new file mode 100644 index 00000000000..1b301608797 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_credit_potential.yaml @@ -0,0 +1,102 @@ +# Disabled-adult care remains eligible when the filer can be claimed. +# The $2,000 childcare is excluded unless the filing exception applies +# or the child is incapable of self-care (IRC 21(b)(1)(B)). + +- name: 2025 claimable filer keeps adult care and only qualifying childcare + period: 2025 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: false + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_credit_potential: 200.0 + +- name: 2025 filing exception filer keeps adult care and only qualifying childcare + period: 2025 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: false + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_credit_potential: 600.0 + +- name: 2025 incapable child filer keeps adult care and only qualifying childcare + period: 2025 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: true + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_credit_potential: 600.0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_subtraction.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_subtraction.yaml index fc6bfbf91c6..3feb4d685d5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_subtraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_childcare_expense_subtraction.yaml @@ -79,3 +79,106 @@ state_code: WI output: wi_childcare_expense_subtraction: 2_000 + +# Disabled-adult care remains eligible when the filer can be claimed. +# The $2,000 childcare is excluded unless the filing exception applies +# or the child is incapable of self-care (IRC 21(b)(1)(B)). + +- name: 2021 claimable filer keeps adult care and only qualifying childcare + period: 2021 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: false + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_subtraction: 1000 + +- name: 2021 filing exception filer keeps adult care and only qualifying childcare + period: 2021 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: false + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_subtraction: 3000 + +- name: 2021 incapable child filer keeps adult care and only qualifying childcare + period: 2021 + absolute_error_margin: 0.01 + input: + people: + filer: + age: 22 + employment_income: 60000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: false + child: + age: 4 + is_tax_unit_dependent: true + is_incapable_of_self_care: true + adult: + age: 30 + is_tax_unit_spouse: false + is_tax_unit_dependent: true + is_incapable_of_self_care: true + care_expenses: 1000 + tax_units: + tax_unit: + members: [filer, child, adult] + tax_unit_childcare_expenses: 2000 + households: + household: + members: [filer, child, adult] + state_code: WI + output: + wi_childcare_expense_subtraction: 3000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index f6b09980a59..a979963d4ac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -101,6 +101,7 @@ wi_homestead_income: 5_000 # Hold TANF at zero to isolate the $500 dependent deduction on wage income. + - name: "2025 single filer who can be claimed as a dependent: no Wisconsin homestead $500 dependent deduction" period: 2025 input: @@ -271,3 +272,33 @@ # $10,000 - $500 = $9,500; ($1,000 - $1,440 x 8.785%) x 80% = $698.7968. wi_homestead_income: 9500 wi_homestead_credit: 698.7968 + +# $10,000 income less the restored $500 dependent deduction. + +- name: 2025 claimant filing exception preserves the Wisconsin homestead dependent deduction + period: 2025 + input: + people: + filer: + age: 22 + employment_income: 10000 + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + dependent_claimant_filing_exception: true + child: + age: 4 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [filer, child] + households: + household: + members: [filer, child] + state_code: WI + spm_units: + spm_unit: + members: [filer, child] + tanf: 0 + output: + wi_homestead_income: 9500 diff --git a/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml b/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml index 9e66390db99..ad1e6ca70af 100644 --- a/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml +++ b/policyengine_us/tests/policy/contrib/states/ut/dependent_exemption/ut_dependent_exemption.yaml @@ -257,3 +257,32 @@ output: # One dependent at the reform amount (no age limit): $3,000. ut_personal_exemption: 3_000 + +- name: Reform in effect, neither spouse claimable (control) (head and spouse labels swapped) + period: 2026 + reforms: policyengine_us.reforms.states.ut.dependent_exemption.ut_dependent_exemption_reform + input: + gov.contrib.states.ut.dependent_exemption.in_effect: true + gov.contrib.states.ut.dependent_exemption.amount: 3000 + people: + person_a: + age: 20 + is_tax_unit_head: false + is_tax_unit_spouse: true + person_b: + age: 22 + is_tax_unit_spouse: false + is_tax_unit_head: true + child1: + age: 5 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person_a, person_b, child1] + filing_status: JOINT + households: + household: + members: [person_a, person_b, child1] + state_code: UT + output: + ut_personal_exemption: 3000 diff --git a/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py b/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py index 0567f3df8f0..3294148b8a8 100644 --- a/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py +++ b/policyengine_us/variables/gov/states/ri/tax/income/credits/property_tax/ri_property_tax_credit_eligible.py @@ -16,8 +16,8 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ri.tax.income.credits.property_tax person = tax_unit.members # R.I. Gen. Laws 44-33-3: the claimant is a homeowner or renter aged - # 65 or older or disabled, and "does not include any person claimed as - # a dependent by any taxpayer". Members of a household choose who + # 65 or older or disabled, and "shall not mean or include any person + # claimed as a dependent by any taxpayer". Members of a household choose who # claims, so either spouse may be the claimant, but the age or # disability and the dependency test belong to the same person. filer = person("is_tax_unit_head_or_spouse", period) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py index 749e29a9271..a296d445201 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/childcare_expense/wi_childcare_expense_credit_potential.py @@ -32,8 +32,9 @@ def formula(tax_unit, period, parameters): # applies: childcare plus care for a disabled adult qualifying # individual (care_expenses). # Only childcare for a qualifying individual counts (IRC - # 21(b)(2)(A)(ii)): a return whose filer can be claimed has no - # qualifying child under 13 (152(b)(1)), so its childcare drops out. + # 21(b)(2)(A)(ii)): outside the claimant filing exception, a + # return whose filer can be claimed has no qualifying child + # under 13 (152(b)(1)), unless incapable of self-care. childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) raw_expenses = childcare + adult_care diff --git a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py index f6b9b9e0d6d..47feef772ce 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/subtractions/wi_childcare_expense_subtraction.py @@ -20,8 +20,9 @@ def formula(tax_unit, period, parameters): # definition, which covers childcare plus care for a disabled adult # qualifying individual (care_expenses). # Only childcare for a qualifying individual counts (IRC - # 21(b)(2)(A)(ii)): a return whose filer can be claimed has no - # qualifying child under 13 (152(b)(1)), so its childcare drops out. + # 21(b)(2)(A)(ii)): outside the claimant filing exception, a return + # whose filer can be claimed has no qualifying child under 13 + # (152(b)(1)), unless the child is incapable of self-care. childcare = tax_unit("cdcc_qualifying_childcare_expenses", period) adult_care = add(tax_unit, period, ["care_expenses"]) uncapped_expenses = childcare + adult_care From 2581ede702d9ee6ecc89f966a047796760e50582 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 03:48:21 -0400 Subject: [PATCH 37/42] Preserve claimable filer exemptions in RI and WI reforms --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 +- .../repeal_state_dependent_exemptions.py | 12 +- .../ri/exemption/ri_exemption_reform.py | 26 +- .../repeal_state_dependent_exemptions.yaml | 163 ++++++++++ .../states/ri/exemption_reform_test.yaml | 292 ++++++++++++++++++ 5 files changed, 479 insertions(+), 16 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index 30525e4d025..7143f2cf39e 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1,6 +1,6 @@ Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent: dependent exemptions, deductions and child credits on such a return, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, the Rhode Island and Vermont claimant tests for property tax relief and the renter credit, and Wisconsin's care subtraction and credit expenses. -Preserve claimable-filer rules in the contributed South Carolina, Vermont and West Virginia dependent exemption reforms and the state dependent exemption repeal. +Preserve claimable-filer counts and the dependent-claimant filing exception in the contributed Rhode Island, South Carolina, Utah, Vermont and West Virginia exemption reforms. Preserve the Rhode Island and Wisconsin filer counts and West Virginia zero-exemption amount under the state dependent exemption repeal. Exclude pretax payroll deductions from Wisconsin dependent standard deduction earnings on both tax paths. diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index eaf11854a5e..3295ce57ba9 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -152,7 +152,10 @@ class ri_exemptions(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ri.tax.income.exemption - exemptions_count = tax_unit("head_spouse_count", period) + # Repeal removes dependents, preserving the baseline filer count. + exemptions_count = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) exemption_amount = exemptions_count * p.amount @@ -377,7 +380,6 @@ def formula(tax_unit, period, parameters): ) return exempt_status.base[filing_status] + personal_exemptions_added - # Using head and spouse count instead of exemptions count class wi_base_exemption(Variable): value_type = float entity = TaxUnit @@ -396,7 +398,11 @@ class wi_base_exemption(Variable): def formula(tax_unit, period, parameters): # compute base exemption amount p = parameters(period).gov.states.wi.tax.income - return tax_unit("head_spouse_count", period) * p.exemption.base + # Repeal removes dependents, preserving the baseline filer count. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period + ) + return own_exemptions * p.exemption.base class de_personal_credit(Variable): value_type = float diff --git a/policyengine_us/reforms/states/ri/exemption/ri_exemption_reform.py b/policyengine_us/reforms/states/ri/exemption/ri_exemption_reform.py index edd25e32b79..ecf00256286 100644 --- a/policyengine_us/reforms/states/ri/exemption/ri_exemption_reform.py +++ b/policyengine_us/reforms/states/ri/exemption/ri_exemption_reform.py @@ -16,7 +16,10 @@ def formula(tax_unit, period, parameters): person = tax_unit.members age = person("age", period) - is_dependent = person("is_tax_unit_dependent", period) + # Retain the baseline dependent taxpayer test and filing exception. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) # Apply age limit if in effect if p.age_limit.in_effect: @@ -83,7 +86,10 @@ class ri_older_dependents_count(Variable): def formula(tax_unit, period, parameters): person = tax_unit.members - is_dependent = person("is_tax_unit_dependent", period) + # Retain the baseline dependent taxpayer test and filing exception. + is_dependent = person("is_tax_unit_dependent", period) & ~person.tax_unit( + "head_or_spouse_is_dependent_elsewhere_without_filing_exception", period + ) total_dependents = tax_unit.sum(is_dependent) eligible_dependent_exemptions = tax_unit( "ri_eligible_dependents_count", period @@ -101,17 +107,13 @@ class ri_exemptions(Variable): def formula(tax_unit, period, parameters): p_base = parameters(period).gov.states.ri.tax.income.exemption - # Calculate personal exemptions base amount - filing_status = tax_unit("filing_status", period) - older_dependents = tax_unit("ri_older_dependents_count", period) - personal_exemptions = ( - where( - filing_status == filing_status.possible_values.JOINT, - 2, - 1, - ) - + older_dependents + # Preserve the baseline exclusion of claimable filers, even when + # the filing exception permits them to claim their dependents. + own_exemptions = tax_unit( + "head_spouse_count_not_dependent_elsewhere", period ) + older_dependents = tax_unit("ri_older_dependents_count", period) + personal_exemptions = own_exemptions + older_dependents personal_exemption_base = personal_exemptions * p_base.amount # Apply baseline phase-out to personal exemptions diff --git a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml index ee04221c61d..66559352c17 100644 --- a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml +++ b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml @@ -194,3 +194,166 @@ tax_units: {tax_unit: {members: [parent], filing_status: SINGLE}} households: {household: {members: [parent], state_name: WV}} output: {wv_personal_exemption: 500} + +- name: WI repeal - single filer claimed - baseline + period: 2025 + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: WI repeal - single filer claimed - reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: WI repeal - both joint filers claimed - baseline + period: 2025 + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head, spouse], filing_status: JOINT}} + households: {household: {members: [head, spouse], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: WI repeal - both joint filers claimed - reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head, spouse], filing_status: JOINT}} + households: {household: {members: [head, spouse], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: RI repeal - single filer claimed - baseline + period: 2025 + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI repeal - single filer claimed - reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: WI repeal - single filer unclaimed but claimable - baseline + period: 2025 + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: WI repeal - single filer unclaimed but claimable - reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: WI}} + output: + exemptions_count: 0 + wi_base_exemption: 0 + +- name: RI repeal - single filer unclaimed but claimable - baseline + period: 2025 + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI repeal - single filer unclaimed but claimable - reform + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + tax_units: {tax_unit: {members: [head], filing_status: SINGLE}} + households: {household: {members: [head], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 diff --git a/policyengine_us/tests/policy/contrib/states/ri/exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/ri/exemption_reform_test.yaml index 0349abe6f45..7afc6a0208b 100644 --- a/policyengine_us/tests/policy/contrib/states/ri/exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/ri/exemption_reform_test.yaml @@ -115,3 +115,295 @@ # Dependent exemption: $0 (set to $0 in reform) # Final: $0 + $0 = $0 ri_exemptions: 0 + +- name: RI exemption reform - young dependent of claimed filer - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - young dependent of claimed filer - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + ri_eligible_dependents_count: 0 + ri_older_dependents_count: 0 + +- name: RI exemption reform - young dependent of unclaimed but claimable filer - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - young dependent of unclaimed but claimable filer - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + ri_eligible_dependents_count: 0 + ri_older_dependents_count: 0 + +- name: RI exemption reform - older dependent of claimed filer - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + gov.contrib.states.ri.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - older dependent of claimed filer - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: true + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + ri_eligible_dependents_count: 0 + ri_older_dependents_count: 0 + +- name: RI exemption reform - older dependent of unclaimed but claimable filer - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + gov.contrib.states.ri.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - older dependent of unclaimed but claimable filer - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.in_effect: true + gov.contrib.states.ri.dependent_exemption.age_limit.threshold: 18 + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child: {age: 20, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + ri_eligible_dependents_count: 0 + ri_older_dependents_count: 0 + +- name: RI exemption reform - joint return with only head claimable - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 + +- name: RI exemption reform - joint return with only head claimable - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: false + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 + +- name: RI exemption reform - joint return with only spouse claimable - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: false + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 + +- name: RI exemption reform - joint return with only spouse claimable - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: false + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 + +- name: RI exemption reform - joint return with both filers claimable - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - joint return with both filers claimable - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + head: + age: 22 + claimable_as_dependent_on_another_return: true + spouse: + age: 22 + claimable_as_dependent_on_another_return: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [head, spouse, child], filing_status: JOINT}} + households: {household: {members: [head, spouse, child], state_name: RI}} + output: + exemptions_count: 0 + ri_exemptions: 0 + +- name: RI exemption reform - dependent claimant filing exception preserves child exemption - baseline + period: 2025 + input: + gov.contrib.states.ri.dependent_exemption.in_effect: false + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 + +- name: RI exemption reform - dependent claimant filing exception preserves child exemption - reform + period: 2025 + reforms: policyengine_us.reforms.states.ri.exemption.ri_exemption_reform.ri_exemption_reform + input: + gov.contrib.states.ri.dependent_exemption.in_effect: true + people: + parent: + age: 22 + claimable_as_dependent_on_another_return: true + dependent_claimant_filing_exception: true + child: {age: 5, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent, child], filing_status: SINGLE}} + households: {household: {members: [parent, child], state_name: RI}} + output: + exemptions_count: 1 + ri_exemptions: 5_100 From a925f5fa28a83c86cdb47fc03cf80e69dff33347 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 03:48:22 -0400 Subject: [PATCH 38/42] Cover joint claimable filers in West Virginia exemption reform --- .../wv/dependent_exemption_reform_test.yaml | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml index d29f7f86de2..1fdd0e3c2f8 100644 --- a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml @@ -213,3 +213,87 @@ wv_personal_exemption: 2_000 wv_eligible_dependents_count: 1 wv_older_dependents_count: 0 + +- name: WV baseline - joint return with one claimable spouse and two children + period: 2025 + input: + people: + parent1: {age: 40, employment_income: 60_000} + parent2: + age: 38 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent1, parent2, child1, child2], filing_status: JOINT}} + households: {household: {members: [parent1, parent2, child1, child2], state_name: WV}} + output: + exemptions_count: 1 + wv_personal_exemption: 2_000 + +- name: WV dependent exemption reform - joint return with one claimable spouse and two children + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + people: + parent1: {age: 40, employment_income: 60_000} + parent2: + age: 38 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent1, parent2, child1, child2], filing_status: JOINT}} + households: {household: {members: [parent1, parent2, child1, child2], state_name: WV}} + output: + exemptions_count: 1 + wv_personal_exemption: 2_000 + wv_eligible_dependents_count: 0 + wv_older_dependents_count: 0 + +- name: WV baseline - joint return with both spouses claimable and two children + period: 2025 + input: + people: + parent1: + age: 40 + employment_income: 60_000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + parent2: + age: 38 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent1, parent2, child1, child2], filing_status: JOINT}} + households: {household: {members: [parent1, parent2, child1, child2], state_name: WV}} + output: + exemptions_count: 0 + wv_personal_exemption: 500 + +- name: WV dependent exemption reform - joint return with both spouses claimable and two children + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + people: + parent1: + age: 40 + employment_income: 60_000 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + parent2: + age: 38 + claimable_as_dependent_on_another_return: true + claimed_as_dependent_on_another_return: false + child1: {age: 10, is_tax_unit_dependent: true} + child2: {age: 12, is_tax_unit_dependent: true} + tax_units: {tax_unit: {members: [parent1, parent2, child1, child2], filing_status: JOINT}} + households: {household: {members: [parent1, parent2, child1, child2], state_name: WV}} + output: + exemptions_count: 0 + wv_personal_exemption: 500 + wv_eligible_dependents_count: 0 + wv_older_dependents_count: 0 From d90971a331ed3ab3a7c87e0c47b4b3ff5bc05469 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 03:48:22 -0400 Subject: [PATCH 39/42] Disclose Vermont zero bedroom averaging alternatives --- .../vt/tax/income/credits/renter/fair_market_rent.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml index d782bfb4d03..0dfc6f7cc62 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/fair_market_rent.yaml @@ -79,6 +79,14 @@ metadata: # following the state-average rule in 6061(7)(C). This is the averaging convention # that reproduces the department's unrounded 2025 rents. These zero-bedroom # values are calculated from HUD schedules rather than department credit tables. +# The unrounded 12-area method is used in every year. It differs from the +# existing 1-4 bedroom reconstructions in 2021 and 2024 (whole-dollar rounding) +# and 2022 (Addison excluded). Applying those alternatives to zero bedrooms +# gives full-year credits of $883 either way in 2021, $919 versus $918 in 2022, +# and $1,172 versus $1,171 in 2024 (this method first); 2023 and 2025 agree. +# A 14-county average instead weights the Burlington area three times and +# gives a 2025 zero-bedroom credit of $1,273 versus this method's $1,209. +# Section 6061(7)(C) does not specify averaging weights or intermediate rounding. 0: ADDISON_COUNTY_VT: 2021-01-01: 751 From 15a794021e969bc6751d3eb7ecc8a97a09a4a637 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 19 Oct 2026 03:48:23 -0400 Subject: [PATCH 40/42] Keep claimable filer case comments attached to their tests --- .../baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml | 1 - .../wi/tax/income/wi_retirement_income_exclusion_tax.yaml | 5 ----- 2 files changed, 6 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index 83bce6849c1..8b766141910 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -85,7 +85,6 @@ vt_ctc: 500 # S.51 (2025) tests for expanded age eligibility - - name: S.51 - CTC includes 6-year-olds starting 2025 period: 2025 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml index 7d5c11c3d92..ee9f2d1e2cf 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_retirement_income_exclusion_tax.yaml @@ -95,7 +95,6 @@ # Regression for issue #8817: the Line 16 subtraction must reduce WI income # BEFORE the standard-deduction phaseout, so a large exclusion recovers # standard deduction that the pre-subtraction income had phased out. - - name: E2E - Joint 67+ with retirement income above the standard-deduction phaseout absolute_error_margin: 1 period: 2025 @@ -132,7 +131,6 @@ # Below the single phaseout start ($19,550, 2025) the standard deduction is at # its max for both full and exclusion-reduced income, so the extra deduction the # fix unlocks is $0 (no regression vs. a plain taxinc - line16 subtraction). - - name: Exclusion path - below phaseout start, extra standard deduction is zero (no regression) absolute_error_margin: 0.01 period: 2025 @@ -152,7 +150,6 @@ # Fully phased-out top end (age 85, very high income): SD = $0 for both full and # reduced income, so extra_SD = 0 and the exclusion is a plain subtraction. - - name: Exclusion path - fully phased-out top end, extra standard deduction is zero absolute_error_margin: 0.01 period: 2025 @@ -173,7 +170,6 @@ # Partial-unlock boundary: full income ($140k) is past the single full-phaseout # point (SD_full = 0) while reduced income ($116k) lands INSIDE the phaseout, so # the fix unlocks a positive partial deduction. - - name: Exclusion path - partial unlock (full income past phaseout end, reduced income inside it) absolute_error_margin: 1 period: 2025 @@ -194,7 +190,6 @@ # Joint with only ONE spouse age 67+: the exclusion uses the per-person single # cap ($24,000), NOT the $48,000 both-eligible joint pool. - - name: Exclusion path - joint with one spouse 67+, uses the $24,000 per-person cap absolute_error_margin: 0.01 period: 2025 From a4b2cf4b59121142c4c1c5edcaba92f603eea2e6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 20 Oct 2026 17:25:54 -0400 Subject: [PATCH 41/42] Apply West Virginia per-filer statutory exemption in childless units --- ...claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md | 2 +- .../tax/income/exemptions/base_personal.yaml | 6 +- .../repeal_state_dependent_exemptions.py | 18 ++- .../wv_dependent_exemption_reform.py | 22 ++- ...able_filer_ri_sc_ut_vt_wv_wi_invariants.py | 18 ++- .../exemptions/wv_personal_exemption.yaml | 125 +++++++++++++++++- .../repeal_state_dependent_exemptions.yaml | 121 +++++++++++++++++ .../wv/dependent_exemption_reform_test.yaml | 125 ++++++++++++++++++ .../exemptions/wv_personal_exemption.py | 28 ++-- 9 files changed, 437 insertions(+), 28 deletions(-) diff --git a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md index 7143f2cf39e..35b9bb42977 100644 --- a/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md +++ b/changelog.d/claimable-filer-ri-sc-ut-vt-wv-wi.fixed.md @@ -1,6 +1,6 @@ Apply the Rhode Island, South Carolina, Utah, Vermont, West Virginia and Wisconsin rules for a filer who can be claimed as a dependent: dependent exemptions, deductions and child credits on such a return, the Wisconsin dependent standard deduction and $250 exemption, the West Virginia exemptions and family credit, the Rhode Island and Vermont claimant tests for property tax relief and the renter credit, and Wisconsin's care subtraction and credit expenses. -Preserve claimable-filer counts and the dependent-claimant filing exception in the contributed Rhode Island, South Carolina, Utah, Vermont and West Virginia exemption reforms. Preserve the Rhode Island and Wisconsin filer counts and West Virginia zero-exemption amount under the state dependent exemption repeal. +Preserve claimable-filer counts and the dependent-claimant filing exception in the contributed Rhode Island, South Carolina, Utah, Vermont and West Virginia exemption reforms. Preserve the Rhode Island and Wisconsin filer counts under the state dependent exemption repeal. For West Virginia units without dependents, preserve the statutory $500 allowance per claimable filer in the baseline, contributed reform and repeal override; retain existing dependent allocation pending resolution. Exclude pretax payroll deductions from Wisconsin dependent standard deduction earnings on both tax paths. diff --git a/policyengine_us/parameters/gov/states/wv/tax/income/exemptions/base_personal.yaml b/policyengine_us/parameters/gov/states/wv/tax/income/exemptions/base_personal.yaml index ac4a5c8f5a7..25d6dd1304f 100644 --- a/policyengine_us/parameters/gov/states/wv/tax/income/exemptions/base_personal.yaml +++ b/policyengine_us/parameters/gov/states/wv/tax/income/exemptions/base_personal.yaml @@ -1,12 +1,14 @@ -description: West Virginia provides an income tax exemption of this value for tax units with no exemptions claimed. +description: West Virginia provides this personal income tax exemption for each individual who is claimable as a dependent on another return. values: 1987-01-01: 500 metadata: period: year unit: currency-USD - label: West Virginia personal exemption + label: West Virginia claimable filer personal exemption reference: + - title: 1988 West Virginia H.B. 4472, enacted personal exemptions, pages 9-10 + href: https://code.wvlegislature.gov/signed_bills/1988/1988-RS-HB4472-SUB%20ENR_signed.pdf#page=9 - title: West Virginia Personal Income Tax Forms And Instructions 2025 href: https://tax.wv.gov/Documents/PIT/2025/it140.PersonalIncomeTaxFormsAndInstructions.2025.pdf#page=26 - title: West Virginia Personal Income Tax Forms And Instructions 2024 diff --git a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py index 3295ce57ba9..1d3a51c7332 100644 --- a/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py +++ b/policyengine_us/reforms/state_dependent_exemptions/repeal_state_dependent_exemptions.py @@ -218,14 +218,26 @@ class wv_personal_exemption(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wv.tax.income.exemptions - # Repealing dependent exemptions does not restore a claimable - # filer's own exemption. Retain IT-140's $500 zero-count amount. own_exemptions = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) - return where( + # Repeal preserves each childless claimable filer's statutory $500. + # Leave dependent-present allocation unchanged pending resolution. + person = tax_unit.members + claimable_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimable_as_dependent_on_another_return", period) + ) + filer_allowances = ( + p.personal * own_exemptions + p.base_personal * claimable_filers + ) + childless_claimable = (tax_unit("tax_unit_dependents", period) == 0) & ( + claimable_filers > 0 + ) + ordinary_amount = where( own_exemptions == 0, p.base_personal, p.personal * own_exemptions ) + return where(childless_claimable, filer_allowances, ordinary_amount) class ca_exemptions(Variable): value_type = float diff --git a/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py b/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py index 3af28ffb130..75739319d88 100644 --- a/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py +++ b/policyengine_us/reforms/states/wv/dependent_exemption/wv_dependent_exemption_reform.py @@ -73,8 +73,7 @@ class wv_personal_exemption(Variable): def formula(tax_unit, period, parameters): p_base = parameters(period).gov.states.wv.tax.income.exemptions - # Claimable filers have no own exemption, even when the filing - # exception permits them to claim a dependent. + # Count ordinary own exemptions separately from dependent eligibility. own_exemptions = tax_unit( "head_spouse_count_not_dependent_elsewhere", period ) @@ -85,14 +84,27 @@ def formula(tax_unit, period, parameters): # Add separated dependent exemption. dependent_exemption_amount = tax_unit("wv_dependent_exemption", period) - # IT-140 line 6 grants $500 once when there are no exemptions. - # Test the exemption count, independent of the reform's amount. + # Section 11-21-16(d) gives each claimable filer $500 in units + # without dependent members. Preserve existing allocation otherwise. + person = tax_unit.members + claimable_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimable_as_dependent_on_another_return", period) + ) + filer_allowances = ( + p_base.personal * own_exemptions + + p_base.base_personal * claimable_filers + ) + childless_claimable = (tax_unit("tax_unit_dependents", period) == 0) & ( + claimable_filers > 0 + ) exemptions = tax_unit("exemptions_count", period) - return where( + ordinary_amount = where( exemptions == 0, p_base.base_personal, personal_amount + dependent_exemption_amount, ) + return where(childless_claimable, filer_allowances, ordinary_amount) class reform(Reform): def apply(self): diff --git a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py index a4807088d67..ba9f165132d 100644 --- a/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_ri_sc_ut_vt_wv_wi_invariants.py @@ -18,8 +18,9 @@ exemption goes away. 3. Identities: the dependent-based amounts are zero on a return where the Dependent Taxpayer Test applies; a return whose every filer is actually - claimed gets no RI relief or VT renter credit; and a West Virginia return - with no exemptions gets the single $500. + claimed gets no RI relief or VT renter credit; and a childless West + Virginia return gets $500 per claimable filer and $2,000 per other filer. + Dependent-present West Virginia allocation retains the existing behavior. """ import numpy as np @@ -127,7 +128,18 @@ def _identities(sim, year): f"{name} in {year} is positive when every filer is claimed" ) state = np.asarray(sim.populations["tax_unit"].household("state_code_str", year)) - wv_none = (state == "WV") & (_calc(sim, "exemptions_count", year) == 0) + dependent_count = _calc(sim, "tax_unit_dependents", year) + wv_childless = (state == "WV") & (dependent_count == 0) + own = _calc(sim, "head_spouse_count_not_dependent_elsewhere", year) + np.testing.assert_array_equal( + _calc(sim, "wv_personal_exemption", year)[wv_childless], + (2_000 * own + 500 * (filers - own))[wv_childless], + ) + wv_none = ( + (state == "WV") + & (dependent_count > 0) + & (_calc(sim, "exemptions_count", year) == 0) + ) np.testing.assert_array_equal( _calc(sim, "wv_personal_exemption", year)[wv_none], 500 ) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml index 557b240cfa2..19e20f23aff 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/exemptions/wv_personal_exemption.yaml @@ -46,7 +46,7 @@ state_code: WV output: # W. Va. Code 11-21-16 and IRC 151(d)(2): no exemption for a filer who - # can be claimed; a return with no exemptions takes $500 instead. + # can be claimed; section 11-21-16(d) grants that individual $500. wv_personal_exemption: 500 - name: "2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the head)" @@ -73,8 +73,8 @@ members: [person_a, person_b] state_code: WV output: - # One exemption for the spouse who cannot be claimed: $2,000. - wv_personal_exemption: 2000 + # $2,000 for the nonclaimable spouse plus the claimable filer's $500. + wv_personal_exemption: 2500 - name: "2025 joint return with one claimable spouse: West Virginia personal exemption counts only exemptions that may be claimed (the claimable spouse is the spouse)" period: 2025 @@ -100,7 +100,7 @@ members: [person_b, person_a] state_code: WV output: - wv_personal_exemption: 2000 + wv_personal_exemption: 2500 - name: "2025 joint return with both spouses claimable: West Virginia personal exemption counts only exemptions that may be claimed" period: 2025 @@ -127,8 +127,8 @@ members: [person_a, person_b] state_code: WV output: - # No exemptions on the return: the $500 fallback, once per return. - wv_personal_exemption: 500 + # Section 11-21-16(d) grants each claimable spouse a $500 allowance. + wv_personal_exemption: 1000 - name: "2025 joint return with neither spouse claimable: West Virginia personal exemption counts only exemptions that may be claimed" period: 2025 @@ -263,3 +263,116 @@ output: # No own exemption; one dependent exemption x $2,000, so no $500. wv_personal_exemption: 2000 + +- name: WV statutory individual allowance - single claimable filer + period: 2025 + input: + people: + filer: + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - filer + filing_status: SINGLE + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - filer + state_code: WV + output: + wv_personal_exemption: 500 + wv_taxable_income: 29500 + +- name: WV statutory individual allowance - claimable head and nonclaimable spouse + period: 2025 + input: + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 + +- name: WV statutory individual allowance - two claimable spouses + period: 2025 + input: + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 1000 + wv_taxable_income: 29000 + +- name: WV statutory individual allowance - nonclaimable head and claimable spouse + period: 2025 + input: + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 diff --git a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml index 66559352c17..da8bbe43114 100644 --- a/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml +++ b/policyengine_us/tests/policy/contrib/state_dependent_exemptions/repeal_state_dependent_exemptions.yaml @@ -357,3 +357,124 @@ output: exemptions_count: 0 ri_exemptions: 0 + +- name: WV statutory individual allowance - single claimable filer + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + filer: + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - filer + filing_status: SINGLE + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - filer + state_code: WV + output: + wv_personal_exemption: 500 + wv_taxable_income: 29500 + +- name: WV statutory individual allowance - claimable head and nonclaimable spouse + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 + +- name: WV statutory individual allowance - two claimable spouses + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 1000 + wv_taxable_income: 29000 + +- name: WV statutory individual allowance - nonclaimable head and claimable spouse + period: 2025 + reforms: policyengine_us.reforms.state_dependent_exemptions.repeal_state_dependent_exemptions.repeal_state_dependent_exemptions + input: + gov.contrib.repeal_state_dependent_exemptions.in_effect: true + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 diff --git a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml index 1fdd0e3c2f8..13e9250e469 100644 --- a/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/wv/dependent_exemption_reform_test.yaml @@ -297,3 +297,128 @@ wv_personal_exemption: 500 wv_eligible_dependents_count: 0 wv_older_dependents_count: 0 + +- name: WV statutory individual allowance - single claimable filer + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + gov.contrib.states.wv.dependent_exemption.amount: 0 + people: + filer: + is_tax_unit_head: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - filer + filing_status: SINGLE + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - filer + state_code: WV + output: + wv_personal_exemption: 500 + wv_taxable_income: 29500 + +- name: WV statutory individual allowance - claimable head and nonclaimable spouse + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + gov.contrib.states.wv.dependent_exemption.amount: 0 + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: false + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 + +- name: WV statutory individual allowance - two claimable spouses + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + gov.contrib.states.wv.dependent_exemption.amount: 0 + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: true + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 1000 + wv_taxable_income: 29000 + +- name: WV statutory individual allowance - nonclaimable head and claimable spouse + period: 2025 + reforms: policyengine_us.reforms.states.wv.dependent_exemption.wv_dependent_exemption_reform.wv_dependent_exemption_reform + input: + gov.contrib.states.wv.dependent_exemption.in_effect: true + gov.contrib.states.wv.dependent_exemption.amount: 0 + people: + head: + is_tax_unit_head: true + is_tax_unit_spouse: false + claimable_as_dependent_on_another_return: false + spouse: + is_tax_unit_head: false + is_tax_unit_spouse: true + claimable_as_dependent_on_another_return: true + tax_units: + tax_unit: + members: + - head + - spouse + filing_status: JOINT + wv_agi: 30000 + wv_low_income_earned_income_exclusion: 0 + households: + household: + members: + - head + - spouse + state_code: WV + output: + wv_personal_exemption: 2500 + wv_taxable_income: 27500 diff --git a/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py b/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py index 4c729af5874..b9ea51c6fb2 100644 --- a/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py +++ b/policyengine_us/variables/gov/states/wv/tax/income/exemptions/wv_personal_exemption.py @@ -17,12 +17,24 @@ class wv_personal_exemption(Variable): def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wv.tax.income.exemptions - # W. Va. Code 11-21-16 allows $2,000 per federal exemption. The IT-140 - # instructions allow none for a filer who "can be claimed as a - # dependent on another person's return" and add: "You cannot claim - # any dependents if you can be claimed as a dependent" (IRC 151(d)(2), - # 152(b)(1)). 11-21-16(d) gives an individual whose federal exemption - # is zero under 151(d)(2) a single $500 exemption; the form applies it - # once per return: "If box e is zero, enter $500 on line 6". + # Section 11-21-16(d) grants each claimable individual $500. + # Limit this correction to units without dependent members: applying + # the single-exemption limit with dependents requires attribution. + person = tax_unit.members + claimable_filers = tax_unit.sum( + person("is_tax_unit_head_or_spouse", period) + & person("claimable_as_dependent_on_another_return", period) + ) + own_exemptions = tax_unit("head_spouse_count_not_dependent_elsewhere", period) + filer_allowances = ( + p.personal * own_exemptions + p.base_personal * claimable_filers + ) + childless_claimable = (tax_unit("tax_unit_dependents", period) == 0) & ( + claimable_filers > 0 + ) + # Preserve the existing dependent allocation pending resolution. exemptions = tax_unit("exemptions_count", period) - return where(exemptions == 0, p.base_personal, p.personal * exemptions) + ordinary_amount = where( + exemptions == 0, p.base_personal, p.personal * exemptions + ) + return where(childless_claimable, filer_allowances, ordinary_amount) From fc6bd175bb80f63e28606e111a4978b722127a21 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 20 Oct 2026 19:28:46 -0400 Subject: [PATCH 42/42] Align West Virginia credit witnesses with per-filer allowances --- ..._low_income_family_tax_credit_eligible.yaml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml index f1e5e340755..d6bf9d58de2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.yaml @@ -59,13 +59,15 @@ members: [person_a, person_b] state_code: WV output: - # Family size 1: 2025 FPG $15,650. Taxable income $16,000 - $2,000 = - # $14,000; tax $10,000 x 2.22% + $4,000 x 2.96% = $340.40. Income - # exceeds the FPG by $350, so the credit is 80%: $272.32. + # Family size 1: 2025 FPG $15,650. Taxable income $16,000 - $2,500 = + # $13,500; tax $10,000 x 2.22% + $3,500 x 2.96% = $325.60. Income + # exceeds the FPG by $350, so the credit is 80%: $260.48. wv_low_income_family_tax_credit_eligible: true wv_low_income_family_tax_credit_fpg: 15650 - wv_income_tax_before_non_refundable_credits: 340.4 - wv_low_income_family_tax_credit_potential: 272.32 + wv_personal_exemption: 2500 + wv_taxable_income: 13500 + wv_income_tax_before_non_refundable_credits: 325.6 + wv_low_income_family_tax_credit_potential: 260.48 - name: "2025 joint return with one claimable spouse: West Virginia low-income family tax credit uses the exemptions that may be claimed (the claimable spouse is the spouse)" period: 2025 @@ -93,8 +95,10 @@ output: wv_low_income_family_tax_credit_eligible: true wv_low_income_family_tax_credit_fpg: 15650 - wv_income_tax_before_non_refundable_credits: 340.4 - wv_low_income_family_tax_credit_potential: 272.32 + wv_personal_exemption: 2500 + wv_taxable_income: 13500 + wv_income_tax_before_non_refundable_credits: 325.6 + wv_low_income_family_tax_credit_potential: 260.48 - name: "2025 joint return with both spouses claimable: West Virginia low-income family tax credit uses the exemptions that may be claimed" period: 2025