diff --git a/changelog.d/vt-ctc-per-child-phase-out.fixed.md b/changelog.d/vt-ctc-per-child-phase-out.fixed.md new file mode 100644 index 00000000000..d6a7db9a0eb --- /dev/null +++ b/changelog.d/vt-ctc-per-child-phase-out.fixed.md @@ -0,0 +1 @@ +Reduce each child's Vermont child tax credit by $20 for each $1,000 of AGI over $125,000, as 32 V.S.A. 5830f(b) requires, instead of taking one reduction off the total, so the credit ends above $174,000 of AGI for any number of children. diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/age_limit.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/age_limit.yaml index 2758429714a..74e5135569a 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/age_limit.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/age_limit.yaml @@ -10,13 +10,11 @@ metadata: href: https://legislature.vermont.gov/statutes/section/32/151/05830f - title: Vt. 2022 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=17 - - title: Vt. 2021 Tax Form # Child Tax Credit was introduced in 2022 - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=24 - title: Vt. 2023 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=33 - title: Vt. 2024 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2024.pdf#page=33 - title: Vt. 2025 Income Tax Return Booklet - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=35 - - title: S.51 (2025) - Vermont tax credit package extends CTC to 6-year-olds - href: https://legislature.vermont.gov/Documents/2026/Docs/BILLS/S-0051/S-0051%20As%20Passed%20by%20Both%20House%20and%20Senate%20Official.pdf#page=6 + href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=34 + - title: "2025 Vt. Act 71, Secs. 1 and 6 #page 1,10" + href: https://legislature.vermont.gov/Documents/2026/Docs/ACTS/ACT071/ACT071%20As%20Enacted.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/amount.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/amount.yaml index 3ff74e37846..f5216eefd72 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/amount.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/amount.yaml @@ -9,11 +9,9 @@ metadata: href: https://legislature.vermont.gov/statutes/section/32/151/05830f - title: Vt. 2022 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=17 - - title: Vt. 2021 Tax Form # Child Tax Credit was introduced in 2022 - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=24 - title: Vt. 2023 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=33 - title: Vt. 2024 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2024.pdf#page=33 - title: Vt. 2025 Income Tax Return Booklet - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=35 + href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/amount.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/amount.yaml index 4fd9b377290..e77d6665230 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/amount.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/amount.yaml @@ -1,19 +1,17 @@ -description: Vermont reduces the Child Tax Credit by this amount for each increment of adjusted gross income exceeding the threshold. +description: Vermont reduces the Child Tax Credit for each qualifying child by this amount for each increment of adjusted gross income exceeding the threshold. values: 2022-01-01: 20 metadata: unit: currency-USD - label: Vermont CTC reduction amount + label: Vermont CTC reduction amount per child reference: - title: Vt. Stat. tit. 32 ยง 5830f Section (b) href: https://legislature.vermont.gov/statutes/section/32/151/05830f - title: Vt. 2022 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=17 - - title: Vt. 2021 Tax Form # Child Tax Credit was introduced in 2022 - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=24 - title: Vt. 2023 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=33 - title: Vt. 2024 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2024.pdf#page=33 - title: Vt. 2025 Income Tax Return Booklet - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=35 + href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/increment.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/increment.yaml index 05f95ab33a4..1103864b918 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/increment.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/increment.yaml @@ -9,11 +9,9 @@ metadata: href: https://legislature.vermont.gov/statutes/section/32/151/05830f - title: Vt. 2022 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=17 - - title: Vt. 2021 Tax Form # Child Tax Credit was introduced in 2022 - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=24 - title: Vt. 2023 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=33 - title: Vt. 2024 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2024.pdf#page=33 - title: Vt. 2025 Income Tax Return Booklet - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=35 + href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=34 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/start.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/start.yaml index 65687341a4c..3e6ac011d6f 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/start.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/ctc/reduction/start.yaml @@ -9,11 +9,9 @@ metadata: href: https://legislature.vermont.gov/statutes/section/32/151/05830f - title: Vt. 2022 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=17 - - title: Vt. 2021 Tax Form # Child Tax Credit was introduced in 2022 - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=24 - title: Vt. 2023 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=33 - title: Vt. 2024 Income Tax Return Booklet href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2024.pdf#page=33 - title: Vt. 2025 Income Tax Return Booklet - href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=35 + href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2025.pdf#page=34 diff --git a/policyengine_us/tests/core/test_vt_ctc_phase_out_properties.py b/policyengine_us/tests/core/test_vt_ctc_phase_out_properties.py new file mode 100644 index 00000000000..98e7ed76f6a --- /dev/null +++ b/policyengine_us/tests/core/test_vt_ctc_phase_out_properties.py @@ -0,0 +1,89 @@ +"""Properties of Vermont's child tax credit phase-out. + +32 V.S.A. 5830f(a) gives $1,000 per qualifying child, and 5830f(b) reduces +"the amount of the credit per child", but not below zero, by $20 for each +$1,000, or fraction thereof, of adjusted gross income over $125,000. + +Hypothesis draws a year, a number of children and a list of AGIs, with the +phase-out's edges mixed in. Each AGI appears twice in one vectorized +simulation, once with one child and once with the drawn number. For every +AGI: + +1. The credit is never below zero and never above $1,000 per child, because + (b) reduces it "but not below zero" and (a) pays $1,000 per child. +2. The credit never rises with AGI for the same children, because the + reduction in (b) grows with each $1,000 of AGI over $125,000. +3. n children get n times one child's credit, because (b) reduces each + child's credit. A single reduction off the total fails this at any AGI + over $125,000. + +The YAML cases for vt_ctc pin the amounts at the edges by hand. +""" + +import numpy as np +from hypothesis import HealthCheck, example, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation + +AMOUNT = 1_000 # 5830f(a): "$1,000.00 per qualifying child" +TOLERANCE = 0.01 +# The start of the phase-out, the first fraction of $1,000 above it, the last +# $20 of credit per child (AGI $173,001 to $174,000) and the first AGI with +# none, per the 2025 Schedule IN-112 Child Tax Credit Table. +EDGES = [0, 125_000, 125_001, 126_000, 126_001, 150_000, 174_000, 174_001, 175_000] +# 2022 is the credit's first year; the age limit rose from 5 to 6 in 2025. +YEARS = [2022, 2024, 2025, 2026] + +agi_values = st.one_of(st.sampled_from(EDGES), st.integers(0, 300_000)) + + +def build_situation(agis, n_children, year): + """One tax unit with one child and one with n children for each AGI.""" + people, tax_units, households = {}, {}, {} + for i, agi in enumerate(agis): + for kids in (1, n_children): + name = f"unit_{i}_{kids}" + members = [f"{name}_parent"] + people[members[0]] = {"age": {year: 35}} + for k in range(kids): + members.append(f"{name}_child_{k}") + # Ages 0 to 5 are within the age limit in every year. + people[members[-1]] = {"age": {year: k % 6}} + tax_units[name] = { + "members": members, + "adjusted_gross_income": {year: float(agi)}, + } + households[name] = {"members": members, "state_code": {year: "VT"}} + return {"people": people, "tax_units": tax_units, "households": households} + + +@settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow], +) +@given( + year=st.sampled_from(YEARS), + agis=st.lists(agi_values, min_size=5, max_size=20), + n_children=st.integers(2, 4), +) +@example(year=2025, agis=EDGES, n_children=2) +@example(year=2022, agis=EDGES, n_children=4) +# No vt_ctc.yaml case covers 2026, so this example checks the per-child +# reduction in a year that file leaves out. +@example(year=2026, agis=EDGES, n_children=3) +def test_vt_ctc_phase_out_properties(year, agis, n_children): + agis = sorted(agis) + sim = Simulation(situation=build_situation(agis, n_children, year)) + ctc = np.asarray(sim.calculate("vt_ctc", year), dtype=float).reshape(-1, 2) + one, many = ctc[:, 0], ctc[:, 1] + for credit, kids in ((one, 1), (many, n_children)): + # 1. Never below zero, never above $1,000 per child. + assert np.all(credit >= 0) + assert np.all(credit <= AMOUNT * kids + TOLERANCE) + # 2. Never rises with AGI (the AGIs are sorted). + assert np.all(np.diff(credit) <= TOLERANCE) + # 3. The reduction applies to each child's credit. + np.testing.assert_allclose(many, n_children * one, atol=TOLERANCE) diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml index 757e825b1e3..661d94eee9a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/vt_ctc.yaml @@ -1,3 +1,285 @@ +# 32 V.S.A. 5830f(b) reduces "the amount of the credit per child", but not +# below zero, by $20 for each $1,000, or fraction thereof, of AGI over +# $125,000. Each child's $1,000 therefore reaches $0 once AGI exceeds +# $174,000. The 2025 Schedule IN-112 instructions' Child Tax Credit Table +# (Income Booklet 2025, file page 34) gives the same amount per qualifying +# child: $980 from $125,001 and $0 from $174,001. + +- name: Two children at $130,000 of wages, the first case in issue 9996, get $900 each. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 130_000 + spouse: + age: 38 + child1: + age: 3 + child2: + age: 2 + tax_units: + tax_unit: + members: [head, spouse, child1, child2] + households: + household: + members: [head, spouse, child1, child2] + state_code: VT + output: + adjusted_gross_income: 130_000 + # $5,000 over $125,000 is 5 increments: $1,000 - 5 x $20 = $900 per child + # (table: 129,001 to 130,000), and 2 x $900 = $1,800. One reduction off + # the $2,000 total would leave $1,900. + vt_ctc: 1_800 + +- name: Three children at $130,000 of wages, the second case in issue 9996, get $900 each. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 130_000 + spouse: + age: 38 + child1: + age: 5 + child2: + age: 3 + child3: + age: 1 + tax_units: + tax_unit: + members: [head, spouse, child1, child2, child3] + households: + household: + members: [head, spouse, child1, child2, child3] + state_code: VT + output: + # 3 x ($1,000 - 5 x $20) = $2,700; one reduction off the total would + # leave $2,900. + vt_ctc: 2_700 + +- name: Two children at $180,000 of wages, the third case in issue 9996, get nothing. + period: 2025 + input: + people: + head: + age: 40 + employment_income: 180_000 + spouse: + age: 38 + child1: + age: 3 + child2: + age: 2 + tax_units: + tax_unit: + members: [head, spouse, child1, child2] + households: + household: + members: [head, spouse, child1, child2] + state_code: VT + output: + adjusted_gross_income: 180_000 + # $55,000 over $125,000 is 55 increments: $1,000 - 55 x $20 is below + # zero, so each child's credit is $0 (table: 174,001 and over). One + # reduction off the $2,000 total would leave $900. + vt_ctc: 0 + +- name: Two children at exactly $125,000 of AGI keep $1,000 each. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 125_000 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # AGI does not exceed $125,000, so nothing is reduced: 2 x $1,000 + # (table: 0 to 125,000). + vt_ctc: 2_000 + +- name: Two children at $125,001 of AGI get $980 each. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 125_001 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # $1 over $125,000 is a fraction of $1,000, so each child loses $20: + # 2 x $980 = $1,960 (table: 125,001 to 126,000). One reduction off the + # total would leave $1,980. + vt_ctc: 1_960 + +- name: Two children at $174,000 of AGI get $20 each. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 174_000 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # $49,000 over $125,000 is 49 increments: $1,000 - 49 x $20 = $20 per + # child (table: 173,001 to 174,000), and 2 x $20 = $40. One reduction off + # the total would leave $1,020. + vt_ctc: 40 + +- name: Two children at $174,001 of AGI get nothing. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 174_001 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # $49,001 over $125,000 is 50 increments, counting the fraction: + # $1,000 - 50 x $20 = $0 per child (table: 174,001 and over). One + # reduction off the total would leave $1,000. + vt_ctc: 0 + +- name: Three children at $174,000 of AGI get $20 each. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 5 + child2: + age: 3 + child3: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2, child3] + adjusted_gross_income: 174_000 + households: + household: + members: [parent, child1, child2, child3] + state_code: VT + output: + # 49 increments leave $20 per child, and 3 x $20 = $60. One reduction off + # the $3,000 total would leave $2,020. + vt_ctc: 60 + +- name: Three children at $174,001 of AGI get nothing. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 5 + child2: + age: 3 + child3: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2, child3] + adjusted_gross_income: 174_001 + households: + household: + members: [parent, child1, child2, child3] + state_code: VT + output: + # 50 increments leave $0 per child. One reduction off the $3,000 total + # would leave $2,000. + vt_ctc: 0 + +- name: In 2022, the credit's first year, two children at $150,000 of AGI get $500 each. + period: 2022 + input: + people: + parent: + age: 35 + child1: + age: 4 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 150_000 + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # 2022 Act 138, Sec. 1 enacted 5830f(b) with the per-child reduction, + # applying from January 1, 2022 (Sec. 13(b)). $25,000 over $125,000 is + # 25 increments: 2 x ($1,000 - 25 x $20) = $1,000. One reduction off the + # total would leave $1,500. + vt_ctc: 1_000 + +- name: A married filer filing separately with two children at $130,000 of AGI gets $900 each, the same as any other status. + period: 2025 + input: + people: + parent: + age: 35 + child1: + age: 3 + child2: + age: 1 + tax_units: + tax_unit: + members: [parent, child1, child2] + adjusted_gross_income: 130_000 + filing_status: SEPARATE + households: + household: + members: [parent, child1, child2] + state_code: VT + output: + # 5830f(b) applies the $125,000 start "irrespective of the individual's + # filing status": 2 x ($1,000 - 5 x $20) = $1,800. + vt_ctc: 1_800 + - name: Five qualifying children. period: 2023 input: diff --git a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py index 6d58a34dfe1..d5941eac418 100644 --- a/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py +++ b/policyengine_us/variables/gov/states/vt/tax/income/credits/ctc/vt_ctc.py @@ -17,13 +17,13 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.vt.tax.income.credits.ctc eligible = age <= p.age_limit count_eligible = tax_unit.sum(eligible) - # Get maximum credit amount. - max_credit = p.amount * count_eligible # Get adjusted gross income. agi = tax_unit("adjusted_gross_income", period) - # Reduce credit amount over the phaseout range. + # 32 V.S.A. 5830f(b) reduces "the amount of the credit per child", + # but not below zero, by $20 for each $1,000, or fraction thereof, of + # AGI over $125,000. Each child's $1,000 is reduced, not the total. excess_agi = max_(agi - p.reduction.start, 0) increments = np.ceil(excess_agi / p.reduction.increment) - total_reduction = p.reduction.amount * increments - # Return reduced credit amount. - return max_(max_credit - total_reduction, 0) + reduction_per_child = p.reduction.amount * increments + credit_per_child = max_(p.amount - reduction_per_child, 0) + return count_eligible * credit_per_child