From cf199abdab1790a37783633dc6142d3c1260c85c Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 24 Jun 2026 16:28:02 -0400 Subject: [PATCH 1/2] Add adult dependent care expenses to the CDCC base via care_expenses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Child and Dependent Care Credit drew care expenses only from tax_unit_childcare_expenses, which is child-scoped (children under 18). A disabled adult qualifying individual under IRC section 21 โ€” a dependent or spouse age 18 or older โ€” was therefore attributed $0 of care expenses and received a $0 credit (and $0 in state mirrors such as Kansas, where ks_cdcc is a share of the federal credit). Route adult dependent care through the existing care_expenses input (now year-defined) and add it to cdcc_relevant_expenses alongside childcare. The childcare and childcare-subsidy pipeline is left unchanged, so CCDF/state CCAP programs and the DC KCCATC are unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../cdcc-expense-distribution.fixed.md | 1 + .../credits/cdcc/cdcc_relevant_expenses.yaml | 16 +++++++++ .../gov/irs/credits/cdcc/integration.yaml | 33 +++++++++++++++++++ .../gov/states/ks/tax/income/ks_cdcc.yaml | 29 ++++++++++++++++ .../expense/childcare/care_expenses.yaml | 5 +++ .../credits/cdcc/cdcc_relevant_expenses.py | 9 ++++- .../expense/childcare/care_expenses.py | 9 ++--- .../childcare/pre_subsidy_care_expenses.py | 5 +-- 8 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 changelog.d/cdcc-expense-distribution.fixed.md diff --git a/changelog.d/cdcc-expense-distribution.fixed.md b/changelog.d/cdcc-expense-distribution.fixed.md new file mode 100644 index 00000000000..1fdf0686cb3 --- /dev/null +++ b/changelog.d/cdcc-expense-distribution.fixed.md @@ -0,0 +1 @@ +Add care expenses for a disabled adult dependent or spouse to the Child and Dependent Care Credit base, via a new care_expenses input, so a disabled qualifying individual age 18 or older is no longer attributed $0 of care expenses; the federal credit and its state mirrors (e.g. Kansas) previously returned $0 in this case. 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 ba37b26263b..8ed8c1ecdd6 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 @@ -24,3 +24,19 @@ min_head_spouse_earned: 8_000 output: cdcc_relevant_expenses: 5_000 + +- name: Adult dependent care expenses add to the childcare base + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + pre_subsidy_care_expenses: 2_000 + tax_units: + tax_unit: + members: [person1] + tax_unit_childcare_expenses: 1_000 + cdcc_limit: 6_000 + min_head_spouse_earned: 10_000 + output: + cdcc_relevant_expenses: 3_000 # 1_000 childcare + 2_000 adult care diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml index c0210fa2edd..04b075b799d 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml @@ -95,3 +95,36 @@ output: # expected results from patched TAXSIM35 2023-01-13 version taxsim_tfica: 11_130.24 income_tax: 24_507.2 + +- name: Disabled adult dependent's care expenses reach the CDCC via care_expenses + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 40_000 + is_tax_unit_head: true + person2: # earning spouse: the earned-income test passes without deemed income + age: 38 + employment_income: 30_000 + is_tax_unit_spouse: true + person3: # disabled adult dependent: care billed as adult care, not childcare + age: 18 + is_incapable_of_self_care: true + is_tax_unit_dependent: true + pre_subsidy_care_expenses: 6_000 + tax_units: + tax_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: KS + output: + # The childcare pipeline is untouched (no children under 13); the adult-care + # expense enters the CDCC base via care_expenses. + tax_unit_childcare_expenses: 0 + care_expenses: [0, 0, 6_000] + cdcc_relevant_expenses: 3_000 # min(6_000, $3,000 cap for one individual, earnings) + cdcc: 600 # 20% x 3_000 at $70,000 joint AGI diff --git a/policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/ks_cdcc.yaml b/policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/ks_cdcc.yaml index a84e622894b..e2ff3ef47d6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/ks_cdcc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/ks_cdcc.yaml @@ -5,3 +5,32 @@ state_code: KS output: ks_cdcc: 0.25 * 1_000 + +- name: Disabled adult dependent's care expenses count toward Kansas CDCC + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 40_000 + is_tax_unit_head: true + person2: + age: 38 + employment_income: 30_000 + is_tax_unit_spouse: true + person3: + age: 18 + is_incapable_of_self_care: true + is_tax_unit_dependent: true + pre_subsidy_care_expenses: 6_000 + tax_units: + tax_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: KS + output: + cdcc: 600 # federal credit, fed by the adult-care expense + ks_cdcc: 300 # Kansas share of the federal credit diff --git a/policyengine_us/tests/policy/baseline/household/expense/childcare/care_expenses.yaml b/policyengine_us/tests/policy/baseline/household/expense/childcare/care_expenses.yaml index 884e9c1b413..69c31dcef59 100644 --- a/policyengine_us/tests/policy/baseline/household/expense/childcare/care_expenses.yaml +++ b/policyengine_us/tests/policy/baseline/household/expense/childcare/care_expenses.yaml @@ -4,3 +4,8 @@ pre_subsidy_care_expenses: 100 output: care_expenses: 100 + +- name: Care expenses default to zero + period: 2024 + output: + care_expenses: 0 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 16f557ee386..d6fab08529e 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 @@ -13,7 +13,14 @@ class cdcc_relevant_expenses(Variable): ) def formula(tax_unit, period, parameters): - expenses = tax_unit("tax_unit_childcare_expenses", period) + # Section 21 employment-related expenses cover care for any qualifying + # individual: childcare for children under 13 + # (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 monthly input summed to the year). + childcare = tax_unit("tax_unit_childcare_expenses", period) + adult_care = add(tax_unit, period, ["care_expenses"]) + expenses = childcare + adult_care cdcc_limit = tax_unit("cdcc_limit", period) eligible_capped_expenses = min_(expenses, cdcc_limit) # cap further to the lowest earnings between the taxpayer and spouse diff --git a/policyengine_us/variables/household/expense/childcare/care_expenses.py b/policyengine_us/variables/household/expense/childcare/care_expenses.py index 8e71e908508..bb978b72ff1 100644 --- a/policyengine_us/variables/household/expense/childcare/care_expenses.py +++ b/policyengine_us/variables/household/expense/childcare/care_expenses.py @@ -4,11 +4,12 @@ class care_expenses(Variable): value_type = float entity = Person - label = "Care expenses" + label = "Care expenses for a disabled adult dependent or spouse" unit = USD - definition_period = MONTH + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/21#b_2" def formula(person, period, parameters): + # No public subsidy applies to adult dependent care, so this passes + # through the pre-subsidy amount (mirrors childcare_expenses). return person("pre_subsidy_care_expenses", period) - - # Add subsidies in a .yaml file once added to the model diff --git a/policyengine_us/variables/household/expense/childcare/pre_subsidy_care_expenses.py b/policyengine_us/variables/household/expense/childcare/pre_subsidy_care_expenses.py index d1ad457f759..c4e7cbe7a50 100644 --- a/policyengine_us/variables/household/expense/childcare/pre_subsidy_care_expenses.py +++ b/policyengine_us/variables/household/expense/childcare/pre_subsidy_care_expenses.py @@ -4,6 +4,7 @@ class pre_subsidy_care_expenses(Variable): value_type = float entity = Person - label = "Pre subsidy care expenses" - definition_period = MONTH + label = "Pre-subsidy care expenses for a disabled adult dependent or spouse" + definition_period = YEAR unit = USD + reference = "https://www.law.cornell.edu/uscode/text/26/21#b_2" From 294902b4ef2b39e1ff105aa62d8dcf0832beff59 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 24 Jun 2026 16:47:57 -0400 Subject: [PATCH 2/2] Improve federal CDCC conformance with IRC section 21 edge cases Builds on the care_expenses expense fix: - Count a disabled spouse modeled as the tax-unit head, and treat a couple's two incapacitated spouses as one qualifying individual (IRC 21(b)(1)(C)), so cdcc_limit and the deemed floor are not double-counted. - Apply IRC section 21(d)(2) deemed earned income for a student or incapacitated spouse, so the earned-income test no longer zeroes the credit for a non-earning disabled spouse. - Disallow the credit for married taxpayers filing separately (IRC 21(e)(2)). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../cdcc-disabled-dependent-expenses.fixed.md | 1 + .../credits/cdcc/deemed_earned_income.yaml | 19 +++ .../baseline/gov/irs/credits/cdcc/cdcc.yaml | 38 ++++++ .../cdcc/cdcc_income_floor_eligible.yaml | 40 +++++++ .../irs/credits/cdcc/count_cdcc_eligible.yaml | 97 +++++++++++++++ .../gov/irs/credits/cdcc/integration.yaml | 29 +++++ .../irs/credits/cdcc/is_cdcc_eligible.yaml | 43 +++++++ .../credits/cdcc/min_head_spouse_earned.yaml | 112 +++++++++++++++++- .../variables/gov/irs/credits/cdcc/cdcc.py | 10 +- .../gov/irs/credits/cdcc/cdcc_eligible.py | 12 +- .../cdcc/cdcc_income_floor_eligible.py | 17 +++ .../irs/credits/cdcc/count_cdcc_eligible.py | 16 ++- .../credits/cdcc/min_head_spouse_earned.py | 30 ++++- 13 files changed, 448 insertions(+), 16 deletions(-) create mode 100644 changelog.d/cdcc-disabled-dependent-expenses.fixed.md create mode 100644 policyengine_us/parameters/gov/irs/credits/cdcc/deemed_earned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_income_floor_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/count_cdcc_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml create mode 100644 policyengine_us/variables/gov/irs/credits/cdcc/cdcc_income_floor_eligible.py diff --git a/changelog.d/cdcc-disabled-dependent-expenses.fixed.md b/changelog.d/cdcc-disabled-dependent-expenses.fixed.md new file mode 100644 index 00000000000..a69d2ae7051 --- /dev/null +++ b/changelog.d/cdcc-disabled-dependent-expenses.fixed.md @@ -0,0 +1 @@ +Improve federal Child and Dependent Care Credit conformance with IRC section 21: count a disabled spouse modeled as the tax unit head and treat a couple's two incapacitated spouses as one qualifying individual, apply the section 21(d)(2) deemed earned income for a student or incapacitated spouse, and disallow the credit for married taxpayers filing separately. diff --git a/policyengine_us/parameters/gov/irs/credits/cdcc/deemed_earned_income.yaml b/policyengine_us/parameters/gov/irs/credits/cdcc/deemed_earned_income.yaml new file mode 100644 index 00000000000..6dd883f7f7a --- /dev/null +++ b/policyengine_us/parameters/gov/irs/credits/cdcc/deemed_earned_income.yaml @@ -0,0 +1,19 @@ +description: The IRS deems a spouse who is a full-time student or incapable of self-care to have at least this earned income for the Child and Dependent Care Credit, based on the number of qualifying individuals. +brackets: + - threshold: + 2013-01-01: 0 + amount: + 2013-01-01: 3_000 + - threshold: + 2013-01-01: 2 + amount: + 2013-01-01: 6_000 +metadata: + type: single_amount + threshold_unit: person + amount_unit: currency-USD + period: year + label: CDCC deemed earned income for student or disabled spouse + reference: + - title: 26 U.S. Code ยง 21(d)(2) + href: https://uscode.house.gov/view.xhtml?req=granuleid:USC-prelim-title26-section21&num=0&edition=prelim diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc.yaml index d010ed7d1b7..7a1be889164 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc.yaml @@ -31,3 +31,41 @@ cdcc_potential: 1_000 output: cdcc: 0 + +# IRC 21(e)(2): a married taxpayer may only claim the credit on a joint return. +- name: Married filing separately does not receive the credit + period: 2024 + input: + cdcc_credit_limit: 1_000 + cdcc_potential: 1_000 + filing_status: SEPARATE + output: + cdcc: 0 + +- name: Head of household receives the credit + period: 2024 + input: + cdcc_credit_limit: 1_000 + cdcc_potential: 1_000 + filing_status: HEAD_OF_HOUSEHOLD + output: + cdcc: 1_000 + +- name: Married filing separately with a child and care expenses gets zero + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + age: 35 + employment_income: 40_000 + is_tax_unit_head: true + person2: + age: 5 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: SEPARATE + tax_unit_childcare_expenses: 3_000 + output: + cdcc: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_income_floor_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_income_floor_eligible.yaml new file mode 100644 index 00000000000..b860477ba45 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/cdcc_income_floor_eligible.yaml @@ -0,0 +1,40 @@ +- name: Case 1, disabled spouse assigned as tax unit head is eligible for the income floor. + period: 2024 + input: + people: + person1: + age: 70 + is_incapable_of_self_care: true + person2: + age: 50 + tax_units: + tax_unit: + members: [person1, person2] + output: + is_tax_unit_head: [true, false] + is_tax_unit_spouse: [false, true] + cdcc_income_floor_eligible: [true, false] + +- name: Case 2, disabled single tax unit head is not eligible for the spouse income floor. + period: 2024 + input: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: true + output: + cdcc_income_floor_eligible: false + +- name: Case 3, full-time student spouse is eligible for the income floor. + period: 2024 + input: + people: + person1: + age: 50 + person2: + age: 45 + is_full_time_student: true + tax_units: + tax_unit: + members: [person1, person2] + output: + cdcc_income_floor_eligible: [false, true] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/count_cdcc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/count_cdcc_eligible.yaml new file mode 100644 index 00000000000..b0246dc3499 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/count_cdcc_eligible.yaml @@ -0,0 +1,97 @@ +- name: Both spouses incapable of self-care count as one qualifying individual + period: 2024 + input: + people: + person1: + age: 45 + is_incapable_of_self_care: true + person2: + age: 43 + is_incapable_of_self_care: true + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + output: + # Both are is_cdcc_eligible, but only one spouse is a qualifying individual + # (the other is the taxpayer) per IRC 21(b)(1)(C). + is_cdcc_eligible: [true, true] + count_cdcc_eligible: 1 + +- name: Disabled spouse plus a young child count as two qualifying individuals + period: 2024 + input: + people: + person1: + age: 45 + is_incapable_of_self_care: true + person2: + age: 43 + person3: + age: 5 + tax_units: + tax_unit: + members: [person1, person2, person3] + filing_status: JOINT + output: + count_cdcc_eligible: 2 + +- name: Both disabled spouses plus a child count as two qualifying individuals + period: 2024 + input: + people: + person1: + age: 45 + is_incapable_of_self_care: true + person2: + age: 43 + is_incapable_of_self_care: true + person3: + age: 5 + tax_units: + tax_unit: + members: [person1, person2, person3] + filing_status: JOINT + output: + count_cdcc_eligible: 2 + +- name: Two disabled adult dependents count as two qualifying individuals + period: 2024 + input: + people: + person1: + age: 45 + person2: + age: 20 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + person3: + age: 22 + is_incapable_of_self_care: true + is_tax_unit_head: false + is_tax_unit_spouse: false + tax_units: + tax_unit: + members: [person1, person2, person3] + output: + count_cdcc_eligible: 2 + +- name: Healthy couple with two young children count as two qualifying individuals + period: 2024 + input: + people: + person1: + age: 45 + person2: + age: 43 + person3: + age: 5 + person4: + age: 8 + tax_units: + tax_unit: + members: [person1, person2, person3, person4] + filing_status: JOINT + output: + count_cdcc_eligible: 2 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml index 04b075b799d..830599c8260 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/integration.yaml @@ -128,3 +128,32 @@ care_expenses: [0, 0, 6_000] cdcc_relevant_expenses: 3_000 # min(6_000, $3,000 cap for one individual, earnings) cdcc: 600 # 20% x 3_000 at $70,000 joint AGI + +- name: Both spouses incapable of self-care are one qualifying individual + absolute_error_margin: 0.01 + period: 2024 + input: + people: + person1: + age: 45 + employment_income: 50_000 + is_incapable_of_self_care: true + person2: # incapacitated spouse: care billed as adult care via care_expenses + age: 43 + is_incapable_of_self_care: true + pre_subsidy_care_expenses: 6_000 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: TX + output: + # Two incapacitated spouses count as one qualifying individual (the taxpayer + # is never their own), so the cap and the deemed floor are $3,000, not + # $6,000. cdcc = 20% x $3,000 = $600 (was $1,200 before the count fix). + count_cdcc_eligible: 1 + cdcc_limit: 3_000 + cdcc: 600 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 new file mode 100644 index 00000000000..bf0002a18ae --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/is_cdcc_eligible.yaml @@ -0,0 +1,43 @@ +- name: Case 1, dependent child under age 13 qualifies. + period: 2024 + input: + age: 12 + is_tax_unit_head: false + is_tax_unit_spouse: false + output: + is_cdcc_eligible: true + +- name: Case 2, dependent child age 13 does not qualify by age. + period: 2024 + input: + age: 13 + is_tax_unit_head: false + is_tax_unit_spouse: false + output: + is_cdcc_eligible: false + +- name: Case 3, disabled single tax unit head is not their own qualifying individual. + period: 2024 + input: + age: 40 + is_incapable_of_self_care: true + is_tax_unit_head: true + output: + is_cdcc_eligible: false + +- name: Case 4, disabled spouse assigned as tax unit head qualifies. + period: 2024 + input: + people: + person1: + age: 70 + is_incapable_of_self_care: true + person2: + age: 50 + tax_units: + tax_unit: + members: [person1, person2] + output: + is_tax_unit_head: [true, false] + is_tax_unit_spouse: [false, true] + is_cdcc_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/min_head_spouse_earned.yaml b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/min_head_spouse_earned.yaml index b3ab1c31637..56b2a935d09 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/min_head_spouse_earned.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/credits/cdcc/min_head_spouse_earned.yaml @@ -1,18 +1,34 @@ - name: Head earns less period: 2018 input: - head_earned: 1 - spouse_earned: 2 - tax_unit_is_joint: true + people: + person1: + is_tax_unit_head: true + adjusted_earnings: 1 + person2: + is_tax_unit_spouse: true + adjusted_earnings: 2 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT output: min_head_spouse_earned: 1 - name: Spouse earns less period: 2018 input: - head_earned: 2 - spouse_earned: 1 - tax_unit_is_joint: true + people: + person1: + is_tax_unit_head: true + adjusted_earnings: 2 + person2: + is_tax_unit_spouse: true + adjusted_earnings: 1 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT output: min_head_spouse_earned: 1 @@ -22,3 +38,87 @@ head_earned: 1 output: min_head_spouse_earned: 1 + +- name: Disabled spouse assigned as tax unit head receives one-person income floor + period: 2024 + input: + people: + person1: + age: 70 + is_incapable_of_self_care: true + person2: + age: 50 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + output: + is_tax_unit_head: [true, false] + is_tax_unit_spouse: [false, true] + count_cdcc_eligible: 1 + min_head_spouse_earned: 3_000 + +- name: Disabled spouse assigned as tax unit head receives two-person income floor + period: 2024 + input: + people: + person1: + age: 70 + is_incapable_of_self_care: true + person2: + age: 50 + employment_income: 50_000 + person3: + age: 5 + tax_units: + tax_unit: + members: [person1, person2, person3] + filing_status: JOINT + output: + count_cdcc_eligible: 2 + min_head_spouse_earned: 6_000 + +- name: Both spouses floor-eligible only one spouse receives the income floor + period: 2024 + input: + people: + person1: + age: 70 + is_incapable_of_self_care: true + person2: + age: 50 + is_full_time_student: true + person3: + age: 5 + tax_units: + tax_unit: + members: [person1, person2, person3] + filing_status: JOINT + output: + count_cdcc_eligible: 2 + min_head_spouse_earned: 0 + +- name: Both spouses floor-eligible with asymmetric earnings deems only the lower earner + period: 2024 + input: + people: + person1: + age: 40 + is_full_time_student: true + adjusted_earnings: 5_000 + person2: + age: 38 + is_full_time_student: true + adjusted_earnings: 1_000 + person3: + age: 5 + tax_units: + tax_unit: + members: [person1, person2, person3] + filing_status: JOINT + output: + count_cdcc_eligible: 1 + # Deem the lower earner ($1,000 -> $3,000 floor), keep the higher actual + # ($5,000); the lesser is $3,000. Only one spouse may be deemed. + min_head_spouse_earned: 3_000 diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc.py index 2edb7782635..e8aaf98e3d9 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc.py @@ -15,6 +15,12 @@ def formula(tax_unit, period, parameters): # In 2021, the CDCC was refundable p = parameters(period).gov.irs.credits if "cdcc" in p.refundable: - return potential + credit = potential else: - return min_(credit_limit, potential) + credit = min_(credit_limit, potential) + # IRC 21(e)(2): a married taxpayer may claim the credit only on a joint + # return. A separated taxpayer who qualifies under 21(e)(4) is treated + # as not married and files as head of household, not separately. + filing_status = tax_unit("filing_status", period) + separate = filing_status == filing_status.possible_values.SEPARATE + return where(separate, 0, credit) 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 b7f1a49b057..401cbca4c8e 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_eligible.py @@ -6,15 +6,19 @@ class is_cdcc_eligible(Variable): entity = Person label = "CDCC-eligible" definition_period = YEAR - reference = "https://www.law.cornell.edu/uscode/text/26/21#b_1" + reference = "https://uscode.house.gov/view.xhtml?req=granuleid:USC-prelim-title26-section21&num=0&edition=prelim" def formula(person, period, parameters): age = person("age", period) + is_dependent = person("is_tax_unit_dependent", period) # Subsection A. max_age = parameters(period).gov.irs.credits.cdcc.eligibility.child_age - qualifies_by_age = age < max_age + qualifies_by_age = is_dependent & (age < max_age) # Subsection B (dependent) and C (spouse). - non_head = ~person("is_tax_unit_head", period) disabled = person("is_incapable_of_self_care", period) - qualifies_by_disability = non_head & disabled + head_or_spouse = person("is_tax_unit_head_or_spouse", period) + has_spouse = person.tax_unit.sum(head_or_spouse) > 1 + qualifies_by_disability = disabled & ( + is_dependent | (head_or_spouse & has_spouse) + ) return qualifies_by_age | qualifies_by_disability diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_income_floor_eligible.py b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_income_floor_eligible.py new file mode 100644 index 00000000000..2f2f0757f3e --- /dev/null +++ b/policyengine_us/variables/gov/irs/credits/cdcc/cdcc_income_floor_eligible.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class cdcc_income_floor_eligible(Variable): + value_type = bool + entity = Person + label = "Eligible for the CDCC spouse earned income floor" + definition_period = YEAR + reference = "https://uscode.house.gov/view.xhtml?req=granuleid:USC-prelim-title26-section21&num=0&edition=prelim" + + def formula(person, period, parameters): + head_or_spouse = person("is_tax_unit_head_or_spouse", period) + has_spouse = person.tax_unit.sum(head_or_spouse) > 1 + student_or_disabled = person("is_full_time_student", period) | person( + "is_incapable_of_self_care", period + ) + return head_or_spouse & has_spouse & student_or_disabled diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/count_cdcc_eligible.py b/policyengine_us/variables/gov/irs/credits/cdcc/count_cdcc_eligible.py index c5de64a0267..0274935569b 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/count_cdcc_eligible.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/count_cdcc_eligible.py @@ -4,8 +4,18 @@ class count_cdcc_eligible(Variable): value_type = int entity = TaxUnit - label = "CDCC-eligible children" - unit = USD + label = "CDCC qualifying individuals" + unit = "person" definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/21#b_1" - adds = ["is_cdcc_eligible"] + def formula(tax_unit, period, parameters): + person = tax_unit.members + is_eligible = person("is_cdcc_eligible", period) + head_or_spouse = person("is_tax_unit_head_or_spouse", period) + # A married couple contributes at most one qualifying individual via + # the spouse prong (IRC 21(b)(1)(C)): the taxpayer is never their own + # qualifying individual, so two incapacitated spouses count as one. + dependent_count = tax_unit.sum(is_eligible & ~head_or_spouse) + has_eligible_spouse = tax_unit.any(is_eligible & head_or_spouse) + return dependent_count + has_eligible_spouse diff --git a/policyengine_us/variables/gov/irs/credits/cdcc/min_head_spouse_earned.py b/policyengine_us/variables/gov/irs/credits/cdcc/min_head_spouse_earned.py index 34145f65f48..cf62d46dcd2 100644 --- a/policyengine_us/variables/gov/irs/credits/cdcc/min_head_spouse_earned.py +++ b/policyengine_us/variables/gov/irs/credits/cdcc/min_head_spouse_earned.py @@ -9,7 +9,35 @@ class min_head_spouse_earned(Variable): definition_period = YEAR def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.credits.cdcc is_joint = tax_unit("tax_unit_is_joint", period) + person = tax_unit.members + is_head = person("is_tax_unit_head", period) + is_spouse = person("is_tax_unit_spouse", period) + floor_eligible = person("cdcc_income_floor_eligible", period) + head_earnings = tax_unit("head_earned", period) spouse_earnings = tax_unit("spouse_earned", period) - return where(is_joint, min_(head_earnings, spouse_earnings), head_earnings) + head_floor_eligible = tax_unit.sum(is_head * floor_eligible) > 0 + spouse_floor_eligible = tax_unit.sum(is_spouse * floor_eligible) > 0 + + # IRC section 21(d)(2): a spouse who is a student or incapable of + # self-care is deemed to earn at least this floor, but only one spouse + # may be deemed. Deem whichever eligible spouse yields the larger + # lesser-of-earnings, leaving the other spouse's actual earnings. + qualifying_individuals = tax_unit("count_cdcc_eligible", period) + floor = p.deemed_earned_income.calc(qualifying_individuals) + no_deem = min_(head_earnings, spouse_earnings) + deem_head = where( + head_floor_eligible, + min_(max_(head_earnings, floor), spouse_earnings), + 0, + ) + deem_spouse = where( + spouse_floor_eligible, + min_(max_(spouse_earnings, floor), head_earnings), + 0, + ) + joint_earnings = max_(no_deem, max_(deem_head, deem_spouse)) + + return where(is_joint, joint_earnings, tax_unit("head_earned", period))