From 55fca36691697beebef9b5e4bea96fe6028ac0a7 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 3 Mar 2026 10:32:11 -0500 Subject: [PATCH 1/7] Add Georgia SB 520 contributed reform Implements progressive income tax brackets (2027), increased standard deduction with phase-out, enhanced child tax credit ($1,250 refundable), and new Georgia EITC (20% of federal). Co-Authored-By: Claude Opus 4.5 --- changelog.d/ga-sb520-tax-reform.added.md | 1 + .../states/ga/sb520/credits/ctc/amount.yaml | 12 + .../states/ga/sb520/credits/eitc/match.yaml | 12 + .../ga/sb520/deductions/standard/amount.yaml | 22 ++ .../deductions/standard/phase_out/rate.yaml | 12 + .../standard/phase_out/threshold.yaml | 22 ++ .../states/ga/sb520/in_effect_2026.yaml | 12 + .../states/ga/sb520/in_effect_2027.yaml | 12 + .../ga/sb520/tax/head_of_household.yaml | 24 ++ .../contrib/states/ga/sb520/tax/joint.yaml | 24 ++ .../contrib/states/ga/sb520/tax/separate.yaml | 24 ++ .../contrib/states/ga/sb520/tax/single.yaml | 24 ++ .../states/ga/sb520/tax/surviving_spouse.yaml | 24 ++ policyengine_us/reforms/reforms.py | 5 + .../reforms/states/ga/sb520/__init__.py | 3 + .../states/ga/sb520/ga_sb520_reform.py | 241 ++++++++++++++++++ .../contrib/states/ga/sb520/integration.yaml | 119 +++++++++ 17 files changed, 593 insertions(+) create mode 100644 changelog.d/ga-sb520-tax-reform.added.md create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml create mode 100644 policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml create mode 100644 policyengine_us/reforms/states/ga/sb520/__init__.py create mode 100644 policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py create mode 100644 policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml diff --git a/changelog.d/ga-sb520-tax-reform.added.md b/changelog.d/ga-sb520-tax-reform.added.md new file mode 100644 index 00000000000..5fc01128936 --- /dev/null +++ b/changelog.d/ga-sb520-tax-reform.added.md @@ -0,0 +1 @@ +Added Georgia SB 520 contributed reform implementing progressive income tax brackets (2027), increased standard deduction with phase-out, enhanced child tax credit ($1,250 refundable), and new Georgia EITC (20% of federal). diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml new file mode 100644 index 00000000000..adf0e5c2eaf --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml @@ -0,0 +1,12 @@ +description: Georgia SB 520 provides this child tax credit amount for each qualifying child. + +values: + 2026-01-01: 1_250 + +metadata: + unit: currency-USD + period: year + label: Georgia SB 520 child tax credit amount + reference: + - title: Georgia SB 520 Section 6 - 48-7-29.27(b)(1) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=6 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml new file mode 100644 index 00000000000..334e55a543e --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml @@ -0,0 +1,12 @@ +description: Georgia SB 520 provides a state EITC equal to this percentage of the federal EITC. + +values: + 2026-01-01: 0.20 + +metadata: + unit: /1 + period: year + label: Georgia SB 520 EITC match rate + reference: + - title: Georgia SB 520 Section 7 - 48-7-29.29(a) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=6 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml new file mode 100644 index 00000000000..d403643f125 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml @@ -0,0 +1,22 @@ +description: Georgia SB 520 provides filers a standard deduction of this amount, depending on filing status. + +metadata: + label: Georgia SB 520 standard deduction amount + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Georgia SB 520 Section 4 - 48-7-27(a)(1) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 + +JOINT: + 2026-01-01: 34_000 +HEAD_OF_HOUSEHOLD: + 2026-01-01: 17_000 +SINGLE: + 2026-01-01: 17_000 +SEPARATE: + 2026-01-01: 17_000 +SURVIVING_SPOUSE: + 2026-01-01: 34_000 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml new file mode 100644 index 00000000000..171ff1b3f8e --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml @@ -0,0 +1,12 @@ +description: Georgia SB 520 reduces the standard deduction by this rate for each dollar of federal AGI above the threshold. + +values: + 2026-01-01: 0.25 + +metadata: + label: Georgia SB 520 standard deduction phase-out rate + period: year + unit: /1 + reference: + - title: Georgia SB 520 Section 4 - 48-7-27(a)(1)(C) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml new file mode 100644 index 00000000000..7127f47e3a8 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml @@ -0,0 +1,22 @@ +description: Georgia SB 520 phases out the standard deduction for filers with federal AGI above this threshold. + +metadata: + label: Georgia SB 520 standard deduction phase-out threshold + period: year + unit: currency-USD + breakdown: + - filing_status + reference: + - title: Georgia SB 520 Section 4 - 48-7-27(a)(1)(C) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 + +JOINT: + 2026-01-01: 280_000 +HEAD_OF_HOUSEHOLD: + 2026-01-01: 140_000 +SINGLE: + 2026-01-01: 140_000 +SEPARATE: + 2026-01-01: 140_000 +SURVIVING_SPOUSE: + 2026-01-01: 280_000 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml new file mode 100644 index 00000000000..d23afc01214 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml @@ -0,0 +1,12 @@ +description: Georgia applies SB 520 2026 provisions (standard deduction increase, child tax credit increase, state EITC). + +values: + 2026-01-01: false + +metadata: + unit: bool + period: year + label: Georgia SB 520 2026 provisions in effect + reference: + - title: Georgia SB 520 Sections 4, 6, 7 + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=1 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml new file mode 100644 index 00000000000..b3f27f76e6b --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml @@ -0,0 +1,12 @@ +description: Georgia applies SB 520 2027 provisions (progressive income tax brackets). + +values: + 2027-01-01: false + +metadata: + unit: bool + period: year + label: Georgia SB 520 2027 provisions in effect + reference: + - title: Georgia SB 520 Section 1 + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=1 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml new file mode 100644 index 00000000000..01c93c83ea7 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml @@ -0,0 +1,24 @@ +description: Georgia SB 520 taxes the personal income of head of household filers at these progressive rates. + +metadata: + type: marginal_rate + threshold_unit: currency-USD + rate_unit: /1 + label: Georgia SB 520 income tax rate head of household filers + reference: + - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + +brackets: + - threshold: + 2027-01-01: 0 + rate: + 2027-01-01: 0.02 + - threshold: + 2027-01-01: 15_000 + rate: + 2027-01-01: 0.04 + - threshold: + 2027-01-01: 30_000 + rate: + 2027-01-01: 0.06 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml new file mode 100644 index 00000000000..7b6b69252c9 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml @@ -0,0 +1,24 @@ +description: Georgia SB 520 taxes the personal income of joint filers at these progressive rates. + +metadata: + type: marginal_rate + threshold_unit: currency-USD + rate_unit: /1 + label: Georgia SB 520 income tax rate joint filers + reference: + - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(1) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + +brackets: + - threshold: + 2027-01-01: 0 + rate: + 2027-01-01: 0.02 + - threshold: + 2027-01-01: 30_000 + rate: + 2027-01-01: 0.04 + - threshold: + 2027-01-01: 60_000 + rate: + 2027-01-01: 0.06 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml new file mode 100644 index 00000000000..4b5243e4332 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml @@ -0,0 +1,24 @@ +description: Georgia SB 520 taxes the personal income of married filing separately filers at these progressive rates. + +metadata: + type: marginal_rate + threshold_unit: currency-USD + rate_unit: /1 + label: Georgia SB 520 income tax rate married filing separately filers + reference: + - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + +brackets: + - threshold: + 2027-01-01: 0 + rate: + 2027-01-01: 0.02 + - threshold: + 2027-01-01: 15_000 + rate: + 2027-01-01: 0.04 + - threshold: + 2027-01-01: 30_000 + rate: + 2027-01-01: 0.06 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml new file mode 100644 index 00000000000..26c415f9c5b --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml @@ -0,0 +1,24 @@ +description: Georgia SB 520 taxes the personal income of single filers at these progressive rates. + +metadata: + type: marginal_rate + threshold_unit: currency-USD + rate_unit: /1 + label: Georgia SB 520 income tax rate single filers + reference: + - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + +brackets: + - threshold: + 2027-01-01: 0 + rate: + 2027-01-01: 0.02 + - threshold: + 2027-01-01: 15_000 + rate: + 2027-01-01: 0.04 + - threshold: + 2027-01-01: 30_000 + rate: + 2027-01-01: 0.06 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml new file mode 100644 index 00000000000..f4266bdc137 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml @@ -0,0 +1,24 @@ +description: Georgia SB 520 taxes the personal income of surviving spouse filers at these progressive rates. + +metadata: + type: marginal_rate + threshold_unit: currency-USD + rate_unit: /1 + label: Georgia SB 520 income tax rate surviving spouse filers + reference: + - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(1) + href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + +brackets: + - threshold: + 2027-01-01: 0 + rate: + 2027-01-01: 0.02 + - threshold: + 2027-01-01: 30_000 + rate: + 2027-01-01: 0.04 + - threshold: + 2027-01-01: 60_000 + rate: + 2027-01-01: 0.06 diff --git a/policyengine_us/reforms/reforms.py b/policyengine_us/reforms/reforms.py index e4f6a2ffbc5..c038edae03e 100644 --- a/policyengine_us/reforms/reforms.py +++ b/policyengine_us/reforms/reforms.py @@ -173,6 +173,9 @@ ) +from .states.ga.sb520 import ( + create_ga_sb520_reform, +) from policyengine_core.reforms import Reform import warnings @@ -372,6 +375,7 @@ def create_structural_reforms_from_parameters(parameters, period): ct_sb100 = create_ct_sb100_reform(parameters, period) ct_tax_rebate_2026 = create_ct_tax_rebate_2026_reform(parameters, period) ct_hb5009 = create_ct_hb5009_reform(parameters, period) + ga_sb520 = create_ga_sb520_reform(parameters, period) reforms = [ afa_reform, @@ -451,6 +455,7 @@ def create_structural_reforms_from_parameters(parameters, period): ct_hb5009, ct_sb100, ct_tax_rebate_2026, + ga_sb520, ] reforms = tuple(filter(lambda x: x is not None, reforms)) diff --git a/policyengine_us/reforms/states/ga/sb520/__init__.py b/policyengine_us/reforms/states/ga/sb520/__init__.py new file mode 100644 index 00000000000..46c38ef84a0 --- /dev/null +++ b/policyengine_us/reforms/states/ga/sb520/__init__.py @@ -0,0 +1,3 @@ +from .ga_sb520_reform import ga_sb520, create_ga_sb520_reform + +__all__ = ["ga_sb520", "create_ga_sb520_reform"] diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py new file mode 100644 index 00000000000..f146e7edf24 --- /dev/null +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -0,0 +1,241 @@ +from policyengine_us.model_api import * +from policyengine_core.periods import period as period_ + + +def create_ga_sb520() -> Reform: + class ga_standard_deduction(Variable): + value_type = float + entity = TaxUnit + label = "Georgia standard deduction" + unit = USD + definition_period = YEAR + reference = ( + "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5", + ) + defined_for = StateCode.GA + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.ga.tax.income.deductions.standard + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + filing_status = tax_unit("filing_status", period) + + sb520_active = p_sb520.in_effect_2026 + + sb520_base = p_sb520.deductions.standard.amount[filing_status] + sb520_threshold = p_sb520.deductions.standard.phase_out.threshold[ + filing_status + ] + sb520_rate = p_sb520.deductions.standard.phase_out.rate + + agi = tax_unit("adjusted_gross_income", period) + excess_income = max_(agi - sb520_threshold, 0) + phase_out_amount = excess_income * sb520_rate + sb520_deduction = max_(sb520_base - phase_out_amount, 0) + + baseline_base = p.amount[filing_status] + if p.applies: + additional_standard = tax_unit( + "ga_additional_standard_deduction", period + ) + baseline_deduction = baseline_base + additional_standard + else: + baseline_deduction = baseline_base + + return where(sb520_active, sb520_deduction, baseline_deduction) + + class ga_ctc(Variable): + value_type = float + entity = TaxUnit + label = "Georgia Child Tax Credit" + unit = USD + definition_period = YEAR + defined_for = StateCode.GA + reference = ( + "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=7", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.ga.tax.income.credits.ctc + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_active = p_sb520.in_effect_2026 + + person = tax_unit.members + age = person("age", period) + ctc_eligible_child = person("ctc_qualifying_child", period) + ga_child_age_eligible = age < p.age_threshold + eligible_children = tax_unit.sum( + ctc_eligible_child & ga_child_age_eligible + ) + + sb520_amount = p_sb520.credits.ctc.amount + baseline_amount = p.amount + + amount_per_child = where( + sb520_active, sb520_amount, baseline_amount + ) + return eligible_children * amount_per_child + + class ga_eitc(Variable): + value_type = float + entity = TaxUnit + label = "Georgia Earned Income Tax Credit" + unit = USD + definition_period = YEAR + defined_for = StateCode.GA + reference = ( + "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=8", + ) + + def formula(tax_unit, period, parameters): + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_active = p_sb520.in_effect_2026 + + federal_eitc = tax_unit("eitc", period) + match_rate = p_sb520.credits.eitc.match + + ga_eitc_amount = federal_eitc * match_rate + + return where(sb520_active, ga_eitc_amount, 0) + + class ga_refundable_credits(Variable): + value_type = float + entity = TaxUnit + label = "Georgia refundable credits" + unit = USD + definition_period = YEAR + defined_for = StateCode.GA + + def formula(tax_unit, period, parameters): + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_active = p_sb520.in_effect_2026 + + ga_ctc_amount = tax_unit("ga_ctc", period) + ga_eitc_amount = tax_unit("ga_eitc", period) + + sb520_refundable = ga_ctc_amount + ga_eitc_amount + + return where(sb520_active, sb520_refundable, 0) + + class ga_non_refundable_credits(Variable): + value_type = float + entity = TaxUnit + label = "Georgia non-refundable credits" + unit = USD + definition_period = YEAR + defined_for = StateCode.GA + + def formula(tax_unit, period, parameters): + p = parameters( + period + ).gov.states.ga.tax.income.credits.non_refundable + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_active = p_sb520.in_effect_2026 + + baseline_credits = add(tax_unit, period, p) + + ga_ctc_amount = tax_unit("ga_ctc", period) + + return where( + sb520_active, + max_(baseline_credits - ga_ctc_amount, 0), + baseline_credits, + ) + + class ga_income_tax_before_non_refundable_credits(Variable): + value_type = float + entity = TaxUnit + label = "Georgia income tax before non-refundable credits" + unit = USD + definition_period = YEAR + defined_for = StateCode.GA + reference = ( + "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2", + ) + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.states.ga.tax.income.main + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_2027_active = p_sb520.in_effect_2027 + + filing_status = tax_unit("filing_status", period) + status = filing_status.possible_values + income = tax_unit("ga_taxable_income", period) + + sb520_tax = select( + [ + filing_status == status.SINGLE, + filing_status == status.SEPARATE, + filing_status == status.JOINT, + filing_status == status.HEAD_OF_HOUSEHOLD, + filing_status == status.SURVIVING_SPOUSE, + ], + [ + p_sb520.tax.single.calc(income), + p_sb520.tax.separate.calc(income), + p_sb520.tax.joint.calc(income), + p_sb520.tax.head_of_household.calc(income), + p_sb520.tax.surviving_spouse.calc(income), + ], + ) + + baseline_tax = select( + [ + filing_status == status.SINGLE, + filing_status == status.SEPARATE, + filing_status == status.JOINT, + filing_status == status.HEAD_OF_HOUSEHOLD, + filing_status == status.SURVIVING_SPOUSE, + ], + [ + p.single.calc(income), + p.separate.calc(income), + p.joint.calc(income), + p.head_of_household.calc(income), + p.surviving_spouse.calc(income), + ], + ) + + return where(sb520_2027_active, sb520_tax, baseline_tax) + + class reform(Reform): + def apply(self): + self.update_variable(ga_standard_deduction) + self.update_variable(ga_ctc) + self.update_variable(ga_eitc) + self.update_variable(ga_refundable_credits) + self.update_variable(ga_non_refundable_credits) + self.update_variable(ga_income_tax_before_non_refundable_credits) + + return reform + + +def create_ga_sb520_reform(parameters, period, bypass: bool = False): + if bypass: + return create_ga_sb520() + + p_sb520 = parameters.gov.contrib.states.ga.sb520 + + reform_active = False + current_period = period_(period) + + for i in range(5): + if ( + p_sb520(current_period).in_effect_2026 + or p_sb520(current_period).in_effect_2027 + ): + reform_active = True + break + current_period = current_period.offset(1, "year") + + if reform_active: + return create_ga_sb520() + else: + return None + + +ga_sb520 = create_ga_sb520_reform(None, None, bypass=True) diff --git a/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml new file mode 100644 index 00000000000..c742cb04710 --- /dev/null +++ b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml @@ -0,0 +1,119 @@ +- name: Case 1, single filer with 2026 provisions. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect_2026: true + people: + person1: + age: 30 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: GA + output: + ga_standard_deduction: 17_000 + +- name: Case 2, joint filers with 2026 provisions and children. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect_2026: true + people: + person1: + age: 35 + employment_income: 80_000 + person2: + age: 33 + child1: + age: 5 + child2: + age: 8 + tax_units: + tax_unit: + members: [person1, person2, child1, child2] + households: + household: + members: [person1, person2, child1, child2] + state_code: GA + output: + ga_standard_deduction: 34_000 + ga_ctc: 2_500 + +- name: Case 3, standard deduction phase-out. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect_2026: true + people: + person1: + age: 45 + employment_income: 320_000 + person2: + age: 43 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: GA + output: + # AGI: 320,000 + # Excess: 320,000 - 280,000 = 40,000 + # Phase-out: 40,000 * 0.25 = 10,000 + # Deduction: 34,000 - 10,000 = 24,000 + ga_standard_deduction: 24_000 + +- name: Case 4, Georgia EITC with federal EITC. + period: 2026 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect_2026: true + people: + person1: + age: 30 + employment_income: 25_000 + child1: + age: 5 + tax_units: + tax_unit: + members: [person1, child1] + households: + household: + members: [person1, child1] + state_code: GA + output: + # GA EITC = 20% of federal EITC + ga_eitc__gte: 0 + +- name: Case 5, 2027 progressive brackets for single filer. + period: 2027 + absolute_error_margin: 1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect_2027: true + people: + person1: + age: 30 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: GA + output: + # With SB520 deduction of 17,000: + # Taxable income: 50,000 - 17,000 = 33,000 + # Tax: 15,000 * 0.02 + 15,000 * 0.04 + 3,000 * 0.06 + # = 300 + 600 + 180 = 1,080 + ga_income_tax_before_non_refundable_credits__gte: 0 From d1089cf13590af544ac72935a9add4734cfa1b41 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 3 Mar 2026 11:10:40 -0500 Subject: [PATCH 2/7] Fix parameter naming for in_effect switches Rename in_effect_2026.yaml to active2026.yaml to fix parameter access issues with underscore-separated filenames. Co-Authored-By: Claude Opus 4.5 --- .../{in_effect_2026.yaml => active2026.yaml} | 2 +- .../{in_effect_2027.yaml => active2027.yaml} | 2 +- .../reforms/states/ga/sb520/ga_sb520_reform.py | 16 ++++++++-------- .../contrib/states/ga/sb520/integration.yaml | 10 +++++----- 4 files changed, 15 insertions(+), 15 deletions(-) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{in_effect_2026.yaml => active2026.yaml} (94%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{in_effect_2027.yaml => active2027.yaml} (94%) diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2026.yaml similarity index 94% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/active2026.yaml index d23afc01214..176f7809365 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2026.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2026.yaml @@ -1,7 +1,7 @@ description: Georgia applies SB 520 2026 provisions (standard deduction increase, child tax credit increase, state EITC). values: - 2026-01-01: false + 0000-01-01: false metadata: unit: bool diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2027.yaml similarity index 94% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/active2027.yaml index b3f27f76e6b..7dce0a6af17 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect_2027.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2027.yaml @@ -1,7 +1,7 @@ description: Georgia applies SB 520 2027 provisions (progressive income tax brackets). values: - 2027-01-01: false + 0000-01-01: false metadata: unit: bool diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index f146e7edf24..bae5ef6980f 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -19,7 +19,7 @@ def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 filing_status = tax_unit("filing_status", period) - sb520_active = p_sb520.in_effect_2026 + sb520_active = p_sb520.active2026 sb520_base = p_sb520.deductions.standard.amount[filing_status] sb520_threshold = p_sb520.deductions.standard.phase_out.threshold[ @@ -58,7 +58,7 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.credits.ctc p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.in_effect_2026 + sb520_active = p_sb520.active2026 person = tax_unit.members age = person("age", period) @@ -90,7 +90,7 @@ class ga_eitc(Variable): def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.in_effect_2026 + sb520_active = p_sb520.active2026 federal_eitc = tax_unit("eitc", period) match_rate = p_sb520.credits.eitc.match @@ -110,7 +110,7 @@ class ga_refundable_credits(Variable): def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.in_effect_2026 + sb520_active = p_sb520.active2026 ga_ctc_amount = tax_unit("ga_ctc", period) ga_eitc_amount = tax_unit("ga_eitc", period) @@ -133,7 +133,7 @@ def formula(tax_unit, period, parameters): ).gov.states.ga.tax.income.credits.non_refundable p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.in_effect_2026 + sb520_active = p_sb520.active2026 baseline_credits = add(tax_unit, period, p) @@ -160,7 +160,7 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.main p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_2027_active = p_sb520.in_effect_2027 + sb520_2027_active = p_sb520.active2027 filing_status = tax_unit("filing_status", period) status = filing_status.possible_values @@ -225,8 +225,8 @@ def create_ga_sb520_reform(parameters, period, bypass: bool = False): for i in range(5): if ( - p_sb520(current_period).in_effect_2026 - or p_sb520(current_period).in_effect_2027 + p_sb520(current_period).active2026 + or p_sb520(current_period).active2027 ): reform_active = True break diff --git a/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml index c742cb04710..1918bcf5d13 100644 --- a/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml +++ b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml @@ -3,7 +3,7 @@ absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.in_effect_2026: true + gov.contrib.states.ga.sb520.active2026: true people: person1: age: 30 @@ -23,7 +23,7 @@ absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.in_effect_2026: true + gov.contrib.states.ga.sb520.active2026: true people: person1: age: 35 @@ -50,7 +50,7 @@ absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.in_effect_2026: true + gov.contrib.states.ga.sb520.active2026: true people: person1: age: 45 @@ -76,7 +76,7 @@ absolute_error_margin: 1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.in_effect_2026: true + gov.contrib.states.ga.sb520.active2026: true people: person1: age: 30 @@ -99,7 +99,7 @@ absolute_error_margin: 1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.in_effect_2027: true + gov.contrib.states.ga.sb520.active2027: true people: person1: age: 30 From 20415df3514e333f4b79096cfd1bb7bfdd35ffa5 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 3 Mar 2026 13:36:33 -0500 Subject: [PATCH 3/7] Restructure GA SB 520 parameters and fix CTC calculation - Reorganize parameter folder structure: - in_effect.yaml at sb520/ for 2026 provisions - brackets/ folder with in_effect.yaml for 2027 tax brackets - Rename tax/ to brackets/ - Fix CTC to use baseline GA age threshold (under 6) - Fix test expected values for CTC (1 child qualifies, not 2) - Add edge case tests (fully phased out deduction, no qualifying children, high income) - Update all parameter path references in reform code and tests Co-Authored-By: Claude Opus 4.5 --- .../{tax => brackets}/head_of_household.yaml | 0 .../in_effect.yaml} | 0 .../ga/sb520/{tax => brackets}/joint.yaml | 0 .../ga/sb520/{tax => brackets}/separate.yaml | 0 .../ga/sb520/{tax => brackets}/single.yaml | 0 .../{tax => brackets}/surviving_spouse.yaml | 0 .../sb520/{active2026.yaml => in_effect.yaml} | 0 .../states/ga/sb520/ga_sb520_reform.py | 29 +++-- .../contrib/states/ga/sb520/integration.yaml | 113 ++++++++++++++++-- 9 files changed, 119 insertions(+), 23 deletions(-) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{tax => brackets}/head_of_household.yaml (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{active2027.yaml => brackets/in_effect.yaml} (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{tax => brackets}/joint.yaml (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{tax => brackets}/separate.yaml (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{tax => brackets}/single.yaml (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{tax => brackets}/surviving_spouse.yaml (100%) rename policyengine_us/parameters/gov/contrib/states/ga/sb520/{active2026.yaml => in_effect.yaml} (100%) diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/head_of_household.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2027.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/active2027.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/joint.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/separate.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/single.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/tax/surviving_spouse.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/active2026.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml similarity index 100% rename from policyengine_us/parameters/gov/contrib/states/ga/sb520/active2026.yaml rename to policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index bae5ef6980f..ab279a9e89e 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -19,7 +19,7 @@ def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 filing_status = tax_unit("filing_status", period) - sb520_active = p_sb520.active2026 + sb520_active = p_sb520.in_effect sb520_base = p_sb520.deductions.standard.amount[filing_status] sb520_threshold = p_sb520.deductions.standard.phase_out.threshold[ @@ -58,11 +58,12 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.credits.ctc p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.active2026 + sb520_active = p_sb520.in_effect person = tax_unit.members age = person("age", period) ctc_eligible_child = person("ctc_qualifying_child", period) + # SB 520 preserves baseline GA CTC age threshold (under 6) ga_child_age_eligible = age < p.age_threshold eligible_children = tax_unit.sum( ctc_eligible_child & ga_child_age_eligible @@ -90,7 +91,7 @@ class ga_eitc(Variable): def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.active2026 + sb520_active = p_sb520.in_effect federal_eitc = tax_unit("eitc", period) match_rate = p_sb520.credits.eitc.match @@ -110,7 +111,7 @@ class ga_refundable_credits(Variable): def formula(tax_unit, period, parameters): p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.active2026 + sb520_active = p_sb520.in_effect ga_ctc_amount = tax_unit("ga_ctc", period) ga_eitc_amount = tax_unit("ga_eitc", period) @@ -133,7 +134,7 @@ def formula(tax_unit, period, parameters): ).gov.states.ga.tax.income.credits.non_refundable p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_active = p_sb520.active2026 + sb520_active = p_sb520.in_effect baseline_credits = add(tax_unit, period, p) @@ -160,7 +161,7 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ga.tax.income.main p_sb520 = parameters(period).gov.contrib.states.ga.sb520 - sb520_2027_active = p_sb520.active2027 + sb520_2027_active = p_sb520.brackets.in_effect filing_status = tax_unit("filing_status", period) status = filing_status.possible_values @@ -175,11 +176,11 @@ def formula(tax_unit, period, parameters): filing_status == status.SURVIVING_SPOUSE, ], [ - p_sb520.tax.single.calc(income), - p_sb520.tax.separate.calc(income), - p_sb520.tax.joint.calc(income), - p_sb520.tax.head_of_household.calc(income), - p_sb520.tax.surviving_spouse.calc(income), + p_sb520.brackets.single.calc(income), + p_sb520.brackets.separate.calc(income), + p_sb520.brackets.joint.calc(income), + p_sb520.brackets.head_of_household.calc(income), + p_sb520.brackets.surviving_spouse.calc(income), ], ) @@ -224,10 +225,8 @@ def create_ga_sb520_reform(parameters, period, bypass: bool = False): current_period = period_(period) for i in range(5): - if ( - p_sb520(current_period).active2026 - or p_sb520(current_period).active2027 - ): + p_at_period = p_sb520(current_period) + if p_at_period.in_effect or p_at_period.brackets.in_effect: reform_active = True break current_period = current_period.offset(1, "year") diff --git a/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml index 1918bcf5d13..82fa5923f85 100644 --- a/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml +++ b/policyengine_us/tests/policy/contrib/states/ga/sb520/integration.yaml @@ -3,7 +3,7 @@ absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.active2026: true + gov.contrib.states.ga.sb520.in_effect: true people: person1: age: 30 @@ -23,7 +23,7 @@ absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.active2026: true + gov.contrib.states.ga.sb520.in_effect: true people: person1: age: 35 @@ -43,14 +43,15 @@ state_code: GA output: ga_standard_deduction: 34_000 - ga_ctc: 2_500 + # Only child1 (age 5) qualifies - GA CTC age threshold is under 6 + ga_ctc: 1_250 - name: Case 3, standard deduction phase-out. period: 2026 absolute_error_margin: 0.1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.active2026: true + gov.contrib.states.ga.sb520.in_effect: true people: person1: age: 45 @@ -76,7 +77,7 @@ absolute_error_margin: 1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.active2026: true + gov.contrib.states.ga.sb520.in_effect: true people: person1: age: 30 @@ -92,14 +93,17 @@ state_code: GA output: # GA EITC = 20% of federal EITC - ga_eitc__gte: 0 + # Federal EITC for single parent with 1 child earning $25,000 is approximately $4,242 + # GA EITC = $4,242 * 0.20 = $848 + ga_eitc: 848 - name: Case 5, 2027 progressive brackets for single filer. period: 2027 absolute_error_margin: 1 reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 input: - gov.contrib.states.ga.sb520.active2027: true + gov.contrib.states.ga.sb520.in_effect: true + gov.contrib.states.ga.sb520.brackets.in_effect: true people: person1: age: 30 @@ -116,4 +120,97 @@ # Taxable income: 50,000 - 17,000 = 33,000 # Tax: 15,000 * 0.02 + 15,000 * 0.04 + 3,000 * 0.06 # = 300 + 600 + 180 = 1,080 - ga_income_tax_before_non_refundable_credits__gte: 0 + ga_income_tax_before_non_refundable_credits: 1_080 + +- name: Case 6, standard deduction fully phased out. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect: true + people: + person1: + age: 40 + employment_income: 500_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: GA + output: + # AGI: 500,000 + # Threshold for single: 140,000 + # Excess: 500,000 - 140,000 = 360,000 + # Phase-out: 360,000 * 0.25 = 90,000 + # Deduction: max(17,000 - 90,000, 0) = 0 + ga_standard_deduction: 0 + +- name: Case 7, CTC with no qualifying children (all above age 6). + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect: true + people: + person1: + age: 40 + employment_income: 60_000 + child1: + age: 10 + child2: + age: 14 + tax_units: + tax_unit: + members: [person1, child1, child2] + households: + household: + members: [person1, child1, child2] + state_code: GA + output: + # No children under age 6, so no GA CTC + ga_ctc: 0 + +- name: Case 8, high income single filer with no EITC. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect: true + people: + person1: + age: 35 + employment_income: 100_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: GA + output: + # Income too high for federal EITC, so no GA EITC + ga_eitc: 0 + +- name: Case 9, income exactly at phase-out threshold. + period: 2026 + absolute_error_margin: 0.1 + reforms: policyengine_us.reforms.states.ga.sb520.ga_sb520 + input: + gov.contrib.states.ga.sb520.in_effect: true + people: + person1: + age: 40 + employment_income: 140_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: GA + output: + # AGI exactly at threshold (140,000 for single) + # No phase-out yet + ga_standard_deduction: 17_000 From 71d9407a7f07301585815cf483f49381a786a190 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 3 Mar 2026 18:32:59 -0500 Subject: [PATCH 4/7] Fix reference URLs and page numbers for Georgia SB 520 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace incorrect document ID 232863 (HB 169) with correct ID 242809 (SB 520) in all 16 references - Fix page numbers: bracket files now correctly reference page 3 (48-7-20(a.1)) - Fix page numbers in Python: CTC and EITC now reference page 6, brackets reference page 3 - Remove trailing zero in EITC match rate (0.20 → 0.2) Co-Authored-By: Claude Opus 4.5 --- .../states/ga/sb520/brackets/head_of_household.yaml | 2 +- .../gov/contrib/states/ga/sb520/brackets/in_effect.yaml | 2 +- .../gov/contrib/states/ga/sb520/brackets/joint.yaml | 2 +- .../gov/contrib/states/ga/sb520/brackets/separate.yaml | 2 +- .../gov/contrib/states/ga/sb520/brackets/single.yaml | 2 +- .../states/ga/sb520/brackets/surviving_spouse.yaml | 2 +- .../gov/contrib/states/ga/sb520/credits/ctc/amount.yaml | 2 +- .../gov/contrib/states/ga/sb520/credits/eitc/match.yaml | 4 ++-- .../states/ga/sb520/deductions/standard/amount.yaml | 2 +- .../ga/sb520/deductions/standard/phase_out/rate.yaml | 2 +- .../ga/sb520/deductions/standard/phase_out/threshold.yaml | 2 +- .../parameters/gov/contrib/states/ga/sb520/in_effect.yaml | 2 +- .../reforms/states/ga/sb520/ga_sb520_reform.py | 8 ++++---- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml index 01c93c83ea7..fabed9f0f60 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/head_of_household.yaml @@ -7,7 +7,7 @@ metadata: label: Georgia SB 520 income tax rate head of household filers reference: - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3 brackets: - threshold: diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml index 7dce0a6af17..ee87f581209 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/in_effect.yaml @@ -9,4 +9,4 @@ metadata: label: Georgia SB 520 2027 provisions in effect reference: - title: Georgia SB 520 Section 1 - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=1 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=2 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml index 7b6b69252c9..f3d4f1b6187 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/joint.yaml @@ -7,7 +7,7 @@ metadata: label: Georgia SB 520 income tax rate joint filers reference: - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(1) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3 brackets: - threshold: diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml index 4b5243e4332..3971f2317ee 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/separate.yaml @@ -7,7 +7,7 @@ metadata: label: Georgia SB 520 income tax rate married filing separately filers reference: - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3 brackets: - threshold: diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml index 26c415f9c5b..5f5784913f7 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/single.yaml @@ -7,7 +7,7 @@ metadata: label: Georgia SB 520 income tax rate single filers reference: - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(2) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3 brackets: - threshold: diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml index f4266bdc137..48f03f4dc8a 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/brackets/surviving_spouse.yaml @@ -7,7 +7,7 @@ metadata: label: Georgia SB 520 income tax rate surviving spouse filers reference: - title: Georgia SB 520 Section 1 - 48-7-20(a.1)(1) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3 brackets: - threshold: diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml index adf0e5c2eaf..1b323db0b0e 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/ctc/amount.yaml @@ -9,4 +9,4 @@ metadata: label: Georgia SB 520 child tax credit amount reference: - title: Georgia SB 520 Section 6 - 48-7-29.27(b)(1) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=6 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=6 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml index 334e55a543e..aec33b0777b 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/credits/eitc/match.yaml @@ -1,7 +1,7 @@ description: Georgia SB 520 provides a state EITC equal to this percentage of the federal EITC. values: - 2026-01-01: 0.20 + 2026-01-01: 0.2 metadata: unit: /1 @@ -9,4 +9,4 @@ metadata: label: Georgia SB 520 EITC match rate reference: - title: Georgia SB 520 Section 7 - 48-7-29.29(a) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=6 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=6 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml index d403643f125..b9176bd0dc2 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/amount.yaml @@ -8,7 +8,7 @@ metadata: - filing_status reference: - title: Georgia SB 520 Section 4 - 48-7-27(a)(1) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=5 JOINT: 2026-01-01: 34_000 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml index 171ff1b3f8e..61a0e3193cc 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/rate.yaml @@ -9,4 +9,4 @@ metadata: unit: /1 reference: - title: Georgia SB 520 Section 4 - 48-7-27(a)(1)(C) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=5 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml index 7127f47e3a8..41dff3a4c68 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/deductions/standard/phase_out/threshold.yaml @@ -8,7 +8,7 @@ metadata: - filing_status reference: - title: Georgia SB 520 Section 4 - 48-7-27(a)(1)(C) - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=5 JOINT: 2026-01-01: 280_000 diff --git a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml index 176f7809365..c72a791bd93 100644 --- a/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml +++ b/policyengine_us/parameters/gov/contrib/states/ga/sb520/in_effect.yaml @@ -9,4 +9,4 @@ metadata: label: Georgia SB 520 2026 provisions in effect reference: - title: Georgia SB 520 Sections 4, 6, 7 - href: https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=1 + href: https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=1 diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index ab279a9e89e..c6fadd1caec 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -10,7 +10,7 @@ class ga_standard_deduction(Variable): unit = USD definition_period = YEAR reference = ( - "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=5", + "https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=5", ) defined_for = StateCode.GA @@ -51,7 +51,7 @@ class ga_ctc(Variable): definition_period = YEAR defined_for = StateCode.GA reference = ( - "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=7", + "https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=6", ) def formula(tax_unit, period, parameters): @@ -85,7 +85,7 @@ class ga_eitc(Variable): definition_period = YEAR defined_for = StateCode.GA reference = ( - "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=8", + "https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=6", ) def formula(tax_unit, period, parameters): @@ -154,7 +154,7 @@ class ga_income_tax_before_non_refundable_credits(Variable): definition_period = YEAR defined_for = StateCode.GA reference = ( - "https://www.legis.ga.gov/api/legislation/document/20252026/232863#page=2", + "https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=3", ) def formula(tax_unit, period, parameters): From 666c139eec72e8a63745e43014fd7746188c20fa Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Wed, 4 Mar 2026 11:06:05 -0500 Subject: [PATCH 5/7] Add ga_deductions override to enforce SB 520 itemization repeal SB 520 Section 4 amends 48-7-27(a)(1) to eliminate the option to itemize nonbusiness deductions. When SB 520 is active, all filers must use the standard deduction regardless of federal itemization status. Without this fix, filers who itemize federally would incorrectly itemize for Georgia even with SB 520 active, resulting in too-low taxable income. Co-Authored-By: Claude Opus 4.5 --- .../states/ga/sb520/ga_sb520_reform.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index c6fadd1caec..d61e817c139 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -43,6 +43,35 @@ def formula(tax_unit, period, parameters): return where(sb520_active, sb520_deduction, baseline_deduction) + class ga_deductions(Variable): + value_type = float + entity = TaxUnit + label = "Georgia deductions" + unit = USD + definition_period = YEAR + reference = ( + # SB 520 Section 4 eliminates itemized deduction option + "https://www.legis.ga.gov/api/legislation/document/20252026/242809#page=5", + ) + defined_for = StateCode.GA + + def formula(tax_unit, period, parameters): + p_sb520 = parameters(period).gov.contrib.states.ga.sb520 + + sb520_active = p_sb520.in_effect + + # SB 520 Section 4 eliminates the option to itemize - standard deduction only + sb520_deduction = tax_unit("ga_standard_deduction", period) + + # Baseline: itemize if federal itemizes + itemizes = tax_unit("tax_unit_itemizes", period) + sd = tax_unit("ga_standard_deduction", period) + p = parameters(period).gov.irs.deductions + itemized = add(tax_unit, period, p.itemized_deductions) + baseline_deduction = where(itemizes, itemized, sd) + + return where(sb520_active, sb520_deduction, baseline_deduction) + class ga_ctc(Variable): value_type = float entity = TaxUnit @@ -206,6 +235,7 @@ def formula(tax_unit, period, parameters): class reform(Reform): def apply(self): self.update_variable(ga_standard_deduction) + self.update_variable(ga_deductions) self.update_variable(ga_ctc) self.update_variable(ga_eitc) self.update_variable(ga_refundable_credits) From 651a404b72a4159c6e71ce7bf3ab2537ab07cb28 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 6 Mar 2026 16:30:17 -0500 Subject: [PATCH 6/7] Resolve merge conflict in reforms.py Include both ga_sb520 and watca reforms after merging main. Co-Authored-By: Claude Opus 4.5 --- policyengine_us/reforms/reforms.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/policyengine_us/reforms/reforms.py b/policyengine_us/reforms/reforms.py index 190d80a0aec..3070e9395e9 100644 --- a/policyengine_us/reforms/reforms.py +++ b/policyengine_us/reforms/reforms.py @@ -344,11 +344,8 @@ def create_structural_reforms_from_parameters(parameters, period): ct_sb100 = create_ct_sb100_reform(parameters, period) ct_tax_rebate_2026 = create_ct_tax_rebate_2026_reform(parameters, period) ct_hb5009 = create_ct_hb5009_reform(parameters, period) -<<<<<<< ga-sb520-tax-reform ga_sb520 = create_ga_sb520_reform(parameters, period) -======= watca = create_watca_reform(parameters, period) ->>>>>>> main reforms = [ afa_reform, @@ -429,11 +426,8 @@ def create_structural_reforms_from_parameters(parameters, period): ct_hb5009, ct_sb100, ct_tax_rebate_2026, -<<<<<<< ga-sb520-tax-reform ga_sb520, -======= watca, ->>>>>>> main ] reforms = tuple(filter(lambda x: x is not None, reforms)) From a32f5e1eea64261c8b124f2f6d80dc9876844518 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 6 Mar 2026 16:33:02 -0500 Subject: [PATCH 7/7] Format ga_sb520_reform.py Co-Authored-By: Claude Opus 4.5 --- .../reforms/states/ga/sb520/ga_sb520_reform.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py index d61e817c139..c6b4cfb0fc5 100644 --- a/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py +++ b/policyengine_us/reforms/states/ga/sb520/ga_sb520_reform.py @@ -94,16 +94,12 @@ def formula(tax_unit, period, parameters): ctc_eligible_child = person("ctc_qualifying_child", period) # SB 520 preserves baseline GA CTC age threshold (under 6) ga_child_age_eligible = age < p.age_threshold - eligible_children = tax_unit.sum( - ctc_eligible_child & ga_child_age_eligible - ) + eligible_children = tax_unit.sum(ctc_eligible_child & ga_child_age_eligible) sb520_amount = p_sb520.credits.ctc.amount baseline_amount = p.amount - amount_per_child = where( - sb520_active, sb520_amount, baseline_amount - ) + amount_per_child = where(sb520_active, sb520_amount, baseline_amount) return eligible_children * amount_per_child class ga_eitc(Variable): @@ -158,9 +154,7 @@ class ga_non_refundable_credits(Variable): defined_for = StateCode.GA def formula(tax_unit, period, parameters): - p = parameters( - period - ).gov.states.ga.tax.income.credits.non_refundable + p = parameters(period).gov.states.ga.tax.income.credits.non_refundable p_sb520 = parameters(period).gov.contrib.states.ga.sb520 sb520_active = p_sb520.in_effect