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