From 44f0f311514694b3f19f672d07c475b37568d46a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 20 Oct 2026 16:40:17 -0400 Subject: [PATCH] Decide Vermont Premium Assistance eligibility on the exact FPL ratio 33 V.S.A. 1812(a)(1) covers income "less than or equal to 300 percent of federal poverty level". The gate compared the truncated aca_magi_fraction with 3.0, so every income from 300.00% to 300.99% passed. Test the exact ratio of nonnegative ACA MAGI to the prior-year guideline instead. Integration Scenario 2 expected a $47,000 single filer (300.32%) to stay eligible because the fraction truncates to 3.00; it now tests exactly 300% ($46,950), and Scenario 2b keeps $47,000 as an ineligible case. Two three-person cases supplied a 3.00 fraction with $100,000 of MAGI (375% of $26,650); they now use $79,950. Co-Authored-By: Claude Opus 5.5 --- ...remium-assistance-exact-fpl-ratio.fixed.md | 1 + .../dvha/premium_assistance/integration.yaml | 68 ++++++--- .../vt_premium_assistance.yaml | 11 +- .../vt_premium_assistance_eligible.yaml | 131 ++++++++++++++++-- .../vt_premium_assistance_eligible.py | 13 +- 5 files changed, 179 insertions(+), 45 deletions(-) create mode 100644 changelog.d/vt-premium-assistance-exact-fpl-ratio.fixed.md diff --git a/changelog.d/vt-premium-assistance-exact-fpl-ratio.fixed.md b/changelog.d/vt-premium-assistance-exact-fpl-ratio.fixed.md new file mode 100644 index 00000000000..a3458c1c0ab --- /dev/null +++ b/changelog.d/vt-premium-assistance-exact-fpl-ratio.fixed.md @@ -0,0 +1 @@ +Decide Vermont Premium Assistance eligibility on the exact ratio of ACA MAGI to the prior-year poverty guideline instead of the truncated `aca_magi_fraction`, so incomes from 300.003% to 300.994% of the poverty line are no longer eligible (33 V.S.A. ยง 1812(a)(1): "less than or equal to 300 percent of federal poverty level"). diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/integration.yaml index 3bcbd77d7c9..503d8ad7c24 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/integration.yaml @@ -56,23 +56,21 @@ vt_premium_assistance: 585.00 # ---- Scenario 2: full federal derivation, exactly 300% FPL boundary ---- -- name: Scenario 2, single Vermont enrollee at 47k derives exactly 300% FPL and stays eligible. - # Raw input: employment_income 47_000. Note the exact 300%/size-1 MAGI is 46_950 - # (see vt_premium_assistance.yaml Case 4); 47_000 sits just above it but the - # model still rounds aca_magi_fraction to 3.00, so the inclusive ceiling holds. - # Derived: aca_magi = 47_000; - # aca_magi_fraction = 3.00 exactly (the 300% ceiling is inclusive -> eligible); - # fed_pct = 9.96% (start of the 300-400% flat bracket); slcsp = 15_588. - # Federal APTC = 15_588 - 47_000*0.0996 = 15_588 - 4_681.20 = 10_906.80. - # VPA: fed_pct 9.96% > 1.5% -> gap = 47_000*0.015 = 705.00. - # residual = 15_588 - 10_906.80 = 4_681.20 > gap -> VPA = 705.00. +- name: Scenario 2, single Vermont enrollee at exactly 300% FPL stays eligible. + # Raw input: employment_income 46_950 = 3 x $15,650 (2025 one-person FPG). + # Derived: aca_magi = 46_950; aca_magi_fraction = 3.00 exactly (the 300% + # ceiling is inclusive -> eligible); fed_pct = 9.96% (start of the 300-400% + # flat bracket); slcsp = 15_588. + # Federal APTC = 15_588 - 46_950*0.0996 = 15_588 - 4_676.22 = 10_911.78. + # VPA: fed_pct 9.96% > 1.5% -> gap = 46_950*0.015 = 704.25. + # residual = 15_588 - 10_911.78 = 4_676.22 > gap -> VPA = 704.25. period: 2026 absolute_error_margin: 0.01 input: people: person1: age: 40 - employment_income: 47_000 + employment_income: 46_950 tax_units: tax_unit: members: [person1] @@ -81,14 +79,41 @@ members: [person1] state_code: VT output: - aca_magi: 47_000 + aca_magi: 46_950 aca_magi_fraction: 3.00 is_aca_ptc_eligible: true aca_required_contribution_percentage: 0.0996 slcsp: 15_588 - aca_ptc: 10_906.80 + aca_ptc: 10_911.78 vt_premium_assistance_eligible: true - vt_premium_assistance: 705.00 + vt_premium_assistance: 704.25 + +- name: Scenario 2b, single Vermont enrollee at 47k (300.32% FPL) is above the ceiling. + # Raw input: employment_income 47_000, which is 300.32% of $15,650. Truncation + # makes aca_magi_fraction 3.00, but 33 V.S.A. 1812(a)(1) covers only income + # "less than or equal to 300 percent", so Vermont Premium Assistance is $0. + # The federal APTC is unaffected: 15_588 - 47_000*0.0996 = 10_906.80. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + employment_income: 47_000 + tax_units: + tax_unit: + members: [person1] + households: + household: + members: [person1] + state_code: VT + output: + aca_magi: 47_000 + aca_magi_fraction: 3.00 + is_aca_ptc_eligible: true + aca_ptc: 10_906.80 + vt_premium_assistance_eligible: false + vt_premium_assistance: 0 # ---- Scenario 3: injected, 250% FPL, gap-binding ---- - name: Scenario 3, 250% FPL injected inputs give a gap-binding VPA of income times 1.5%. @@ -251,11 +276,11 @@ # ---- Scenario 8: multi-member household at 300% FPL, gap-binding ---- - name: Scenario 8, three-person Vermont household at 300% FPL gets a gap-binding VPA scaling with MAGI. # A larger family reaches the 300% ceiling at a higher aca_magi, so the gap - # (income * 0.015) scales up. fed_pct at 300% FPL = 9.96% (start of the - # 300-400% flat bracket, derived from the injected aca_magi_fraction 3.00). - # income = 100_000 -> gap = 100_000 * 0.015 = 1_500. + # (income * 0.015) scales up. 300% of the 2025 three-person FPG ($26,650) is + # 79_950, so aca_magi_fraction = 3.00 and fed_pct = 9.96% (start of the + # 300-400% flat bracket). gap = 79_950 * 0.015 = 1_199.25. # residual = slcsp - aca_ptc = 20_000 - 8_000 = 12_000 > gap -> - # VPA = min(1_500, 12_000) = 1_500 (gap binds). Only one member need be + # VPA = min(1_199.25, 12_000) = 1_199.25 (gap binds). Only one member need be # APTC-eligible for the tax unit to qualify at the inclusive 300% ceiling. period: 2026 absolute_error_margin: 0.01 @@ -273,8 +298,7 @@ tax_units: tax_unit: members: [person1, person2, person3] - aca_magi: 100_000 - aca_magi_fraction: 3.00 + aca_magi: 79_950 slcsp: 20_000 aca_ptc: 8_000 households: @@ -282,13 +306,13 @@ members: [person1, person2, person3] state_code: VT output: - aca_magi: 100_000 + aca_magi: 79_950 aca_magi_fraction: 3.00 aca_required_contribution_percentage: 0.0996 slcsp: 20_000 aca_ptc: 8_000 vt_premium_assistance_eligible: true - vt_premium_assistance: 1_500 + vt_premium_assistance: 1_199.25 # ---- Scenario 9: full federal derivation, exactly 180% FPL, gap-binding ---- - name: Scenario 9, single Vermont enrollee at 28.2k derives 180% FPL and gets a gap-binding VPA. diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance.yaml index 52b98db3dc1..796577243d6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance.yaml @@ -319,9 +319,10 @@ - name: Case 13, three-person Vermont household at 300% FPL gets a gap scaling with the larger MAGI. # A larger family reaches 300% FPL at a higher aca_magi, so the gap - # (income * 0.015) scales up accordingly. fed_pct at 300% FPL = 9.96% > 1.5%, - # income = 100_000 -> gap = 100_000 * 0.015 = 1_500. residual = - # 20_000 - 8_000 = 12_000 > gap, so VPA = 1_500 (gap binds). Eligibility holds + # (income * 0.015) scales up accordingly. 300% of the 2025 three-person FPG + # ($26,650) is 79_950; fed_pct at 300% FPL = 9.96% > 1.5%, so + # gap = 79_950 * 0.015 = 1_199.25. residual = 20_000 - 8_000 = 12_000 > gap, + # so VPA = 1_199.25 (gap binds). Eligibility holds # at the inclusive 300% ceiling for a multi-member unit, and only one member # need be APTC-eligible. period: 2026 @@ -340,7 +341,7 @@ tax_units: tax_unit: members: [person1, person2, person3] - aca_magi: 100_000 + aca_magi: 79_950 aca_magi_fraction: 3.00 slcsp: 20_000 aca_ptc: 8_000 @@ -349,7 +350,7 @@ members: [person1, person2, person3] state_code: VT output: - vt_premium_assistance: 1_500 + vt_premium_assistance: 1_199.25 # ---- Negative MAGI clamp (REQ-12) ---- diff --git a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.yaml index 9de538b6ed7..d35356f151e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.yaml @@ -1,10 +1,14 @@ # Unit tests for Vermont Premium Assistance eligibility (REQ-1, REQ-2, REQ-3, # REQ-8). A tax unit is eligible when the program is in effect (2026+), at least # one member is federal-ACA-PTC-eligible (is_aca_ptc_eligible), and ACA MAGI is -# at or below 300% of the federal poverty line (aca_magi_fraction <= 3.0). -# eligible = in_effect & any(is_aca_ptc_eligible) & (aca_magi_fraction <= 3.0) -# aca_magi_fraction is injected directly so the 300% boundary is exact and each -# expectation is hand-verifiable. There is no minimum-income floor. +# at or below 300% of the federal poverty line (33 V.S.A. 1812(a)(1): "less +# than or equal to 300 percent"), on the exact ratio of ACA MAGI to the +# prior-year guideline, not the truncated aca_magi_fraction: +# eligible = in_effect & any(is_aca_ptc_eligible) & (aca_magi / FPG <= 3.0) +# Supplied-income cases pin the prior-year guideline to $10,000 and set +# aca_magi to the target ratio, so each expectation is hand-verifiable. +# Income-derived cases use the actual 2025 one-person guideline, $15,650. +# There is no minimum-income floor. - name: Case 1, 250% FPL APTC-eligible Vermont tax unit is eligible. # in_effect (2026) true; member APTC-eligible; 2.50 <= 3.0 -> eligible. @@ -17,7 +21,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -36,7 +42,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.99 + aca_magi: 29_900 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -55,7 +63,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 3.00 + aca_magi: 30_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -74,7 +84,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 3.01 + aca_magi: 30_100 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -93,7 +105,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -115,7 +129,9 @@ tax_units: tax_unit: members: [person1, person2] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1, person2] @@ -134,7 +150,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -153,7 +171,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2024: 10_000 households: household: members: [person1] @@ -172,7 +192,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2026: 10_000 households: household: members: [person1] @@ -181,7 +203,7 @@ vt_premium_assistance_eligible: true - name: Case 10, 300.01% FPL just barely above the ceiling is ineligible. - # 3.0001 > 3.0 -> ineligible. Tightest just-above-ceiling case (REQ-2 boundary), + # 30_001 / 10_000 = 3.0001 > 3.0 -> ineligible. Tightest just-above-ceiling case (REQ-2 boundary), # complementing the inclusive 3.00 eligible case and the coarser 3.01 case. period: 2026 input: @@ -192,10 +214,89 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 3.0001 + aca_magi: 30_001 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] state_code: VT output: vt_premium_assistance_eligible: false + +- name: Case 11, $46,950 (exactly 300% of the 2025 guideline) is eligible. + # 3 x $15,650 = $46,950; aca_magi_fraction 3.00. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 46_950 + households: + household: + members: [person1] + state_code: VT + output: + aca_magi_fraction: 3.00 + vt_premium_assistance_eligible: true + +- name: Case 12, $46,950.47 (300.003%) is ineligible though truncated to 3.00. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 46_950.47 + households: + household: + members: [person1] + state_code: VT + output: + aca_magi_fraction: 3.00 + vt_premium_assistance_eligible: false + +- name: Case 13, $47,105.56 (300.994%) is ineligible though truncated to 3.00. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 47_105.56 + households: + household: + members: [person1] + state_code: VT + output: + aca_magi_fraction: 3.00 + vt_premium_assistance_eligible: false + +- name: Case 14, $46,949 (299.994%) is eligible. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 46_949 + households: + household: + members: [person1] + state_code: VT + output: + aca_magi_fraction: 2.99 + vt_premium_assistance_eligible: true diff --git a/policyengine_us/variables/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.py b/policyengine_us/variables/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.py index 4281774a6e8..4ae29555bda 100644 --- a/policyengine_us/variables/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.py +++ b/policyengine_us/variables/gov/states/vt/dvha/premium_assistance/vt_premium_assistance_eligible.py @@ -17,7 +17,9 @@ class vt_premium_assistance_eligible(Variable): "premium tax credit (which embeds on-Marketplace enrollment, the " "married-filing-separately exclusion, and the federal required-" "contribution income test), and household income is at or below 300% of " - "the federal poverty line. Vermont imposes no minimum-income floor, so " + "the federal poverty line, measured as the exact ratio of ACA MAGI to " + "the prior-year guideline rather than the truncated aca_magi_fraction. " + "Vermont imposes no minimum-income floor, so " "the low end is governed by the federal PTC gate, including its below-" "poverty immigration exception, which Vermont inherits rather than " "overrides." @@ -28,6 +30,11 @@ def formula(tax_unit, period, parameters): in_effect = p.in_effect # At least one member must be eligible for the federal ACA PTC. aptc_eligible = tax_unit.any(tax_unit.members("is_aca_ptc_eligible", period)) - magi_fraction = tax_unit("aca_magi_fraction", period) - income_eligible = magi_fraction <= p.fpl_limit + # 33 V.S.A. 1812(a)(1) covers income "less than or equal to 300 + # percent of federal poverty level". Test the exact ratio, not the + # truncated aca_magi_fraction: truncation maps 300.00%-300.99% to + # 3.00, which would admit incomes above 300%. + magi = max_(tax_unit("aca_magi", period), 0) + exact_magi_frac = magi / tax_unit("tax_unit_fpg", period.last_year) + income_eligible = exact_magi_frac <= p.fpl_limit return in_effect & aptc_eligible & income_eligible