Skip to content
Merged
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/fix-vt-renter-credit-income-limits.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Correct eight Vermont renter credit income limits for 2021, 2023, 2024 and 2025 to HUD's limits and the department's tables, including Washington County's 2025 seven-person partial-credit limit ($88,990; the TY2025 booklet prints $88,880).
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
description: Vermont limits the partial renter credit to filers with income below this amount, based on the county and household size.
metadata:
propagate_metadata_to_children: true
unit: /1
unit: currency-USD
period: year
breakdown:
- list(range(1,9))
Expand All @@ -10,8 +10,30 @@ metadata:
label: Vermont partial renter credit income limit
reference:
# The table is derived from the official Vermont government website. The legal code refers to area median income.
# The published tables equal HUD's very low-income (l50) limit for 2021-2023 and
# 1.3 times it for 2024-2025, rounded to the nearest $10. For Franklin and Grand
# Isle they use the average across Vermont's twelve HUD income-limit areas, rounded
# to the nearest $10 before the multiplier (32 V.S.A. § 6061(20)).
- title: 32 V.S.A. §6066. Homestead Property Tax Credit and Renter Credit (b)(B)
href: https://law.justia.com/codes/vermont/2022/title-32/chapter-154/section-6066/
- title: 32 V.S.A. § 6061(20), very low-income limit
href: https://legislature.vermont.gov/statutes/section/32/154/06061
- title: 2025 Renter Credit Website Calculator, 2025 Income Limits sheet
href: https://tax.vermont.gov/sites/tax/files/documents/2025%20Renter%20Credit%20Website%20Calculator.xlsx
- title: 2024 Renter Credit Website Calculator, 2024 Income Limits sheet
href: https://tax.vermont.gov/sites/tax/files/documents/2024%20Renter%20Credit%20Website%20Calculator.xlsx
- title: 2023 Renter Credit Website Calculator, 2023 Income Limits sheet
href: https://web.archive.org/web/20250526044955/https://tax.vermont.gov/document/2023-renter-credit-website-calculator
- title: HUD FY2025 Section 8 income limits (l50 columns)
href: https://www.huduser.gov/portal/datasets/il/il25/Section8-FY25.xlsx
- title: HUD FY2024 Section 8 income limits (l50 columns)
href: https://www.huduser.gov/portal/datasets/il/il24/Section8-FY24.xlsx
- title: HUD FY2023 Section 8 income limits (l50 columns)
href: https://www.huduser.gov/portal/datasets/il/il23/Section8-FY23.xlsx
- title: HUD FY2022 Section 8 income limits (l50 columns)
href: https://www.huduser.gov/portal/datasets/il/il22/Section8-FY22.xlsx
- title: HUD FY2021 Section 8 income limits (l50 columns)
href: https://www.huduser.gov/portal/datasets/il/il21/Section8-FY21.xlsx
- title: 2021 Vermont instruction booklet
href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=34
- title: 2022 Vermont instruction booklet
Expand Down Expand Up @@ -187,7 +209,7 @@ metadata:
2024-01-01: 49_400
2025-01-01: 53_950
WINDSOR_COUNTY_VT:
2021-01-01: 31_350
2021-01-01: 31_550
2022-01-01: 35_250
2023-01-01: 37_350
2024-01-01: 52_260
Expand Down Expand Up @@ -263,7 +285,7 @@ metadata:
2021-01-01: 37_000
2022-01-01: 41_150
2023-01-01: 43_600
2024-01-01: 61_960
2024-01-01: 61_690
2025-01-01: 64_610
WINDHAM_COUNTY_VT:
2021-01-01: 35_250
Expand Down Expand Up @@ -566,7 +588,7 @@ metadata:
FRANKLIN_COUNTY_VT:
2021-01-01: 49_980
2022-01-01: 55_100
2023-01-01: 56_250
2023-01-01: 58_320
2024-01-01: 80_510
2025-01-01: 87_390
GRAND_ISLE_COUNTY_VT:
Expand All @@ -578,7 +600,7 @@ metadata:
LAMOILLE_COUNTY_VT:
2021-01-01: 48_550
2022-01-01: 53_150
2023-01-01: 58_320
2023-01-01: 56_250
2024-01-01: 76_570
2025-01-01: 83_590
ORANGE_COUNTY_VT:
Expand All @@ -604,7 +626,9 @@ metadata:
2022-01-01: 56_700
2023-01-01: 60_050
2024-01-01: 84_960
2025-01-01: 88_880
# The TY2025 booklet prints $88,880. The 2025 calculator has $88,990, which is
# 1.3 times HUD's FY2025 limit of $68,450 ($88,985), rounded to the nearest $10.
2025-01-01: 88_990
WINDHAM_COUNTY_VT:
2021-01-01: 48_550
2022-01-01: 53_150
Expand Down Expand Up @@ -640,7 +664,7 @@ metadata:
2021-01-01: 63_300
2022-01-01: 70_850
2023-01-01: 75_000
2024-01-01: 102_850
2024-01-01: 102_050
2025-01-01: 111_410
ESSEX_COUNTY_VT:
2021-01-01: 51_700
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,33 @@ metadata:
label: Vermont full renter credit income limit
reference:
# The table is derived from the official Vermont government website. The legal code refers to area median income.
# The published tables equal HUD's extremely low-income (ELI) limit. For Franklin
# and Grand Isle they use the average across Vermont's twelve HUD income-limit
# areas, rounded to the nearest $10 (32 V.S.A. § 6061(19)).
- title: 32 V.S.A. §6066. Homestead Property Tax Credit and Renter Credit (b)(B)
href: https://law.justia.com/codes/vermont/2022/title-32/chapter-154/section-6066/
- title: 32 V.S.A. § 6061(19), extremely low-income limit
href: https://legislature.vermont.gov/statutes/section/32/154/06061
- title: 2025 Renter Credit Website Calculator, 2025 Income Limits sheet
href: https://tax.vermont.gov/sites/tax/files/documents/2025%20Renter%20Credit%20Website%20Calculator.xlsx
- title: 2024 Renter Credit Website Calculator, 2024 Income Limits sheet
href: https://tax.vermont.gov/sites/tax/files/documents/2024%20Renter%20Credit%20Website%20Calculator.xlsx
- title: 2023 Renter Credit Website Calculator, 2023 Income Limits sheet
href: https://web.archive.org/web/20250526044955/https://tax.vermont.gov/document/2023-renter-credit-website-calculator
- title: HUD FY2025 Section 8 income limits (ELI columns)
href: https://www.huduser.gov/portal/datasets/il/il25/Section8-FY25.xlsx
- title: HUD FY2024 Section 8 income limits (ELI columns)
href: https://www.huduser.gov/portal/datasets/il/il24/Section8-FY24.xlsx
- title: HUD FY2023 Section 8 income limits (ELI columns)
href: https://www.huduser.gov/portal/datasets/il/il23/Section8-FY23.xlsx
- title: HUD FY2022 Section 8 income limits (ELI columns)
href: https://www.huduser.gov/portal/datasets/il/il22/Section8-FY22.xlsx
- title: HUD FY2021 Section 8 income limits (ELI columns)
href: https://www.huduser.gov/portal/datasets/il/il21/Section8-FY21.xlsx
- title: 2021 Vermont instruction booklet
href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2021.pdf#page=34
- title: 2022 Vermont instruction booklet
href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=35
href: https://tax.vermont.gov/sites/tax/files/documents/Income%20Booklet-2022.pdf#page=36
- title: Vt. 2023 Income Tax Return Booklet
href: https://tax.vermont.gov/sites/tax/files/documents/Income-Booklet-2023.pdf#page=36
- title: Vt. 2024 Income Tax Return Booklet
Expand Down Expand Up @@ -294,7 +315,7 @@ metadata:
2021-01-01: 26_500
2022-01-01: 27_750
2023-01-01: 30_000
2024-01-01: 30_000
2024-01-01: 31_200
2025-01-01: 32_150
CHITTENDEN_COUNTY_VT:
2021-01-01: 28_750
Expand Down Expand Up @@ -382,7 +403,7 @@ metadata:
2024-01-01: 36_580
2025-01-01: 37_650
CHITTENDEN_COUNTY_VT:
2021-01-01: 31_040
2021-01-01: 31_050
2022-01-01: 34_800
2023-01-01: 36_850
2024-01-01: 38_550
Expand Down Expand Up @@ -600,6 +621,8 @@ metadata:
2024-01-01: 47_340
2025-01-01: 48_650
WASHINGTON_COUNTY_VT:
# The TY2021 booklet prints $41,120. HUD's FY2021 limit, and every other
# county's 7-person value in the booklet, is $40,120.
2021-01-01: 40_120
2022-01-01: 41_910
2023-01-01: 45_420
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
"""Check Vermont's renter credit income limit tables against HUD's limits.

32 V.S.A. § 6061(19)-(20) define the full and partial credit limits from HUD's
Section 8 income limits: the extremely low-income limit, and 1.3 times the very
low-income limit. Franklin and Grand Isle use the state average instead. PE
transcribes the department's published tables, and PE also bundles HUD's limits
(gov/hud/income_limits) for FY2024 onward, so the two must agree for every year
both cover. The department rounds to the nearest $10, half up; for Franklin and
Grand Isle it averages across Vermont's twelve HUD areas and rounds that average
to $10 before applying the multiplier. Transcription slips in the 2024 and 2025
tables (including the TY2025 booklet's $88,880 for Washington County, seven
people, where 1.3 x $68,450 gives $88,990) broke this check.
"""

