diff --git a/changelog.d/eitc-mfs-separation-conditions.fixed.md b/changelog.d/eitc-mfs-separation-conditions.fixed.md new file mode 100644 index 00000000000..1556c9f36f8 --- /dev/null +++ b/changelog.d/eitc-mfs-separation-conditions.fixed.md @@ -0,0 +1 @@ +Require a resident qualifying child and the statutory separation conditions for married separate filers, and for separated head-of-household filers from 2021, to receive the federal EITC, CalEITC, and credits that follow the federal marital rule. A head or spouse marked as separated meets the six-month living-apart condition by default. A new `is_separated_under_decree` input (default false) treats a head or spouse separated under a decree of divorce or separate maintenance as unmarried for the EITC, under IRC 7703(a)(2). New Jersey's EITC, including its childless age fallback, now follows the federal marital condition and, under N.J.S.A. 54A:4-7a.(3), bars every married separate filer unless a decree applies. Head-of-household status for a separated filer now requires a child resident for more than half the year and six months apart from the spouse (IRC 7703(b)). Washington's Working Families Tax Credit no longer meets the childless age test through a dependent's age. diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/eligibility/separate_filer.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/eligibility/separate_filer.yaml index 4d25043ef51..43a85d7edcf 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/eligibility/separate_filer.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/eligibility/separate_filer.yaml @@ -1,13 +1,13 @@ -description: The US makes married filing separate filers eligible for the EITC when this is true. +description: The US enables the EITC exception to the joint-return requirement for separate filers with a resident qualifying child who meet the statutory separated-spouse conditions when this is true. metadata: unit: bool - label: EITC separate filers eligible + label: EITC separated-spouse exception enabled reference: - title: 26 U.S. Code § 32(d) - Earned income credit - href: https://www.law.cornell.edu/uscode/text/26/32#d + href: https://www.govinfo.gov/content/pkg/USCODE-2023-title26/pdf/USCODE-2023-title26-subtitleA-chap1-subchapA-partIV-subpartC-sec32.pdf#page=3 # ARPA changed it for 2021 forward. - title: American Rescue Plan Act Sec. 9623 - href: https://www.congress.gov/bill/117th-congress/house-bill/1319/text + href: https://www.congress.gov/117/bills/hr1319/BILLS-117hr1319enr.pdf#page=151 values: 2013-01-01: false 2021-01-01: true diff --git a/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py b/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py index b26290aa35e..db6db822e65 100644 --- a/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py +++ b/policyengine_us/reforms/states/ny/wftc/ny_working_families_tax_credit.py @@ -187,23 +187,15 @@ class younger_eitc_eligible(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/32#c_1_A" def formula(tax_unit, period, parameters): - eitc = parameters.gov.irs.credits.eitc(period) investment_income_eligible = tax_unit( "eitc_investment_income_eligible", period ) demographic_eligible = tax_unit("eitc_younger_demographic_eligible", period) no_older_children = tax_unit("eitc_older_children_count", period) == 0 - # Define eligibility before considering separate filer limitation. eligible = ( demographic_eligible & investment_income_eligible & no_older_children ) - # This parameter is true if separate filers are eligible. - if eitc.eligibility.separate_filer: - return eligible - # If separate filers are not eligible, check if the filer is separate. - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE - return eligible & ~separate + return eligible & tax_unit("eitc_marital_eligible", period) class younger_eitc(Variable): value_type = float @@ -382,25 +374,17 @@ class older_eitc_eligible(Variable): reference = "https://www.law.cornell.edu/uscode/text/26/32#c_1_A" def formula(tax_unit, period, parameters): - eitc = parameters.gov.irs.credits.eitc(period) investment_income_eligible = tax_unit( "eitc_investment_income_eligible", period ) demographic_eligible = tax_unit("eitc_older_demographic_eligible", period) younger_eitc_eligible = tax_unit("younger_eitc_eligible", period) - # Define eligibility before considering separate filer limitation. eligible = ( demographic_eligible & investment_income_eligible & ~younger_eitc_eligible ) - # This parameter is true if separate filers are eligible. - if eitc.eligibility.separate_filer: - return eligible - # If separate filers are not eligible, check if the filer is separate. - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE - return eligible & ~separate + return eligible & tax_unit("eitc_marital_eligible", period) class older_eitc(Variable): value_type = float diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_marital_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_marital_eligible.yaml new file mode 100644 index 00000000000..53565609f00 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/earned_income/eitc_marital_eligible.yaml @@ -0,0 +1,427 @@ +# IRC 32(d), as amended by ARPA section 9623, first applies to 2021. +- name: Joint filers do not need a child or separation facts before 2021 + period: 2020 + input: + filing_status: JOINT + is_eitc_qualifying_child: false + eitc_demographic_eligible: true + eitc_investment_income_eligible: true + filer_meets_eitc_identification_requirements: true + output: + eitc_marital_eligible: true + eitc_eligible: true + +- name: Joint filers do not need a child or separation facts after 2021 + period: 2025 + input: + filing_status: JOINT + is_eitc_qualifying_child: false + eitc_demographic_eligible: true + eitc_investment_income_eligible: true + filer_meets_eitc_identification_requirements: true + output: + eitc_marital_eligible: true + eitc_eligible: true + +- name: Single filers do not need a child or separation facts + period: 2025 + input: + filing_status: SINGLE + is_eitc_qualifying_child: false + output: + eitc_marital_eligible: true + +- name: MFS with a child and separation facts was ineligible in 2020 + period: 2020 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: true + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + output: + eitc_marital_eligible: false + +- name: MFS with a resident child first qualifies under the six-month route in 2021 + period: 2021 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: true + eitc_child_lived_with_taxpayer_more_than_half_year: true + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: false + output: + eitc_marital_eligible: true + +- name: MFS with a resident child first qualifies under the legal-instrument route in 2021 + period: 2021 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: true + eitc_child_lived_with_taxpayer_more_than_half_year: true + eitc_spouses_lived_apart_last_six_months: false + eitc_legal_separation_and_apart_at_year_end: true + output: + eitc_marital_eligible: true + +- name: A residence fact cannot replace the qualifying-child requirement + period: 2025 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: false + eitc_child_lived_with_taxpayer_more_than_half_year: true + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + output: + eitc_marital_eligible: false + +- name: Separation of the head defaults the six-month living-apart route + period: 2025 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: true + is_separated: true + output: + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: false + eitc_marital_eligible: true + +- name: The six-month living-apart input overrides the separation default + period: 2025 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: true + is_separated: true + eitc_spouses_lived_apart_last_six_months: false + output: + eitc_marital_eligible: false + +- name: Separation still requires a qualifying child + period: 2025 + input: + filing_status: SEPARATE + is_eitc_qualifying_child: false + is_separated: true + output: + eitc_spouses_lived_apart_last_six_months: true + eitc_marital_eligible: false + +- name: Separated MFS parent with a qualifying child receives the EITC + # IRC 32(d)(2)(B)(iii)(I): is_separated defaults the living-apart route. + # 2026, one child: 4_427 - 0.1598 x (30_000 - 23_890) = 3_450.62. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + households: + household: + members: [head, child] + state_code: TX + output: + eitc_spouses_lived_apart_last_six_months: true + eitc_marital_eligible: true + eitc_eligible: true + eitc: 3_450.62 + +- name: MFS parent with a qualifying child but no separation facts gets no EITC + # IRC 32(d)(1) requires a joint return from a married individual; neither + # route in 32(d)(2)(B)(iii) is established, so the exception doesn't apply. + period: 2026 + input: + people: + head: + age: 35 + employment_income: 30_000 + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + households: + household: + members: [head, child] + state_code: TX + output: + eitc_spouses_lived_apart_last_six_months: false + eitc_marital_eligible: false + eitc_eligible: false + eitc: 0 + +- name: MFS parent with a separation agreement and separate household at year end gets the EITC + # IRC 32(d)(2)(B)(iii)(II); same schedule as the separated-parent case. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 30_000 + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [head, child] + state_code: TX + output: + eitc_spouses_lived_apart_last_six_months: false + eitc_marital_eligible: true + eitc_eligible: true + eitc: 3_450.62 + +- name: A separated dependent does not establish the living-apart route + period: 2026 + input: + people: + head: + age: 35 + employment_income: 30_000 + child: + age: 17 + is_separated: true + child2: + age: 5 + tax_units: + tax_unit: + members: [head, child, child2] + filing_status: SEPARATE + households: + household: + members: [head, child, child2] + state_code: TX + output: + eitc_spouses_lived_apart_last_six_months: false + eitc_marital_eligible: false + +- name: Separated parent filing as head of household meets the exception by default + # head_of_household_eligible's IRC 7703(b) route gives HoH status; from + # 2021, IRC 32(d)(2)(A) still treats the filer as married under 7703(a). + period: 2025 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + filing_status: HEAD_OF_HOUSEHOLD + eitc_spouses_lived_apart_last_six_months: true + eitc_marital_eligible: true + +- name: Separated filer who did not live apart for six months files separately and fails from 2021 + period: 2025 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + eitc_spouses_lived_apart_last_six_months: false + households: + household: + members: [head, child] + state_code: TX + output: + filing_status: SEPARATE + eitc_marital_eligible: false + eitc: 0 + +- name: Separated head of household was unmarried for the EITC before 2021 + # Before ARPA section 9623, IRC 32(d) applied all of section 7703, so the + # 7703(b) treated-unmarried filer needed no separation exception. + period: 2020 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + filing_status: HEAD_OF_HOUSEHOLD + eitc_marital_eligible: true + +- name: Unmarried head of household needs no separation facts + period: 2025 + input: + people: + head: + age: 35 + employment_income: 30_000 + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: TX + output: + filing_status: HEAD_OF_HOUSEHOLD + eitc_spouses_lived_apart_last_six_months: false + eitc_marital_eligible: true + +- name: Separated head of household under a decree of separate maintenance is unmarried for the EITC + # IRC 7703(a)(2) treats a filer separated under a decree of separate + # maintenance as unmarried, so IRC 32(d)(1) doesn't apply and no + # qualifying child is needed for the marital test. + # 2025, no qualifying child: phase-in 0.0765 x 10_000 = 765, capped at the + # 649 maximum; earnings 10_000 are below the 10_620 phase-out start, so + # the reduction is 0 and the EITC is 649. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 10_000 + is_separated: true + is_separated_under_decree: true + mother: + age: 70 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, mother] + filing_status: HEAD_OF_HOUSEHOLD + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [head, mother] + state_code: TX + output: + eitc_child_count: 0 + eitc_marital_eligible: true + eitc_eligible: true + eitc: 649 + +- name: Separated head of household without a decree stays married and needs a qualifying child + # Same household with is_separated_under_decree false (the default): the + # filer is married under IRC 7703(a), the exception in IRC 32(d)(2)(B) + # needs a resident qualifying child, and there is none, so the EITC is 0. + period: 2025 + input: + people: + head: + age: 35 + employment_income: 10_000 + is_separated: true + is_separated_under_decree: false + mother: + age: 70 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, mother] + filing_status: HEAD_OF_HOUSEHOLD + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [head, mother] + state_code: TX + output: + eitc_child_count: 0 + eitc_marital_eligible: false + eitc_eligible: false + eitc: 0 + +- name: Separate filer under a decree needs no living-apart fact + # IRC 7703(a)(2): the decree makes the unit unmarried, so the exception + # isn't needed and no separation route is checked. Both routes are false. + # 2026, one child: 4_427 - 0.1598 x (30_000 - 23_890) = 3_450.62, the same + # schedule as the separated MFS parent above. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated_under_decree: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: false + eitc_legal_separation_and_apart_at_year_end: false + households: + household: + members: [head, child] + state_code: TX + output: + eitc_marital_eligible: true + eitc_eligible: true + eitc: 3_450.62 + +- name: Decree on the spouse alone makes a separate filer unmarried for the EITC + # The predicate reads the decree from the head or the spouse: IRC 7703(a)(2) + # treats an individual legally separated under a decree as not married, so + # the marital test passes with both separation routes false. 2026, one child: + # 4_427 - 0.1598 x (30_000 - 23_890) = 3_450.62. Without the decree the + # filer needs the IRC 32(d)(2)(B) exception and gets 0. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 30_000 + spouse: + age: 35 + is_separated_under_decree: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, spouse, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: false + eitc_legal_separation_and_apart_at_year_end: false + households: + household: + members: [head, spouse, child] + state_code: TX + output: + eitc_marital_eligible: true + eitc: 3_450.62 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/earned_income/ca_eitc_mfs.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/earned_income/ca_eitc_mfs.yaml new file mode 100644 index 00000000000..8f64e17295f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/tax/income/credits/earned_income/ca_eitc_mfs.yaml @@ -0,0 +1,325 @@ +- name: Childless MFS cannot receive California or federal EITC in 2023 + period: 2023 + input: + people: + parent: + age: 40 + employment_income: 8_639 + tax_units: + unit: + members: [parent] + filing_status: SEPARATE + households: + household: + members: [parent] + state_code: CA + output: + ca_eitc: 0 + eitc: 0 + ca_yctc: 0 + +- name: Childless MFS cannot receive California or federal EITC in 2025 + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + tax_units: + unit: + members: [parent] + filing_status: SEPARATE + households: + household: + members: [parent] + state_code: CA + output: + ca_eitc: 0 + eitc: 0 + ca_yctc: 0 + +- name: MFS parent without separation cannot receive CalEITC or YCTC in 2023 + period: 2023 + input: + people: + parent: + age: 40 + employment_income: 8_639 + is_separated: false + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + cohabitating_spouses: true + households: + household: + members: [parent, child] + state_code: CA + output: + ca_eitc: 0 + ca_yctc: 0 + eitc: 0 + +- name: MFS parent without separation cannot receive CalEITC or YCTC in 2025 + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + is_separated: false + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + cohabitating_spouses: true + households: + household: + members: [parent, child] + state_code: CA + output: + ca_eitc: 0 + ca_yctc: 0 + eitc: 0 + +- name: MFS parent living apart for the last six months qualifies in 2023 + period: 2023 + absolute_error_margin: 0.02 + input: + people: + parent: + age: 40 + employment_income: 8_639 + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: true + eitc_eligible: true + ca_eitc_eligible: true + eitc: 2_937.26 + ca_eitc: 1_304.835 + ca_yctc: 1_117 + +- name: MFS parent with legal separation and separate year-end households qualifies in 2025 + period: 2025 + absolute_error_margin: 0.02 + input: + people: + parent: + age: 40 + employment_income: 8_639 + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: true + eitc_eligible: true + ca_eitc_eligible: true + eitc: 2_937.26 + ca_eitc: 1_548.173 + ca_yctc: 1_189 + +- name: Childless MFS remains ineligible even with both separation facts + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + tax_units: + unit: + members: [parent] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [parent] + state_code: CA + output: + eitc_marital_eligible: false + ca_eitc: 0 + ca_yctc: 0 + eitc: 0 + +- name: A child who did not live with the MFS filer for more than half the year cannot enable the exception + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + child: + age: 3 + is_tax_unit_dependent: true + eitc_child_lived_with_taxpayer_more_than_half_year: false + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: false + ca_eitc: 0 + ca_yctc: 0 + eitc: 0 + +- name: A qualifying child without a valid SSN still satisfies the MFS exception + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + child: + age: 3 + is_tax_unit_dependent: true + ssn_card_type: NONE + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + households: + household: + members: [parent, child] + state_code: CA + output: + is_eitc_qualifying_child: [false, true] + eitc_child_count: 0 + eitc_marital_eligible: true + eitc_eligible: true + ca_eitc_eligible: true + ca_yctc: 1_189 + +- name: CalEITC retains its own age rules for an eligible separated parent + period: 2025 + input: + people: + parent: + age: 20 + employment_income: 8_639 + child: + age: 3 + is_tax_unit_dependent: true + ssn_card_type: NONE + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_child_count: 0 + eitc_marital_eligible: true + eitc_eligible: false + ca_eitc_eligible: true + ca_yctc: 1_189 + +- name: CalEITC does not import the federal filer's SSN restriction + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 8_639 + ssn_card_type: NONE + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: true + eitc_eligible: false + ca_eitc_eligible: true + ca_yctc: 1_189 + +- name: Zero earnings do not bypass the YCTC marital eligibility requirement + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 0 + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: false + ca_eitc_eligible: false + ca_eitc: 0 + ca_yctc: 0 + +- name: Zero earnings preserve YCTC for an MFS parent satisfying the separation exception + period: 2025 + input: + people: + parent: + age: 40 + employment_income: 0 + child: + age: 3 + is_tax_unit_dependent: true + tax_units: + unit: + members: [parent, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + households: + household: + members: [parent, child] + state_code: CA + output: + eitc_marital_eligible: true + ca_eitc_eligible: true + ca_eitc: 0 + ca_yctc: 1_189 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/eitc/nj_eitc.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/eitc/nj_eitc.yaml index be6996b945b..6e59297a498 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/eitc/nj_eitc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/credits/eitc/nj_eitc.yaml @@ -138,3 +138,230 @@ state_code: NJ output: nj_eitc: 0 + +- name: Married separated head of household without a qualifying child gets no NJ age fallback + # The filer is married under IRC 7703(a) (7703(b) gives only HoH status), + # lived apart six months (is_separated default), but has no EITC + # qualifying child, so IRC 32(d) denies the federal credit. The NJ + # fallback waives only the age limits, so the marital failure denies it + # too: federal 0, NJ 0. Before this fix NJ paid 0.4 x 649 = 259.60. + period: 2025 + input: + people: + head: + age: 55 + employment_income: 12_000 + is_separated: true + adult_child: + age: 30 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, adult_child] + filing_status: HEAD_OF_HOUSEHOLD + households: + household: + members: [head, adult_child] + state_code: NJ + output: + eitc_spouses_lived_apart_last_six_months: true + eitc_child_count: 0 + eitc_marital_eligible: false + eitc: 0 + nj_childless_eitc_age_eligible: false + nj_eitc: 0 + +- name: Unmarried 22-year-old without a qualifying child still gets the NJ age fallback + # Below the federal minimum age of 25 but above NJ's minimum of 18. + # 2025: NJ match 0.4 x federal zero-child maximum 649 = 259.60. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 22 + employment_income: 8_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: NJ + output: + eitc_marital_eligible: true + eitc: 0 + nj_childless_eitc_age_eligible: true + nj_eitc: 259.60 + +- name: Separate filer who meets the federal exception through a child without an SSN gets no NJ age fallback + # The child counts for IRC 32(d)(2)(B) but not for the federal schedule + # (32(c)(3)(D)), so eitc_child_count is 0 and the filer, at 22, is below + # the federal minimum age. The filer meets the federal marital test, but + # N.J.S.A. 54A:4-7a.(3) requires a married claimant to file jointly, so + # the age fallback pays 0. + period: 2025 + input: + people: + head: + age: 22 + employment_income: 8_000 + child: + age: 5 + ssn_card_type: NONE + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: true + households: + household: + members: [head, child] + state_code: NJ + output: + eitc_marital_eligible: true + eitc_child_count: 0 + nj_eitc_separate_filer_eligible: false + nj_childless_eitc_age_eligible: false + nj_eitc: 0 + +- name: Separate filer on the separation-instrument route gets the federal EITC but no NJ EITC + # IRC 32(d)(2)(B)(ii)(II): a written separation instrument and living apart + # at year end meet the federal exception. 2026, one child: phase-in + # 0.34 x 20,000 = 6,800 caps at the 4,427 maximum, and 20,000 is below the + # 23,890 phase-out start, so the federal EITC is 4,427. N.J.S.A. + # 54A:4-7a.(3) requires a married claimant to file jointly, so NJ pays 0. + period: 2026 + input: + people: + head: + age: 35 + employment_income: 20_000 + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [head, child] + state_code: NJ + output: + eitc: 4_427 + nj_eitc_separate_filer_eligible: false + nj_eitc: 0 + +- name: Separate filer on the separation-instrument route with a child without an SSN gets no NJ age fallback + # The instrument route meets the federal marital test, and the child + # without an SSN leaves eitc_child_count at 0, so the 22-year-old reaches + # only the age fallback. N.J.S.A. 54A:4-7a.(3) bars a married separate + # filer from it. + period: 2025 + input: + people: + head: + age: 22 + employment_income: 8_000 + child: + age: 5 + ssn_card_type: NONE + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_legal_separation_and_apart_at_year_end: true + households: + household: + members: [head, child] + state_code: NJ + output: + eitc_marital_eligible: true + nj_childless_eitc_age_eligible: false + nj_eitc: 0 + +- name: Separate filer with a child and six months apart gets no NJ EITC, against the NJ-1040 line 58 instructions + # The 2025 NJ-1040 instructions allow this filer the credit; the statute, + # N.J.S.A. 54A:4-7a.(3), and N.J.A.C. 18:35-4.3(g)(3) require a married + # claimant to file jointly, and the model follows them. Federal 2026, one + # child: 4,427 - 0.1598 x (30,000 - 23,890) = 3,450.62. The instructions' + # reading would pay 0.4 x 3,450.62 = 1,380.25. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 30_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + households: + household: + members: [head, child] + state_code: NJ + output: + eitc_spouses_lived_apart_last_six_months: true + eitc: 3_450.62 + nj_eitc_separate_filer_eligible: false + nj_eitc: 0 + +- name: Separate filer under a decree with a child gets the NJ match + # IRC 7703(a)(2): a filer legally separated under a decree is not married, + # so neither the federal marital test nor New Jersey's joint-filing rule + # applies, with both separation routes false. 2025, one child: phase-in + # 0.34 x 20,000 = 6,800 caps at 4,328, so NJ pays 0.4 x 4,328 = 1,731.20. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 35 + employment_income: 20_000 + is_separated_under_decree: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + filing_status: SEPARATE + eitc_spouses_lived_apart_last_six_months: false + eitc_legal_separation_and_apart_at_year_end: false + households: + household: + members: [head, child] + state_code: NJ + output: + eitc: 4_328 + nj_eitc_separate_filer_eligible: true + nj_eitc: 1_731.20 + +- name: Separate filer under a decree without a child gets the NJ age fallback + # The decree makes the 22-year-old unmarried (IRC 7703(a)(2)), so the + # marital tests pass with no child. Below the federal minimum age of 25, + # the filer reaches the age fallback: 2025, 0.4 x 649 = 259.60. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + head: + age: 22 + employment_income: 8_000 + is_separated_under_decree: true + tax_units: + tax_unit: + members: [head] + filing_status: SEPARATE + households: + household: + members: [head] + state_code: NJ + output: + eitc: 0 + nj_childless_eitc_age_eligible: true + nj_eitc: 259.60 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml index 44d2423efa2..8276a5ee687 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/tax/income/credits/wa_working_families_tax_credit.yaml @@ -1654,6 +1654,76 @@ # 62,000 would give 995 - 0.199 x (62,000 - 59,430) = 484. wa_working_families_tax_credit: 995 +- name: Childless separate filer below the age band gets no credit from a dependent's age + # IRC 32(c)(1)(A)(ii)(II), in the frozen 2022 federal rules, applies the + # 25-64 age test to the filer, not a dependent. The 22-year-old filer has + # no qualifying child, and the 30-year-old qualifying relative does not + # satisfy the age test for them. + period: 2025 + input: + people: + head: + age: 22 + employment_income: 10_000 + dependent: + age: 30 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, dependent] + filing_status: SEPARATE + households: + household: + members: [head, dependent] + state_code: WA + output: + wa_working_families_tax_credit: 0 + +- name: Childless single filer below the age band gets no credit from a dependent's age + period: 2025 + input: + people: + head: + age: 22 + employment_income: 10_000 + dependent: + age: 30 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, dependent] + filing_status: SINGLE + households: + household: + members: [head, dependent] + state_code: WA + output: + wa_working_families_tax_credit: 0 + +- name: Childless separate filer in the age band keeps the state-only credit + # The filer is 30, so the age test is met. Earnings of 10,000 are below the + # childless phase-out start (maximum qualifying income less 2,500), so the + # credit is the 2025 no-child amount, $335. + period: 2025 + input: + people: + head: + age: 30 + employment_income: 10_000 + dependent: + age: 22 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [head, dependent] + filing_status: SEPARATE + households: + household: + members: [head, dependent] + state_code: WA + output: + wa_working_families_tax_credit: 335 + - name: Case 39, interest raises AGI but not the earned income the reduction is measured on (taxsim 1489). period: 2024 absolute_error_margin: 0.01 @@ -1685,3 +1755,31 @@ # income $17,600: 325 - 0.13 x 1,509 = 128.83, rounded to 129. Measuring # from AGI ($17,618.27) would give 127. wa_working_families_tax_credit: 129 + +- name: Separated filer who did not live apart for six months files separately and keeps the state-only credit + # IRC 7703(b)(3) needs the spouse out of the household for the last six + # months, so the filer cannot file as head of household and files + # separately. The federal EITC is 0 (IRC 32(d)), and Washington's + # state-only path pays the 2025 one-child amount, $660, with no phase-out + # at $10,000 of earnings. + period: 2025 + input: + people: + head: + age: 35 + employment_income: 10_000 + is_separated: true + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + eitc_spouses_lived_apart_last_six_months: false + households: + household: + members: [head, child] + state_code: WA + output: + filing_status: SEPARATE + eitc: 0 + wa_working_families_tax_credit: 660 diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/cdcc/federal.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/cdcc/federal.yaml index 2f7593050cc..559455b06e8 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/cdcc/federal.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/cdcc/federal.yaml @@ -845,6 +845,8 @@ # joint return, so cdcc = 0 (deliberate filing-status-ineligibility pin). # The gate applies in cdcc_potential (pre-liability-cap), which is what # state credits consume, so both are pinned at 0. +# The same facts deny the EITC: without six months apart or a separation +# instrument, IRC 32(d) bars a married separate filer, so eitc = 0. - name: cdcc_separate_filer_ineligible (federal) period: 2026 absolute_error_margin: 0.1 @@ -895,15 +897,15 @@ output: tax_units: tax_unit: - eitc: 3_450.62 - eitc_eligible: true + eitc: 0.0 + eitc_eligible: false ctc: 2_200.0 ctc_value: 2_200.0 refundable_ctc: 780.0 cdcc_potential: 0.0 cdcc: 0.0 adjusted_gross_income: 30_000.0 - income_tax: -4_230.62 + income_tax: -780.0 # Same separate-filing parent, but living apart (is_separated) while # maintaining a home for the qualifying child: IRC 21(e)(4) treats them as # unmarried, restoring eligibility. AGI 30_000 -> first phase-out removes diff --git a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/eitc/federal.yaml b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/eitc/federal.yaml index 04973206659..cb7eaca3056 100644 --- a/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/eitc/federal.yaml +++ b/policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/tax_credits/eitc/federal.yaml @@ -869,7 +869,7 @@ cdcc: 0.0 adjusted_gross_income: 10_000.0 income_tax: -664.0 -- name: eitc_married_filing_separately_eligible_post_arpa (federal) +- name: eitc_married_filing_separately_same_household_ineligible (federal) period: 2026 absolute_error_margin: 0.1 input: @@ -931,22 +931,24 @@ output: tax_units: tax_unit: - # ARPA Sec. 9623 permanently amended 26 USC § 32(d): separate filers - # ARE eligible from 2021 on (eitc.eligibility.separate_filer: true), - # so this pins eligible/positive — NOT the pre-2021 exclusion. - # Non-joint phase-out start = 23_890 (no joint bonus); earnings - # 20_000 < 23_890 -> plateau eitc = 4_427. + # ARPA Sec. 9623 opened 26 USC § 32(d) from 2021 only to "certain + # separated spouses": a separate filer needs a qualifying child + # living with them for over half the year and one 32(d)(2)(B) + # condition. This couple shares a household with no separation + # facts, so neither condition holds (no six months apart, no + # separation instrument with separate households at year end): + # eitc = 0, eitc_eligible = false. # income_tax: taxable = 20_000 - 16_100 MFS standard deduction = # 3_900 -> 390 tax, absorbed by non-refundable CTC; net = - # -(4_427 + 1_700) = -6_127. - eitc: 4_427.0 - eitc_eligible: true + # -1_700 refundable CTC. + eitc: 0.0 + eitc_eligible: false ctc: 2_200.0 ctc_value: 2_090.0 refundable_ctc: 1_700.0 cdcc: 0.0 adjusted_gross_income: 20_000.0 - income_tax: -6_127.0 + income_tax: -1_700.0 - name: eitc_negative_self_employment_income_offsets_wages (federal) period: 2026 absolute_error_margin: 0.1 diff --git a/policyengine_us/tests/test_eitc_marital_eligibility_properties.py b/policyengine_us/tests/test_eitc_marital_eligibility_properties.py new file mode 100644 index 00000000000..8a640b47a71 --- /dev/null +++ b/policyengine_us/tests/test_eitc_marital_eligibility_properties.py @@ -0,0 +1,323 @@ +"""IRC 32(d) and New Jersey invariants over vectorized tax units. + +The tests call the production formulas on small entity proxies with the +real parameters, so they build no model. Each invariant can fail on its +own and carries its reason. The tests compare results across inputs and +do not restate a formula. YAML tests pin the dollar amounts. +""" + +from itertools import product +from pathlib import Path +from types import SimpleNamespace + +import numpy as np +import pytest +from hypothesis import example, given, settings +from hypothesis import strategies as st +from policyengine_core.parameters import ParameterNode + +from policyengine_us.tools.section_911 import SECTION_911_LEAF_INPUTS +from policyengine_us.tools.state_eitc_helpers import eitc_filing_status_eligible +from policyengine_us.variables.gov.irs.credits.earned_income.eligibility.eitc_marital_eligible import ( + eitc_marital_eligible, +) +from policyengine_us.variables.gov.states.nj.tax.income.credits.eitc.nj_childless_eitc_age_eligible import ( + nj_childless_eitc_age_eligible, +) +from policyengine_us.variables.gov.states.nj.tax.income.credits.eitc.nj_eitc import ( + nj_eitc, +) +from policyengine_us.variables.gov.states.nj.tax.income.credits.eitc.nj_eitc_separate_filer_eligible import ( + nj_eitc_separate_filer_eligible, +) + +PARAMETERS = Path(__file__).resolve().parents[1] / "parameters/gov" +FEDERAL_EITC = ParameterNode( + "eitc", directory_path=str(PARAMETERS / "irs/credits/eitc") +) +NJ_EITC = ParameterNode( + "eitc", directory_path=str(PARAMETERS / "states/nj/tax/income/credits/eitc") +) + + +def parameters(period): + return SimpleNamespace( + gov=SimpleNamespace( + irs=SimpleNamespace(credits=SimpleNamespace(eitc=FEDERAL_EITC(period))), + states=SimpleNamespace( + nj=SimpleNamespace( + tax=SimpleNamespace( + income=SimpleNamespace( + credits=SimpleNamespace(eitc=NJ_EITC(period)) + ) + ) + ) + ), + ) + ) + + +class FilingStatuses(np.ndarray): + possible_values = SimpleNamespace( + SEPARATE="SEPARATE", + JOINT="JOINT", + HEAD_OF_HOUSEHOLD="HEAD_OF_HOUSEHOLD", + SINGLE="SINGLE", + ) + + +class Units: + """Entity access and group aggregation, with no policy logic.""" + + def __init__(self, member_groups, person_values, values): + self.member_groups = np.asarray(member_groups, dtype=int) + self.count = int(self.member_groups.max()) + 1 + self.person_values = person_values + self.values = values + + def __call__(self, variable, period): + return self.values[variable] + + def members(self, variable, period): + return self.person_values[variable] + + def any(self, values): + result = np.zeros(self.count, dtype=bool) + np.logical_or.at(result, self.member_groups, values) + return result + + +STATUSES = ("SEPARATE", "HEAD_OF_HOUSEHOLD", "JOINT", "SINGLE") +HOLDERS = ("nobody", "head", "dependent") +GRID = list(product(STATUSES, (False, True), (False, True), (False, True), HOLDERS)) +# Child facts: (qualifying child, resident more than half the year, identified). +child = st.tuples(st.booleans(), st.booleans(), st.booleans()) + + +def federal_units(cases): + """Cross each child list with every status, head is_separated value, + separation-fact pair and decree holder. Members are the head, a + dependent who is never a qualifying child, then the children.""" + groups, people, units = [], [], [] + for case_index, case in enumerate(cases): + for status, separated, apart, legal, holder in GRID: + members = [ + (True, separated, holder == "head", False, True, True), + (False, False, holder == "dependent", False, True, True), + *[(False, False, False, *facts) for facts in case], + ] + groups += [len(units)] * len(members) + people += members + units.append((case_index, status, separated, apart, legal, holder)) + names = ( + "is_tax_unit_head_or_spouse", + "is_separated", + "is_separated_under_decree", + "is_eitc_qualifying_child", + "eitc_child_lived_with_taxpayer_more_than_half_year", + "meets_eitc_identification_requirements", + ) + person_values = dict(zip(names, np.asarray(people, dtype=bool).T)) + case_index, status, separated, apart, legal, holder = map(np.asarray, zip(*units)) + identified = ( + person_values["is_eitc_qualifying_child"] + & person_values["meets_eitc_identification_requirements"] + ) + values = { + "filing_status": status.view(FilingStatuses), + "eitc_spouses_lived_apart_last_six_months": apart, + "eitc_legal_separation_and_apart_at_year_end": legal, + # The identification-restricted count, exposed so that reading it in + # place of pre-identification children fails an invariant below. + "eitc_child_count": np.bincount(groups, weights=identified), + } + facts = SimpleNamespace( + case=case_index, + status=status, + separated=separated, + apart=apart, + legal=legal, + holder=holder, + resident_child=np.asarray([any(q and r for q, r, _ in case) for case in cases])[ + case_index + ], + ) + return Units(groups, person_values, values), facts + + +def counterpart(facts, field, old, new): + """Return (i, j) index arrays pairing units that differ only in field.""" + keys = np.rec.fromarrays( + [ + facts.case, + facts.status, + facts.separated, + facts.apart, + facts.legal, + facts.holder, + ], + names="case,status,separated,apart,legal,holder", + ) + lookup = {tuple(k): n for n, k in enumerate(keys.tolist())} + position = keys.dtype.names.index(field) + i, j = [], [] + for n, key in enumerate(keys.tolist()): + if key[position] == old: + other = list(key) + other[position] = new + i.append(n) + j.append(lookup[tuple(other)]) + return np.asarray(i), np.asarray(j) + + +@settings(max_examples=40, deadline=None, derandomize=True) +@given(cases=st.lists(st.lists(child, max_size=3), min_size=1, max_size=5)) +@example(cases=[[], [(True, True, False)], [(True, False, True)]]) +def test_federal_marital_invariants(cases): + units, f = federal_units(cases) + separate = f.status == "SEPARATE" + no_decree = f.holder != "head" + for year in (2020, 2021, 2025): + ok = eitc_marital_eligible.formula(units, f"{year}-01-01", parameters) + + # 32(d) reaches only married filers who do not file jointly. + assert ok[(f.status == "JOINT") | (f.status == "SINGLE")].all() + # 32(d)(2)(B)(i) needs a qualifying child resident over half the year. + assert not ok[separate & no_decree & ~f.resident_child].any() + # 7703(a)(2) reaches the taxpayer only, so a dependent's decree is inert. + i, j = counterpart(f, "holder", "nobody", "dependent") + np.testing.assert_array_equal(ok[i], ok[j]) + # Each separation route and the head's decree only relieve the test. + for field, old, new in ( + ("apart", False, True), + ("legal", False, True), + ("holder", "nobody", "head"), + ): + i, j = counterpart(f, field, old, new) + assert np.all(ok[i] <= ok[j]) + + if year < 2021: + # Before ARPA 2021, 32(d) barred every separate return. + np.testing.assert_array_equal(ok, ~separate) + continue + # The exception needs one of the two separation routes. + assert not ok[separate & no_decree & ~f.apart & ~f.legal].any() + # Either route passes with a resident child, identified or not: + # 32(c)(3)(D) limits the credit schedule, not 32(d)(2)(B). + assert ok[separate & f.resident_child & (f.apart | f.legal)].all() + # 7703(a)(2): a head separated under a decree is not married. + assert ok[f.holder == "head"].all() + # 32(d)(2)(A) uses 7703(a) only, so a separated head of household + # faces the separate filer's test. + i, j = counterpart(f, "status", "HEAD_OF_HOUSEHOLD", "SEPARATE") + hoh_separated = f.separated[i] + np.testing.assert_array_equal(ok[i][hoh_separated], ok[j][hoh_separated]) + # An unseparated head of household is unmarried and passes. + assert ok[i][~hoh_separated].all() + + +@pytest.mark.parametrize("year", [2020, 2025]) +def test_state_frozen_switch_sets_only_when_the_exception_applies(year): + units, f = federal_units([[(True, True, False)], [], [(True, False, True)]]) + period = f"{year}-01-01" + off = eitc_filing_status_eligible( + units, period, parameters, separate_filer_eligible=False + ) + on = eitc_filing_status_eligible( + units, period, parameters, separate_filer_eligible=True + ) + # A state that freezes the exception off bars every separate return. + np.testing.assert_array_equal(off, f.status != "SEPARATE") + # Switching it on gives the current federal test, whatever the year. + np.testing.assert_array_equal( + on, eitc_marital_eligible.formula(units, "2025-01-01", parameters) + ) + + +def nj_units(rows, marital_eligible): + """One-person New Jersey units with federal EITC results as inputs.""" + ( + status, + decree, + age, + child_count, + income_eligible, + other_federal, + phased_in, + reduction, + takes_up, + ) = map(np.asarray, zip(*rows)) + marital = np.asarray(marital_eligible, dtype=bool) + eligible = other_federal.astype(bool) & marital + zeros = np.zeros(len(rows)) + values = { + "filing_status": status.view(FilingStatuses), + "eitc_marital_eligible": marital, + "eitc_child_count": child_count, + "nj_eitc_income_eligible": income_eligible.astype(bool), + "eitc_eligible": eligible, + "eitc_phased_in": phased_in, + "eitc_reduction": reduction, + "takes_up_eitc": takes_up.astype(bool), + "eitc": np.maximum(phased_in - reduction, 0) * eligible * takes_up, + "foreign_earned_income_exclusion": zeros, + "section_911_excluded_income": zeros, + **{variable: zeros for variable in SECTION_911_LEAF_INPUTS}, + } + person_values = { + "age": age, + "is_tax_unit_head_or_spouse": np.ones(len(rows), dtype=bool), + "is_separated_under_decree": decree.astype(bool), + } + return Units(np.arange(len(rows)), person_values, values) + + +def nj_eitc_for(rows, marital_eligible, year): + units = nj_units(rows, marital_eligible) + period = f"{year}-01-01" + for variable in (nj_eitc_separate_filer_eligible, nj_childless_eitc_age_eligible): + units.values[variable.__name__] = variable.formula(units, period, parameters) + return nj_eitc.formula(units, period, parameters) + + +nj_row = st.tuples( + st.sampled_from(STATUSES), + st.booleans(), + st.integers(15, 80), + st.integers(0, 2), + st.booleans(), + st.booleans(), + st.floats(0, 8_000), + st.floats(0, 8_000), + st.booleans(), +) + + +@settings(max_examples=40, deadline=None, derandomize=True) +@given( + rows=st.lists(nj_row, min_size=1, max_size=20), + marital=st.lists(st.booleans(), min_size=20, max_size=20), +) +@example( + rows=[ + ("SEPARATE", False, 22, 0, True, False, 612.0, 0.0, True), + ("SEPARATE", True, 22, 0, True, False, 612.0, 0.0, True), + ], + marital=[True] * 20, +) +def test_nj_eitc_marital_invariants(rows, marital): + marital = np.asarray(marital[: len(rows)]) + status = np.asarray([row[0] for row in rows]) + decree = np.asarray([row[1] for row in rows]) + relaxed_rows = [("JOINT", *row[1:]) for row in rows] + for year in (2021, 2025): + actual = nj_eitc_for(rows, marital, year) + # P.L.2021, c.130 waives only the federal ages, so a unit that fails + # 32(d) gets nothing through the match or the age fallback. + assert np.all(actual[~marital] == 0) + # N.J.S.A. 54A:4-7a.(3) bars a married separate filer, even one the + # federal exception admits; a decree makes the filer unmarried. + assert np.all(actual[(status == "SEPARATE") & ~decree] == 0) + # Neither rule can raise the credit when it applies. + relaxed = nj_eitc_for(relaxed_rows, np.ones(len(rows), dtype=bool), year) + assert np.all(actual <= relaxed) diff --git a/policyengine_us/tools/state_eitc_helpers.py b/policyengine_us/tools/state_eitc_helpers.py index 90362cdda17..c44617ba3d2 100644 --- a/policyengine_us/tools/state_eitc_helpers.py +++ b/policyengine_us/tools/state_eitc_helpers.py @@ -28,15 +28,57 @@ def eitc_section_911_eligible(tax_unit, period): def eitc_filing_status_eligible( tax_unit, period, parameters, separate_filer_eligible=None ): - """Apply the federal EITC separate-filer rule unless a state overrides it.""" + """Apply IRC 32(d), optionally using a state's frozen exception switch. + + The switch enables the separated-spouse exception, rather than making + every separate filer eligible. Once enabled, the exception also governs + separated head-of-household filers, who are married under IRC 7703(a) + unless separated under a decree (IRC 7703(a)(2)). + Qualifying children are counted before identification requirements: + IRC 32(c)(3)(D) limits the credit schedule, not the separated-spouse + exception in IRC 32(d)(2)(B). + """ if separate_filer_eligible is None: separate_filer_eligible = parameters( period ).gov.irs.credits.eitc.eligibility.separate_filer filing_status = tax_unit("filing_status", period) - return separate_filer_eligible | ( - filing_status != filing_status.possible_values.SEPARATE + statuses = filing_status.possible_values + separate = filing_status == statuses.SEPARATE + if not separate_filer_eligible: + return ~separate + + person = tax_unit.members + # From 2021, IRC 32(d)(2)(A) determines marital status under IRC 7703(a) + # only, so a separated filer whom IRC 7703(b) lets file as head of + # household is still married for the EITC and needs the same exception. + # is_separated on the head or spouse is the model's married-but-apart + # indicator; head_of_household_eligible reads it for the 7703(b) route. + separated_head_of_household = ( + filing_status == statuses.HEAD_OF_HOUSEHOLD + ) & tax_unit.any( + person("is_tax_unit_head_or_spouse", period) & person("is_separated", period) + ) + # IRC 7703(a)(2): "an individual legally separated from his spouse under + # a decree of divorce or of separate maintenance shall not be considered + # as married." Such a unit is unmarried for IRC 32(d)(2)(A), so neither + # the separate-return nor the separated head-of-household branch needs + # the exception. + separated_under_decree = tax_unit.any( + person("is_tax_unit_head_or_spouse", period) + & person("is_separated_under_decree", period) + ) + needs_exception = (separate | separated_head_of_household) & ~separated_under_decree + has_resident_qualifying_child = tax_unit.any( + person("is_eitc_qualifying_child", period) + & person("eitc_child_lived_with_taxpayer_more_than_half_year", period) + ) + meets_separation_conditions = tax_unit( + "eitc_spouses_lived_apart_last_six_months", period + ) | tax_unit("eitc_legal_separation_and_apart_at_year_end", period) + return ~needs_exception | ( + has_resident_qualifying_child & meets_separation_conditions ) @@ -57,7 +99,13 @@ def calculate_eitc_demographic_eligibility( eitc_parameters.eligibility.age.min, ) max_age = eitc_parameters.eligibility.age.max - return has_child | tax_unit.any((age >= min_age) & (age <= max_age)) + # IRC 32(c)(1)(A)(ii)(II) applies the childless age test to the filer or, + # on a joint return, either spouse, never to a dependent, matching the + # federal eitc_demographic_eligible. + is_filer_or_spouse = ~person("is_tax_unit_dependent", period) + return has_child | tax_unit.any( + is_filer_or_spouse & (age >= min_age) & (age <= max_age) + ) def calculate_eitc_phase_out_start( diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_child_lived_with_taxpayer_more_than_half_year.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_child_lived_with_taxpayer_more_than_half_year.py new file mode 100644 index 00000000000..5a5c5a26c1f --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_child_lived_with_taxpayer_more_than_half_year.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class eitc_child_lived_with_taxpayer_more_than_half_year(Variable): + value_type = bool + entity = Person + label = "Child lived with the EITC filer for more than half the tax year" + definition_period = YEAR + default_value = True + reference = "https://www.law.cornell.edu/uscode/text/26/32#d_2_B_ii" + documentation = """ + Whether this person resided with the tax unit filer for more than + half the tax year, including applicable temporary-absence exceptions. + Defaults to true to preserve the model's existing assumption that + qualifying child dependents reside with their filer. Set to false + when the child does not meet the residence-duration test. This input + alone does not establish that the person is a qualifying child. + """ diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_eligible.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_eligible.py index e01f084154b..c3bbfad9445 100644 --- a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_eligible.py @@ -11,6 +11,7 @@ class eitc_eligible(Variable): "https://www.law.cornell.edu/uscode/text/26/32#c_1_A", # No EITC for an individual who claims the benefits of section 911. "https://www.law.cornell.edu/uscode/text/26/32#c_1_C", + "https://www.law.cornell.edu/uscode/text/26/32#d", # Former section 32(c)(1)(F), struck from 2021. "https://www.govinfo.gov/content/pkg/USCODE-2015-title26/html/USCODE-2015-title26-subtitleA-chap1-subchapA-partIV-subpartC-sec32.htm", ) @@ -26,12 +27,12 @@ def formula(tax_unit, period, parameters): # Section 32(c)(1)(C): "eligible individual" does not include an # individual who claims the benefits of section 911 (Form 2555). claims_section_911 = elects_section_911_exclusion(tax_unit, period) - # Define eligibility before considering separate filer limitation. eligible = ( demographic_eligible & investment_income_eligible & filers_has_ssn & ~claims_section_911 + & tax_unit("eitc_marital_eligible", period) ) # Former section 32(c)(1)(F), through 2020: "No credit shall be # allowed under this section to any eligible individual who has one or @@ -48,10 +49,4 @@ def formula(tax_unit, period, parameters): ) no_child_counted = tax_unit("eitc_child_count", period) == 0 eligible = eligible & ~(has_qualifying_child & no_child_counted) - # This parameter is true if separate filers are eligible. - if eitc.eligibility.separate_filer: - return eligible - # If separate filers are not eligible, check if the filer is separate. - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE - return eligible & ~separate + return eligible diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_legal_separation_and_apart_at_year_end.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_legal_separation_and_apart_at_year_end.py new file mode 100644 index 00000000000..c558f58b129 --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_legal_separation_and_apart_at_year_end.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class eitc_legal_separation_and_apart_at_year_end(Variable): + value_type = bool + entity = TaxUnit + label = "Filer has a qualifying separation instrument and separate household at year end" + definition_period = YEAR + default_value = False + reference = ( + "https://www.law.cornell.edu/uscode/text/26/32#d_2_B_iii_II", + "https://www.law.cornell.edu/uscode/text/26/121#d_3_C", + ) + documentation = """ + Whether the filer has a decree of separate maintenance, written + separation agreement, or other qualifying instrument under IRC + 121(d)(3)(C), other than a decree of divorce, and is not a member of + the spouse's household at the end of the tax year. Both conditions + must hold. Defaults to false when these facts are not supplied. + """ diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_marital_eligible.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_marital_eligible.py new file mode 100644 index 00000000000..648e7afbaad --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_marital_eligible.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * +from policyengine_us.tools.state_eitc_helpers import eitc_filing_status_eligible + + +class eitc_marital_eligible(Variable): + value_type = bool + entity = TaxUnit + label = "Meets EITC marital eligibility requirements" + definition_period = YEAR + reference = ( + "https://www.govinfo.gov/content/pkg/USCODE-2023-title26/pdf/USCODE-2023-title26-subtitleA-chap1-subchapA-partIV-subpartC-sec32.pdf#page=3", + "https://www.congress.gov/117/bills/hr1319/BILLS-117hr1319enr.pdf#page=151", + ) + documentation = """ + Married separate filers must have a qualifying child residing with them + for more than half the year and satisfy either statutory separation + route. The exception is enabled from 2021. From then, IRC 32(d)(2)(A) + determines marriage under IRC 7703(a) only, so a separated head of + household filer (is_separated on the head or spouse) faces the same + test. Child identification rules do not determine whether the marital + exception applies. A head or spouse separated under a decree + (is_separated_under_decree) is not married under IRC 7703(a)(2) and + needs no exception. + """ + + def formula(tax_unit, period, parameters): + return eitc_filing_status_eligible(tax_unit, period, parameters) diff --git a/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_spouses_lived_apart_last_six_months.py b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_spouses_lived_apart_last_six_months.py new file mode 100644 index 00000000000..8bfcb4f5d72 --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/earned_income/eligibility/eitc_spouses_lived_apart_last_six_months.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class eitc_spouses_lived_apart_last_six_months(Variable): + value_type = bool + entity = TaxUnit + label = "Spouses had different principal residences for the last six months" + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/32#d_2_B_iii_I" + documentation = """ + Whether the filer and spouse had different principal places of abode + throughout the last six months of the tax year, for the separated-spouse + exception in IRC 32(d)(2)(B)(iii)(I). Defaults to true when the tax unit + head or spouse is separated (is_separated), the model's existing + living-apart indicator, which the head-of-household, filing-status and + child and dependent care credit rules also read. Set this input directly + to override the default. It does not establish the legal-separation + route in IRC 32(d)(2)(B)(iii)(II), which requires a separation + instrument and separate households at year end. + """ + + def formula(tax_unit, period, parameters): + person = tax_unit.members + return tax_unit.any( + person("is_tax_unit_head_or_spouse", period) + & person("is_separated", period) + ) diff --git a/policyengine_us/variables/gov/states/ca/tax/income/credits/earned_income/ca_eitc_eligible.py b/policyengine_us/variables/gov/states/ca/tax/income/credits/earned_income/ca_eitc_eligible.py index 8d6d28990ce..28e3be20de5 100644 --- a/policyengine_us/variables/gov/states/ca/tax/income/credits/earned_income/ca_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/ca/tax/income/credits/earned_income/ca_eitc_eligible.py @@ -11,6 +11,7 @@ class ca_eitc_eligible(Variable): reference = ( "https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=RTC§ionNum=17052.", "https://www.ftb.ca.gov/forms/2024/2024-3514-booklet.html", + "https://www.ftb.ca.gov/forms/2023/2023-3514-booklet.pdf#page=4", ) def formula(tax_unit, period, parameters): @@ -41,4 +42,5 @@ def formula(tax_unit, period, parameters): meets_age_requirements & meets_investment_income_requirements & meets_agi_requirements + & tax_unit("eitc_marital_eligible", period) ) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py index 5704b213abf..70c7251cd86 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_childless_eitc_age_eligible.py @@ -18,9 +18,12 @@ class nj_childless_eitc_age_eligible(Variable): def formula(tax_unit, period, parameters): # Return True if all federal EITC conditions are met, except with modified age paramaters and household has no children. - # Check if filing status is separate. - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE + # P.L.2021, c.130 waives only the federal minimum and maximum ages, + # so the unit must still meet the federal marital condition in + # IRC 32(d), and New Jersey's own bar on married separate filers in + # section 1a.(3). + marital_eligible = tax_unit("eitc_marital_eligible", period) + nj_separate_filer_eligible = tax_unit("nj_eitc_separate_filer_eligible", period) # Check if tax unit has any EITC qualifying children. person = tax_unit.members no_qualifying_children = tax_unit("eitc_child_count", period) == 0 @@ -37,7 +40,8 @@ def formula(tax_unit, period, parameters): claims_section_911 = elects_section_911_exclusion(tax_unit, period) return ( - ~separate + marital_eligible + & nj_separate_filer_eligible & no_qualifying_children & tax_unit.any(age_eligible) & tax_unit("nj_eitc_income_eligible", period) diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc.py index 86c3d553299..890f4fc07d9 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc.py @@ -32,10 +32,13 @@ def formula(tax_unit, period, parameters): "eitc_reduction", period ) federal_eitc_takes_up = tax_unit("takes_up_eitc", period) + # N.J.S.A. 54A:4-7a.(3) bars a married separate filer, without the + # federal separated-spouse exception. federal_eitc_eligible = ( federal_eitc_eligible_assuming_meets_income_test & federal_eitc_meets_income_test & federal_eitc_takes_up + & tax_unit("nj_eitc_separate_filer_eligible", period) ) childless_eitc_age_eligible = tax_unit("nj_childless_eitc_age_eligible", period) childless_eitc_eligible = ( diff --git a/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc_separate_filer_eligible.py b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc_separate_filer_eligible.py new file mode 100644 index 00000000000..0561a0473b9 --- /dev/null +++ b/policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc_separate_filer_eligible.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class nj_eitc_separate_filer_eligible(Variable): + value_type = bool + entity = TaxUnit + label = "Meets New Jersey's EITC rule for a married separate filer" + documentation = ( + "N.J.S.A. 54A:4-7a.(3) and N.J.A.C. 18:35-4.3(g)(3) require a married " + "claimant to file jointly, except one who files as a head of household " + "or surviving spouse. They have no exception for a separate filer with " + "a qualifying child who lived apart from the spouse." + ) + definition_period = YEAR + reference = ( + # P.L.2021, c.130, section 1a.(3). + "https://pub.njleg.state.nj.us/Bills/2020/PL21/130_.HTM", + "https://www.law.cornell.edu/regulations/new-jersey/N-J-A-C-18-35-4-3", + ) + defined_for = StateCode.NJ + + def formula(tax_unit, period, parameters): + filing_status = tax_unit("filing_status", period) + separate = filing_status == filing_status.possible_values.SEPARATE + person = tax_unit.members + # IRC 7703(a)(2): a filer separated under a decree is not married. + under_decree = tax_unit.any( + person("is_tax_unit_head_or_spouse", period) + & person("is_separated_under_decree", period) + ) + return ~separate | under_decree diff --git a/policyengine_us/variables/gov/states/nm/tax/income/credits/eitc/nm_eitc_eligible.py b/policyengine_us/variables/gov/states/nm/tax/income/credits/eitc/nm_eitc_eligible.py index 3e0dfcf7267..00ad717d054 100644 --- a/policyengine_us/variables/gov/states/nm/tax/income/credits/eitc/nm_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/nm/tax/income/credits/eitc/nm_eitc_eligible.py @@ -12,15 +12,7 @@ class nm_eitc_eligible(Variable): def formula(tax_unit, period, parameters): # Use federal EITC eligibility, but replace demographic eligibility # with NM version. - eitc = parameters.gov.irs.credits.eitc(period) investment_income_eligible = tax_unit("eitc_investment_income_eligible", period) demographic_eligible = tax_unit("nm_eitc_demographic_eligible", period) - # Define eligibility before considering separate filer limitation. - eligible = demographic_eligible & investment_income_eligible - # This parameter is true if separate filers are eligible. - if eitc.eligibility.separate_filer: - return eligible - # If separate filers are not eligible, check if the filer is separate. - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE - return eligible & ~separate + marital_eligible = tax_unit("eitc_marital_eligible", period) + return demographic_eligible & investment_income_eligible & marital_eligible diff --git a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/eligible/ok_federal_eitc_eligible.py b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/eligible/ok_federal_eitc_eligible.py index a943e47be8d..f74fc6a8d09 100644 --- a/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/eligible/ok_federal_eitc_eligible.py +++ b/policyengine_us/variables/gov/states/ok/tax/income/credits/eitc/federal_credit/eligible/ok_federal_eitc_eligible.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.tools.state_eitc_helpers import eitc_filing_status_eligible class ok_federal_eitc_eligible(Variable): @@ -31,12 +32,11 @@ def formula(tax_unit, period, parameters): ) # Check demographic (age) eligibility demographic_eligible = tax_unit("ok_federal_eitc_demographic_eligible", period) - # Define eligibility before considering separate filer limitation - eligible = demographic_eligible & investment_income_eligible - # This parameter is true if separate filers are eligible - if eitc.eligibility.separate_filer: - return eligible - # If separate filers are not eligible, check if the filer is separate - filing_status = tax_unit("filing_status", period) - separate = filing_status == filing_status.possible_values.SEPARATE - return eligible & ~separate + # The frozen 2020 law predates the separated-spouse exception. + marital_eligible = eitc_filing_status_eligible( + tax_unit, + period, + parameters, + separate_filer_eligible=eitc.eligibility.separate_filer, + ) + return demographic_eligible & investment_income_eligible & marital_eligible diff --git a/policyengine_us/variables/household/demographic/marital_unit/is_separated_under_decree.py b/policyengine_us/variables/household/demographic/marital_unit/is_separated_under_decree.py new file mode 100644 index 00000000000..d33d39b026a --- /dev/null +++ b/policyengine_us/variables/household/demographic/marital_unit/is_separated_under_decree.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class is_separated_under_decree(Variable): + value_type = bool + entity = Person + label = "Legally separated under a decree of divorce or separate maintenance" + documentation = """ + Whether the person is legally separated from their spouse under a decree + of divorce or of separate maintenance. IRC 7703(a)(2) treats such a + person as not married. A written separation agreement without a decree + does not count. + Defaults to false because survey data cannot tell a decree from an + informal separation, so is_separated alone keeps the married reading + for the EITC. Only the EITC marital test (IRC 32(d)) and New Jersey's + separate-filer rule read this input, and the federal test reads it only + from 2021. Filing status and other rules do not read it yet. + """ + definition_period = YEAR + default_value = False + reference = "https://www.law.cornell.edu/uscode/text/26/7703#a_2" diff --git a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py index 7a6fd30e58b..5dfe85ae38f 100644 --- a/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py +++ b/policyengine_us/variables/household/demographic/tax_unit/head_of_household_eligible.py @@ -42,12 +42,21 @@ def formula(tax_unit, period, parameters): # IRC 7703(b)(1) further requires the taxpayer to file a separate # return: a JOINT filer who is separated cannot claim HoH because # the joint return forecloses the "considered unmarried" path. - treated_unmarried_qualifies = tax_unit.sum(is_qualifying_child) > 0 + # IRC 7703(b)(1) needs a child in the home for more than half the year, + # and 7703(b)(3) needs the spouse out of the household for the last + # six months. + resident_child = is_qualifying_child & person( + "eitc_child_lived_with_taxpayer_more_than_half_year", period + ) + treated_unmarried_qualifies = tax_unit.sum(resident_child) > 0 + lived_apart = tax_unit("eitc_spouses_lived_apart_last_six_months", period) surviving_spouse = tax_unit("surviving_spouse_eligible", period) unmarried_qualifies = has_qualifying_person & ~married & ~is_separated # ~married masks the JOINT-with-is_separated=true case: a JOINT # filer cannot use IRC 7703(b)(1) because they have not filed a # separate return. MFS filers (spouse in a different tax unit) have # married=false and continue to qualify via this branch. - separated_qualifies = treated_unmarried_qualifies & is_separated & ~married + separated_qualifies = ( + treated_unmarried_qualifies & is_separated & ~married & lived_apart + ) return (unmarried_qualifies | separated_qualifies) & ~surviving_spouse diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 6b1dbcbedc4..e59be87270a 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -6087,6 +6087,39 @@ candidates: note: 'Retain as a disputable gap-fill; verify whether repayments are statutory losses and separate this issue from unsupported spouse attribution. Code not yet on main: policyengine_us/variables/gov/states/nj/tax/income/adjusted_gross_income/claim_of_right/nj_claim_of_right_repayment_category.py, gov/states/nj/tax/income/adjusted_gross_income/nj_claim_of_right_offset.yaml, nj_claim_of_right_offset, nj_claim_of_right_repayment_category.' +- id: us-nj:eitc-married-separate-filer + program: income-tax + years: + from: 2021 + type: tax + question: Can a married New Jersey resident who files a separate return claim the NJEITC when the federal + separated-spouse exception applies? + sources: + - kind: statute + citation: N.J.S.A. 54A:4-7a.(3), as amended by P.L.2021, c.130 + url: https://pub.njleg.state.nj.us/Bills/2020/PL21/130_.HTM + - kind: regulation + citation: N.J.A.C. 18:35-4.3(g)(3) + url: https://www.law.cornell.edu/regulations/new-jersey/N-J-A-C-18-35-4-3 + - kind: instructions + citation: 2025 Form NJ-1040 instructions, line 58 + url: https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf#page=44 + readings: + statute: A married claimant who does not file federally as a head of household or surviving spouse must file + jointly, with no exception for a separated filer. N.J.A.C. 18:35-4.3(g)(3) says the same. + other: A married filer not filing jointly can claim the credit with a qualifying child who lived with the filer + for more than half the year and six months apart from the spouse. + example: In 2026, a separate filer with a child, $30,000 of wages and six months apart gets $1,380.25 under the + instructions and $0 under the statute. + pe_us: + follows: statute + code: + - policyengine_us/variables/gov/states/nj/tax/income/credits/eitc/nj_eitc_separate_filer_eligible.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10307 + state: ruled + note: A filer separated under a decree of divorce or separate maintenance is not married (IRC 7703(a)(2)) and can + claim the credit on a separate return. - id: us-nj:liheap-income-ceiling program: liheap years: