Skip to content
Open
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/ga-ctc-refundable-contrib.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Georgia refundable child tax credit contributed reform.
1 change: 1 addition & 0 deletions changelog.d/id-ctc-revival-contrib.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Idaho child tax credit revival contributed reform, with an optional refundable top-up.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Georgia refunds up to this amount per eligible child under its refundable child tax credit reform.
values:
2026-01-01: 250

metadata:
unit: currency-USD
period: year
label: Georgia refundable child tax credit amount per child
reference:
- title: Georgia HB 136 (2025)
href: https://legiscan.com/GA/text/HB136/id/3204611/Georgia-2025-HB136-Enrolled.pdf#page=2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Georgia makes its child tax credit refundable if this is true.
values:
0000-01-01: false

metadata:
unit: bool
period: year
label: Georgia refundable child tax credit in effect
reference:
- title: Georgia HB 136 (2025)
href: https://legiscan.com/GA/text/HB136/id/3204611/Georgia-2025-HB136-Enrolled.pdf#page=2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Idaho revives its expired child tax credit if this is true.
values:
0000-01-01: false

metadata:
unit: bool
period: year
label: Idaho child tax credit revival in effect
reference:
- title: Idaho Statutes Title 63, Section 63-3029L (1)
href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3029L/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Idaho refunds up to this amount per qualifying child under its refundable child tax credit reform.
values:
2026-01-01: 205

metadata:
unit: currency-USD
period: year
label: Idaho refundable child tax credit amount per child
reference:
- title: Idaho Statutes Title 63, Section 63-3029L (1)
href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3029L/
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Idaho makes its revived child tax credit refundable if this is true.
values:
0000-01-01: false

metadata:
unit: bool
period: year
label: Idaho refundable child tax credit in effect
reference:
- title: Idaho Statutes Title 63, Section 63-3029L (1)
href: https://legislature.idaho.gov/statutesrules/idstat/Title63/T63CH30/SECT63-3029L/
10 changes: 10 additions & 0 deletions policyengine_us/reforms/reforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,12 @@
from .states.id.s1450 import (
create_id_s1450_reform,
)
from .states.id.ctc import (
create_id_ctc_reform,
)
from .states.ga.ctc import (
create_ga_ctc_reform,
)
from .states.il.sb3567 import (
create_il_sb3567_reform,
)
Expand Down Expand Up @@ -497,6 +503,8 @@ def create_structural_reforms_from_parameters(parameters, period):
ga_eitc = create_ga_eitc_reform(parameters, period)
id_eitc = create_id_eitc_reform(parameters, period)
id_s1450 = create_id_s1450_reform(parameters, period)
id_ctc = create_id_ctc_reform(parameters, period)
ga_ctc = create_ga_ctc_reform(parameters, period)
il_sb3567 = create_il_sb3567_reform(parameters, period)
ky_eitc = create_ky_eitc_reform(parameters, period)
ms_eitc = create_ms_eitc_reform(parameters, period)
Expand Down Expand Up @@ -628,6 +636,8 @@ def create_structural_reforms_from_parameters(parameters, period):
ga_eitc,
id_eitc,
id_s1450,
id_ctc,
ga_ctc,
il_sb3567,
ky_eitc,
ms_eitc,
Expand Down
4 changes: 4 additions & 0 deletions policyengine_us/reforms/states/ga/ctc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .ga_ctc_reform import (
create_ga_ctc_reform,
ga_ctc_reform,
)
83 changes: 83 additions & 0 deletions policyengine_us/reforms/states/ga/ctc/ga_ctc_reform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
from policyengine_us.model_api import *
from policyengine_core.periods import period as period_
from policyengine_us.variables.gov.states.tax.income.non_refundable_credit_cap import (
state_non_refundable_credit_limit,
)


def create_ga_ctc() -> Reform:
class ga_refundable_ctc(Variable):
value_type = float
entity = TaxUnit
label = "Georgia refundable child tax credit"
unit = USD
definition_period = YEAR
defined_for = StateCode.GA

def formula(tax_unit, period, parameters):
p = parameters(period).gov.contrib.states.ga.ctc
# Potential is the full Georgia CTC worksheet amount before the
# nonrefundable liability limit.
potential = tax_unit("ga_ctc_potential", period)
ordered_credits = parameters(
period
).gov.states.ga.tax.income.credits.non_refundable
credit_limit = state_non_refundable_credit_limit(
tax_unit,
period,
ordered_credits,
"ga_income_tax_before_non_refundable_credits",
"ga_ctc",
)
non_refundable = min_(potential, credit_limit)
unused_credit = max_(potential - non_refundable, 0)
# Cap the refund at amount per eligible child (same child count as
# ga_ctc_potential: qualifying children under the age threshold).
baseline = parameters(period).gov.states.ga.tax.income.credits.ctc
person = tax_unit.members
age = person("age", period)
ctc_eligible_child = person("ctc_qualifying_child", period)
ga_child_age_eligible = age < baseline.age_threshold
eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible)
refund_limit = p.refundable.amount * eligible_children
return min_(unused_credit, refund_limit)

class ga_refundable_credits(Variable):
value_type = float
entity = TaxUnit
label = "Georgia refundable credits"
unit = USD
definition_period = YEAR
defined_for = StateCode.GA
adds = ["ga_refundable_ctc"]

class reform(Reform):
def apply(self):
self.update_variable(ga_refundable_ctc)
self.update_variable(ga_refundable_credits)

return reform


def create_ga_ctc_reform(parameters, period, bypass: bool = False):
if bypass:
return create_ga_ctc()

p = parameters.gov.contrib.states.ga.ctc

reform_active = False
current_period = period_(period)

for _ in range(5):
if p(current_period).refundable.in_effect:
reform_active = True
break
current_period = current_period.offset(1, "year")

if reform_active:
return create_ga_ctc()
else:
return None


ga_ctc_reform = create_ga_ctc_reform(None, None, bypass=True)
4 changes: 4 additions & 0 deletions policyengine_us/reforms/states/id/ctc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .id_ctc_reform import (
create_id_ctc_reform,
id_ctc_reform,
)
94 changes: 94 additions & 0 deletions policyengine_us/reforms/states/id/ctc/id_ctc_reform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from policyengine_us.model_api import *
from policyengine_core.periods import instant
from policyengine_core.periods import period as period_
from policyengine_us.variables.gov.states.tax.income.non_refundable_credit_cap import (
state_non_refundable_credit_limit,
)


def create_id_ctc() -> Reform:
class id_refundable_ctc(Variable):
value_type = float
entity = TaxUnit
label = "Idaho refundable child tax credit"
unit = USD
definition_period = YEAR
defined_for = StateCode.ID

def formula(tax_unit, period, parameters):
p = parameters(period).gov.contrib.states.id.ctc
# Potential is the revived (nonrefundable) Idaho CTC worksheet
# amount, ctc_qualifying_children * amount.
potential = tax_unit("id_ctc", period)
ordered_credits = parameters(
period
).gov.states.id.tax.income.credits.non_refundable
credit_limit = state_non_refundable_credit_limit(
tax_unit,
period,
ordered_credits,
"id_income_tax_before_non_refundable_credits",
"id_ctc",
)
non_refundable = min_(potential, credit_limit)
unused_credit = max_(potential - non_refundable, 0)
eligible_children = tax_unit("ctc_qualifying_children", period)
refund_limit = p.refundable.amount * eligible_children
refundable_credit = min_(unused_credit, refund_limit)
# Only pay the refundable portion when the refundable option is on;
# otherwise the revival stays purely nonrefundable.
return where(p.refundable.in_effect, refundable_credit, 0)

def modify_parameters(parameters):
# Revive id_ctc in the ordered nonrefundable list (it was dropped as of
# 2026), so the baseline credit applies against liability again.
non_refundable = parameters.gov.states.id.tax.income.credits.non_refundable
current_non_refundable = non_refundable(instant("2026-01-01"))
if "id_ctc" not in current_non_refundable:
non_refundable.update(
start=instant("2026-01-01"),
stop=instant("2100-12-31"),
value=list(current_non_refundable) + ["id_ctc"],
)
# Register the refundable top-up in the refundable list.
refundable = parameters.gov.states.id.tax.income.credits.refundable
current_refundable = refundable(instant("2026-01-01"))
if "id_refundable_ctc" not in current_refundable:
refundable.update(
start=instant("2026-01-01"),
stop=instant("2100-12-31"),
value=list(current_refundable) + ["id_refundable_ctc"],
)
return parameters

class reform(Reform):
def apply(self):
self.update_variable(id_refundable_ctc)
self.modify_parameters(modify_parameters)

return reform


def create_id_ctc_reform(parameters, period, bypass: bool = False):
if bypass:
return create_id_ctc()

p = parameters.gov.contrib.states.id.ctc

reform_active = False
current_period = period_(period)

for _ in range(5):
node = p(current_period)
if node.in_effect or node.refundable.in_effect:
reform_active = True
break
current_period = current_period.offset(1, "year")

if reform_active:
return create_id_ctc()
else:
return None


id_ctc_reform = create_id_ctc_reform(None, None, bypass=True)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
"variables/gov/states/de/tax/income/credits/eitc/de_eitc.py",
"variables/gov/states/de/tax/income/credits/eitc/refundability_calculation/de_income_tax_if_claiming_non_refundable_eitc.py",
},
"ga_ctc": {
# Georgia refundable CTC contrib reform references "ga_ctc" only as the
# ordering key for state_non_refundable_credit_limit; it reads the
# pre-ordering ga_ctc_potential for the value, not the applied credit.
"reforms/states/ga/ctc/ga_ctc_reform.py",
},
"ky_personal_tax_credits": {
"variables/gov/states/ky/tax/income/credits/family_size_credit/ky_family_size_tax_credit_potential.py",
},
Expand Down
Loading
Loading