from decimal import ROUND_HALF_UP, Decimal

import pytest

from policyengine_us.parameters.gov.hud.income_limits import income_limits
from policyengine_us.system import system

TABLES = system.parameters.gov.states.vt.tax.income.credits.renter.income_limit_ami
SIZES = range(1, 9)
VERY_LOW_INCOME_MULTIPLIER = Decimal("1.3")
COUNTY_FIPS = {
"ADDISON_COUNTY_VT": "50001",
"BENNINGTON_COUNTY_VT": "50003",
"CALEDONIA_COUNTY_VT": "50005",
"CHITTENDEN_COUNTY_VT": "50007",
"ESSEX_COUNTY_VT": "50009",
"FRANKLIN_COUNTY_VT": "50011",
"GRAND_ISLE_COUNTY_VT": "50013",
"LAMOILLE_COUNTY_VT": "50015",
"ORANGE_COUNTY_VT": "50017",
"ORLEANS_COUNTY_VT": "50019",
"RUTLAND_COUNTY_VT": "50021",
"WASHINGTON_COUNTY_VT": "50023",
"WINDHAM_COUNTY_VT": "50025",
"WINDSOR_COUNTY_VT": "50027",
}
STATE_AVERAGE_COUNTIES = ("FRANKLIN_COUNTY_VT", "GRAND_ISLE_COUNTY_VT")


