diff --git a/changelog.d/fix-vt-renter-credit-income-limits.fixed.md b/changelog.d/fix-vt-renter-credit-income-limits.fixed.md new file mode 100644 index 00000000000..6f30a9628c9 --- /dev/null +++ b/changelog.d/fix-vt-renter-credit-income-limits.fixed.md @@ -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). diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/fifty_percent.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/fifty_percent.yaml index efe891885c1..a882bcb9751 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/fifty_percent.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/fifty_percent.yaml @@ -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)) @@ -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 @@ -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 @@ -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 @@ -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: @@ -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: @@ -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 @@ -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 diff --git a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/thirty_percent.yaml b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/thirty_percent.yaml index 398b4514a3b..a982d09e3e5 100644 --- a/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/thirty_percent.yaml +++ b/policyengine_us/parameters/gov/states/vt/tax/income/credits/renter/income_limit_ami/thirty_percent.yaml @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/test_vt_renter_credit_income_limits.py b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/test_vt_renter_credit_income_limits.py new file mode 100644 index 00000000000..ec22d9fec68 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/test_vt_renter_credit_income_limits.py @@ -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 + ] diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml index 1e8a1245a2a..6a063373d9a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/credits/rent/vt_renter_credit.yaml @@ -352,3 +352,147 @@ output: # 924 x (30,000 - 23,500) / (30,000 - 18,000) = 500.50. vt_renter_credit: 501 + +# Income limit corrections. Each case puts income where the corrected limits +# and PE's earlier values give different credits. Base credit = ROUND(fair market +# rent x 12 x 10%, 0), before the income phase-out; final credits round half up. +# The 2023-2025 expectations match the department's Renter Credit Website +# Calculator for the same household. +- name: Washington, 7 people, 2025, midpoint of the corrected income limits + period: 2025 + input: + vt_renter_credit_income: 68_820 + county: WASHINGTON_COUNTY_VT + tax_unit_size: 7 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 88,990 = 1.3 x HUD FY2025 very low-income limit 68,450, + # rounded to $10 (the booklet prints 88,880). Full limit 48,650. + # Base: ROUND(1,977 x 12 x 0.1, 0) = ROUND(2,372.4, 0) = 2,372. + # (88,990 - 68,820) / (88,990 - 48,650) = 0.5; 0.5 x 2,372 = 1,186. + vt_renter_credit: 1_186 + +- name: Washington, 7 people, 2025, income between the booklet and corrected limits + period: 2025 + input: + vt_renter_credit_income: 88_950 + county: WASHINGTON_COUNTY_VT + tax_unit_size: 7 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Above the booklet's 88,880 but below 88,990. + # Base: ROUND(1,977 x 12 x 0.1, 0) = 2,372. + # (88,990 - 88,950) / 40,340 x 2,372 = 2.352... -> 2. + vt_renter_credit: 2 + +- name: Chittenden, 8 people, 2024, income above the corrected partial limit + period: 2024 + input: + vt_renter_credit_income: 102_400 + county: CHITTENDEN_COUNTY_VT + tax_unit_size: 8 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 102,050 = 1.3 x HUD FY2024 limit 78,500 (PE had 102,850). + vt_renter_credit: 0 + +- name: Washington, 3 people, 2024, income above the corrected partial limit + period: 2024 + input: + vt_renter_credit_income: 61_800 + county: WASHINGTON_COUNTY_VT + tax_unit_size: 3 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 61,690 = 1.3 x HUD FY2024 limit 47,450 (PE had 61,960). + vt_renter_credit: 0 + +- name: Caledonia, 4 people, 2024, income below the corrected full limit + period: 2024 + input: + vt_renter_credit_income: 30_600 + county: CALEDONIA_COUNTY_VT + tax_unit_size: 4 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Full limit 31,200 = HUD FY2024 extremely low-income limit (PE had 30,000). + # Full credit: ROUND(1,657 x 12 x 0.1, 0) = ROUND(1,988.4, 0) = 1,988. + vt_renter_credit: 1_988 + +- name: Franklin, 7 people, 2023, midpoint of the corrected income limits + period: 2023 + input: + vt_renter_credit_income: 51_870 + county: FRANKLIN_COUNTY_VT + tax_unit_size: 7 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 58,320 (PE had Lamoille's 56,250). Full limit 45,420. + # Base: ROUND(1,586.94 x 12 x 0.1, 0) = ROUND(1,904.328, 0) = 1,904. + # (58,320 - 51,870) / (58,320 - 45,420) = 0.5; 0.5 x 1,904 = 952. + vt_renter_credit: 952 + +- name: Lamoille, 7 people, 2023, midpoint of the corrected income limits + period: 2023 + input: + vt_renter_credit_income: 50_835 + county: LAMOILLE_COUNTY_VT + tax_unit_size: 7 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 56,250 (PE had Franklin's 58,320). Full limit 45,420. + # Base: ROUND(1,556 x 12 x 0.1, 0) = ROUND(1,867.2, 0) = 1,867. + # (56,250 - 50,835) / (56,250 - 45,420) = 0.5; 0.5 x 1,867 = 933.5 -> 934. + vt_renter_credit: 934 + +- name: Windsor, 2 people, 2021, midpoint of the corrected income limits + period: 2021 + input: + vt_renter_credit_income: 25_250 + county: WINDSOR_COUNTY_VT + tax_unit_size: 2 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Partial limit 31,550 = HUD FY2021 limit (PE had 31,350). Full limit 18,950. + # Base: ROUND(1,007 x 12 x 0.1, 0) = ROUND(1,208.4, 0) = 1,208. + # (31,550 - 25,250) / (31,550 - 18,950) = 0.5; 0.5 x 1,208 = 604. + vt_renter_credit: 604 + +- name: Chittenden, 5 people, 2021, income below the corrected full limit + period: 2021 + input: + vt_renter_credit_income: 31_045 + county: CHITTENDEN_COUNTY_VT + tax_unit_size: 5 + rent_is_shared_with_another_tax_unit: false + housing_assistance: 0 + rent: 0 + state_code: VT + output: + # Full limit 31,050 = HUD FY2021 extremely low-income limit (PE had 31,040). + # Full credit: ROUND(2,083 x 12 x 0.1, 0) = ROUND(2,499.6, 0) = 2,500. + vt_renter_credit: 2_500