Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/credits-2026-id-ctc-co-fatc.fixed.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ 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
- co_ctc
- co_family_affordability_credit
- dc_ctc
- ga_ctc
- id_ctc
- il_ctc
- ma_child_and_family_credit_or_dependent_care_credit
- md_ctc
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
28 changes: 28 additions & 0 deletions policyengine_us/reforms/states/id/ctc/id_ctc_reform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions policyengine_us/reforms/states/id/s1450/s1450.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -73,3 +77,4 @@
output:
id_ctc: 600
id_non_refundable_credits: 600
taxsim_state_ctc: 600
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"]
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down