diff --git a/changelog.d/credits-2026-id-ctc-co-fatc.fixed.md b/changelog.d/credits-2026-id-ctc-co-fatc.fixed.md new file mode 100644 index 00000000000..f413282f4fd --- /dev/null +++ b/changelog.d/credits-2026-id-ctc-co-fatc.fixed.md @@ -0,0 +1 @@ +Remove Idaho's expired child tax credit from state child tax credit totals after 2025, preserve those totals under Idaho revival reforms, and pause Colorado's family affordability tax credit for 2026. diff --git a/policyengine_us/parameters/gov/states/co/tax/income/credits/family_affordability/amount.yaml b/policyengine_us/parameters/gov/states/co/tax/income/credits/family_affordability/amount.yaml index 9770219ff05..038c2639435 100644 --- a/policyengine_us/parameters/gov/states/co/tax/income/credits/family_affordability/amount.yaml +++ b/policyengine_us/parameters/gov/states/co/tax/income/credits/family_affordability/amount.yaml @@ -2,6 +2,10 @@ description: Colorado provides this family affordability tax credit base amount values: 2024-01-01: 3_200 2025-01-01: 3_273 + # C.R.S. 39-22-130(6)(f): with forecast revenue growth "LESS THAN THREE + # PERCENT, NO CREDIT IS ALLOWED"; the Department of Revenue pauses the credit + # for tax year 2026. + 2026-01-01: 0 metadata: period: year unit: currency-USD @@ -11,5 +15,9 @@ metadata: href: https://leg.colorado.gov/bills/hb24-1311 - title: 2024 Colorado Child Tax Credit Instructions, Section C href: https://tax.colorado.gov/sites/tax/files/documents/DR0104CN_2024.pdf#page=3 - - title: 2025 Colorado Individual Tax Filing Guide 104 Book, Section C - href: https://tax.colorado.gov/sites/tax/files/documents/Book104_2025.pdf#page=27 + - title: 2025 Colorado Child Tax Credit Instructions, Section C + href: https://tax.colorado.gov/sites/tax/files/documents/DR_0104CN_2025.pdf#page=3 + - title: House Bill 24-1311, C.R.S. 39-22-130(6)(f) + href: https://leg.colorado.gov/sites/default/files/2024a_1311_signed.pdf#page=10 + - title: Colorado Department of Revenue, Child Tax Credit and EITC FAQs (tax year 2026 pause) + href: https://tax.colorado.gov/ctc-eitc-faqs diff --git a/policyengine_us/parameters/gov/states/household/state_ctcs.yaml b/policyengine_us/parameters/gov/states/household/state_ctcs.yaml index b34e65aa3fc..6c36d2e58b5 100644 --- a/policyengine_us/parameters/gov/states/household/state_ctcs.yaml +++ b/policyengine_us/parameters/gov/states/household/state_ctcs.yaml @@ -74,6 +74,7 @@ values: - or_ctc - ut_ctc - vt_ctc + # Idaho's child tax credit ends after 2025 (Idaho Code 63-3029L(1)). 2026-01-01: - az_dependent_tax_credit - ca_yctc @@ -81,7 +82,6 @@ values: - co_family_affordability_credit - dc_ctc - ga_ctc - - id_ctc - il_ctc - ma_child_and_family_credit_or_dependent_care_credit - md_ctc @@ -103,7 +103,6 @@ values: - co_family_affordability_credit - dc_ctc - ga_ctc - - id_ctc - il_ctc - ma_child_and_family_credit_or_dependent_care_credit - md_ctc diff --git a/policyengine_us/parameters/gov/states/id/tax/income/credits/ctc/amount.yaml b/policyengine_us/parameters/gov/states/id/tax/income/credits/ctc/amount.yaml index 0817261a666..aec34f303a0 100644 --- a/policyengine_us/parameters/gov/states/id/tax/income/credits/ctc/amount.yaml +++ b/policyengine_us/parameters/gov/states/id/tax/income/credits/ctc/amount.yaml @@ -15,4 +15,4 @@ metadata: - title: Idaho Individual Income Tax Instructions Packet 2024 href: https://tax.idaho.gov/wp-content/uploads/forms/EIN00046/EIN00046_10-23-2024.pdf#page=10 - title: Idaho Individual Income Tax Instructions Packet 2025 - href: https://tax.idaho.gov/wp-content/uploads/forms/EIN00046/EIN00046_03-02-2026.pdf + href: https://tax.idaho.gov/wp-content/uploads/forms/EIN00046/EIN00046_03-02-2026.pdf#page=12 diff --git a/policyengine_us/reforms/states/id/ctc/id_ctc_reform.py b/policyengine_us/reforms/states/id/ctc/id_ctc_reform.py index 771d5c45848..c465321bb49 100644 --- a/policyengine_us/reforms/states/id/ctc/id_ctc_reform.py +++ b/policyengine_us/reforms/states/id/ctc/id_ctc_reform.py @@ -24,6 +24,33 @@ def _first_true_instant(nodes, default): return min(candidates) +def _restore_id_ctc_reporting(parameters, revival_start): + """Include the revived worksheet amount without erasing later credits.""" + state_ctcs = parameters.gov.states.household.state_ctcs + revival_stop = instant("2100-12-31") + if revival_start > revival_stop: + return + + # Snapshot every dated list before update replaces overlapping intervals. + # In particular, the 2027 list adds Rhode Island's credit independently. + dated_lists = [(revival_start, list(state_ctcs(revival_start)))] + dated_lists.extend( + (instant(value.instant_str), list(value.value)) + for value in state_ctcs.values_list + if str(revival_start) < value.instant_str <= str(revival_stop) + ) + dated_lists.sort(key=lambda entry: entry[0]) + for index, (start, credits) in enumerate(dated_lists): + if "id_ctc" in credits: + continue + stop = ( + dated_lists[index + 1][0].offset(-1, "day") + if index + 1 < len(dated_lists) + else revival_stop + ) + state_ctcs.update(start=start, stop=stop, value=credits + ["id_ctc"]) + + def create_id_ctc() -> Reform: class id_refundable_ctc(Variable): value_type = float @@ -73,6 +100,7 @@ def modify_parameters(parameters): # Never revive before the baseline drop date: id_ctc is already in the # list through 2025, so revival only matters from 2026 onward. revival_start = max(activation, baseline_drop) + _restore_id_ctc_reporting(parameters, revival_start) non_refundable = parameters.gov.states.id.tax.income.credits.non_refundable current_non_refundable = non_refundable(revival_start) if "id_ctc" not in current_non_refundable: diff --git a/policyengine_us/reforms/states/id/s1450/s1450.py b/policyengine_us/reforms/states/id/s1450/s1450.py index b4e1ed177d3..9d55950c7e4 100644 --- a/policyengine_us/reforms/states/id/s1450/s1450.py +++ b/policyengine_us/reforms/states/id/s1450/s1450.py @@ -3,6 +3,9 @@ from policyengine_core.reforms import Reform from policyengine_us.model_api import * +from policyengine_us.reforms.states.id.ctc.id_ctc_reform import ( + _restore_id_ctc_reporting, +) def create_id_s1450() -> Reform: @@ -14,6 +17,7 @@ def modify_parameters(parameters: ParameterNode) -> ParameterNode: stop=instant("2100-12-31"), value=["id_ctc"], ) + _restore_id_ctc_reporting(parameters, instant("2026-01-01")) return parameters class reform(Reform): diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/family_affordability/co_family_affordability_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/family_affordability/co_family_affordability_credit.yaml index 5d6b0e59273..326e39a3966 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/family_affordability/co_family_affordability_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/tax/income/credits/family_affordability/co_family_affordability_credit.yaml @@ -118,3 +118,45 @@ output: co_family_affordability_credit: [0, 0, 0] co_refundable_credits: 0 + +- name: Colorado family affordability credit in 2025 + period: 2025 + input: + people: + head: + age: 30 + child: + age: 1 + tax_units: + tax_unit: + members: [head, child] + adjusted_gross_income: 0 + households: + household: + members: [head, child] + state_code: CO + output: + co_family_affordability_credit: [0, 3_273] + +- name: Colorado pauses the family affordability credit in 2026 + period: 2026 + input: + people: + head: + age: 30 + child: + age: 1 + older_child: + age: 10 + tax_units: + tax_unit: + members: [head, child, older_child] + adjusted_gross_income: 0 + households: + household: + members: [head, child, older_child] + state_code: CO + output: + # C.R.S. 39-22-130(6)(f): no credit when forecast revenue growth is below + # three percent; the Department of Revenue pauses it for tax year 2026. + co_family_affordability_credit: [0, 0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/credits/id_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/credits/id_ctc.yaml index 9b0596d7c1d..15dd1e920fc 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/credits/id_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/id/tax/income/credits/id_ctc.yaml @@ -126,3 +126,24 @@ output: # 5 children under 17 * $205 id_ctc: 1025 + +- name: No Idaho child tax credit after 2025 in the credit list or the state child tax credit totals + period: 2026 + input: + people: + head: + age: 30 + child: + age: 5 + tax_units: + tax_unit: + members: [head, child] + households: + household: + members: [head, child] + state_code: ID + output: + # Idaho Code 63-3029L(1): taxable years beginning before January 1, 2026. + # id_ctc stays the worksheet amount a reform can revive. + id_non_refundable_credits: 0 + taxsim_state_ctc: 0 diff --git a/policyengine_us/tests/policy/contrib/states/id/ctc/id_ctc_reform.yaml b/policyengine_us/tests/policy/contrib/states/id/ctc/id_ctc_reform.yaml index e191a130705..af07805dafb 100644 --- a/policyengine_us/tests/policy/contrib/states/id/ctc/id_ctc_reform.yaml +++ b/policyengine_us/tests/policy/contrib/states/id/ctc/id_ctc_reform.yaml @@ -15,6 +15,7 @@ # id_ctc still computes 2 * 205 = 410 as a worksheet amount, but it is not # in the 2026 nonrefundable list, so no credit is applied. id_non_refundable_credits: 0 + taxsim_state_ctc: 0 - name: Revival applies the nonrefundable CTC capped at liability period: 2026 @@ -29,6 +30,7 @@ # Revived list is [id_ctc]; applied = min(410, 1000) = 410. Refundable off. id_non_refundable_credits: 410 id_refundable_ctc: 0 + taxsim_state_ctc: 410 - name: Refundable option pays unused CTC to a no-liability family period: 2026 @@ -44,6 +46,7 @@ # potential 410, nonrefundable 0 (no liability), unused 410, # cap = 205 * 2 = 410 -> fully refundable. id_refundable_ctc: 410 + taxsim_state_ctc: 410 - name: Refundable cap override composes period: 2026 @@ -59,6 +62,7 @@ output: # refund = min(410 unused, 100 * 2) = 200. id_refundable_ctc: 200 + taxsim_state_ctc: 410 - name: Baseline CTC amount override composes with the revival period: 2026 @@ -73,3 +77,4 @@ output: id_ctc: 600 id_non_refundable_credits: 600 + taxsim_state_ctc: 600 diff --git a/policyengine_us/tests/policy/contrib/states/id/s1450/id_s1450.yaml b/policyengine_us/tests/policy/contrib/states/id/s1450/id_s1450.yaml index 4f2424257ff..8124038f057 100644 --- a/policyengine_us/tests/policy/contrib/states/id/s1450/id_s1450.yaml +++ b/policyengine_us/tests/policy/contrib/states/id/s1450/id_s1450.yaml @@ -27,6 +27,7 @@ output: # 1 qualifying child * $205 = $205 id_ctc: 205 + taxsim_state_ctc: 205 - name: Case 2, multiple children in 2026. # Three qualifying children, credit = $615 @@ -59,6 +60,7 @@ output: # 3 qualifying children * $205 = $615 id_ctc: 615 + taxsim_state_ctc: 615 - name: Case 3, MFJ filing status with two children. # Married couple with two qualifying children @@ -117,6 +119,7 @@ output: # 1 qualifying child * $205 = $205 id_ctc: 205 + taxsim_state_ctc: 205 - name: Case 5, no qualifying children. # No children under 17, credit = $0 diff --git a/policyengine_us/tests/policy/contrib/states/test_id_ga_ctc_reform_activation.py b/policyengine_us/tests/policy/contrib/states/test_id_ga_ctc_reform_activation.py index 91f4d035358..78aff3e779b 100644 --- a/policyengine_us/tests/policy/contrib/states/test_id_ga_ctc_reform_activation.py +++ b/policyengine_us/tests/policy/contrib/states/test_id_ga_ctc_reform_activation.py @@ -21,9 +21,13 @@ """ from policyengine_core.periods import instant +from policyengine_core.parameters import ParameterNode from policyengine_core.reforms import Reform from policyengine_us import Simulation +from policyengine_us.reforms.states.id.ctc.id_ctc_reform import ( + _restore_id_ctc_reporting, +) ACTIVATION_YEAR = "2028-01-01" @@ -128,5 +132,42 @@ def test__id_ctc_revival_does_not_leak_before_activation(): assert sim.calculate("id_ctc", 2026)[0] == 410 # ...but the revival must NOT apply it in the pre-activation year. assert sim.calculate("id_non_refundable_credits", 2026)[0] == 0 + assert sim.calculate("taxsim_state_ctc", 2026)[0] == 0 # In the activation year the revived nonrefundable credit applies. assert sim.calculate("id_non_refundable_credits", 2028)[0] == 410 + assert sim.calculate("taxsim_state_ctc", 2028)[0] == 410 + + reporting = sim.tax_benefit_system.parameters.gov.states.household.state_ctcs + assert "id_ctc" not in reporting(instant("2027-01-01")) + assert "ri_ctc" in reporting(instant("2027-01-01")) + assert "ri_ctc" in reporting(instant("2028-01-01")) + assert reporting(instant("2028-01-01")).count("id_ctc") == 1 + + +def test__id_ctc_reporting_preserves_later_credits(): + # A small parameter tree checks a revival before a later credit starts, + # without constructing another country model or household simulation. + parameters = ParameterNode( + "", + data={ + "gov": { + "states": { + "household": { + "state_ctcs": { + "values": { + "2026-01-01": ["ca_yctc"], + "2027-01-01": ["ca_yctc", "ri_ctc"], + }, + "metadata": {"unit": "list"}, + } + } + } + } + }, + ) + _restore_id_ctc_reporting(parameters, instant("2026-01-01")) + _restore_id_ctc_reporting(parameters, instant("2026-01-01")) + reporting = parameters.gov.states.household.state_ctcs + assert reporting(instant("2026-01-01")) == ["ca_yctc", "id_ctc"] + assert reporting(instant("2027-01-01")) == ["ca_yctc", "ri_ctc", "id_ctc"] + assert reporting(instant("2101-01-01")) == ["ca_yctc", "ri_ctc"] diff --git a/policyengine_us/variables/gov/states/id/tax/income/credits/id_ctc.py b/policyengine_us/variables/gov/states/id/tax/income/credits/id_ctc.py index 032866e9861..a2717427e53 100644 --- a/policyengine_us/variables/gov/states/id/tax/income/credits/id_ctc.py +++ b/policyengine_us/variables/gov/states/id/tax/income/credits/id_ctc.py @@ -5,6 +5,13 @@ class id_ctc(Variable): value_type = float entity = TaxUnit label = "Idaho Child Tax Credit" + documentation = ( + "Worksheet amount of the Idaho child tax credit. Idaho Code 63-3029L(1) " + "allows the credit only for taxable years beginning before January 1, " + "2026; from 2026 the nonrefundable credit list and the state child tax " + "credit totals leave it out, so it applies only under a reform that " + "revives it." + ) unit = USD definition_period = YEAR defined_for = StateCode.ID