def _table(name):
"""Map (year, county, size) to each value dated January 1 in a table."""
values = {}
for size in SIZES:
for county, parameter in (
TABLES.children[name].children[str(size)].children.items()
):
for entry in parameter.values_list:
year, month_day = int(entry.instant_str[:4]), entry.instant_str[5:]
assert month_day == "01-01", entry.instant_str
values[(year, county, size)] = entry.value
return values


PARTIAL = _table("fifty_percent")
FULL = _table("thirty_percent")
TABLE_YEARS = sorted({year for year, _, _ in PARTIAL})
HUD_YEARS = sorted(set(TABLE_YEARS) & set(income_limits["year"].unique().tolist()))


def _round_to_ten(amount):
return int((Decimal(amount) / 10).quantize(Decimal(1), rounding=ROUND_HALF_UP) * 10)


def _hud(year):
rows = income_limits[
(income_limits["year"] == year)
& income_limits["county_fips"].isin(COUNTY_FIPS.values())
].set_index("county_fips")
assert len(rows) == len(COUNTY_FIPS)
return rows


def _expected(year, county, size, column, multiplier):
rows = _hud(year)
if county in STATE_AVERAGE_COUNTIES:
# Franklin and Grand Isle share the Burlington-South Burlington HUD
# area with Chittenden, so the other twelve counties are the twelve
# distinct HUD areas.
for other in STATE_AVERAGE_COUNTIES:
assert rows.loc[COUNTY_FIPS[other]].equals(
rows.loc[COUNTY_FIPS["CHITTENDEN_COUNTY_VT"]]
)
areas = [COUNTY_FIPS[c] for c in COUNTY_FIPS if c not in STATE_AVERAGE_COUNTIES]
average = Decimal(int(rows.loc[areas, f"{column}_{size}"].sum())) / len(areas)
return _round_to_ten(_round_to_ten(average) * multiplier)
return _round_to_ten(
Decimal(int(rows.loc[COUNTY_FIPS[county], f"{column}_{size}"])) * multiplier
)


def test_hud_comparison_covers_2024_onward():
# The published tables start in 2021; pe-us copies each parameter's first
# value back to 2015 (tools/parameters.py backdate_parameters).
assert set(range(2021, 2026)) <= set(TABLE_YEARS)
assert HUD_YEARS[:2] == [2024, 2025]


@pytest.mark.parametrize("year", HUD_YEARS)
@pytest.mark.parametrize("county", sorted(COUNTY_FIPS))
@pytest.mark.parametrize("size", SIZES)
def test_partial_credit_limit_is_1_3_times_hud_very_low_income_limit(
size, county, year
):
assert PARTIAL[(year, county, size)] == _expected(
year, county, size, "very_low_income", VERY_LOW_INCOME_MULTIPLIER
)


@pytest.mark.parametrize("year", HUD_YEARS)
@pytest.mark.parametrize("county", sorted(COUNTY_FIPS))
@pytest.mark.parametrize("size", SIZES)
def test_full_credit_limit_is_hud_extremely_low_income_limit(size, county, year):
assert FULL[(year, county, size)] == _expected(
year, county, size, "extremely_low_income", Decimal(1)
)


def test_tables_cover_every_county_size_and_year():
keys = {
(year, county, size)
for year in TABLE_YEARS
for county in COUNTY_FIPS
for size in SIZES
}
assert set(PARTIAL) == keys
assert set(FULL) == keys


@pytest.mark.parametrize("year", TABLE_YEARS)
@pytest.mark.parametrize("county", sorted(COUNTY_FIPS))
def test_limits_rise_with_household_size_and_full_stays_below_partial(county, year):
partial = [PARTIAL[(year, county, size)] for size in SIZES]
full = [FULL[(year, county, size)] for size in SIZES]
assert partial == sorted(partial)
assert full == sorted(full)
assert all(f < p for f, p in zip(full, partial))
assert all(value % 10 == 0 for value in partial + full)


@pytest.mark.parametrize("year", TABLE_YEARS)
def test_franklin_and_grand_isle_share_the_state_average(year):
for table in (PARTIAL, FULL):
assert [table[(year, "FRANKLIN_COUNTY_VT", size)] for size in SIZES] == [
table[(year, "GRAND_ISLE_COUNTY_VT", size)] for size in SIZES
]
Loading
Loading