From ccc11f9be0e76b28120412aada575cea942e1e25 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Thu, 12 Nov 2026 22:18:02 -0500 Subject: [PATCH 1/8] Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fraction aca_magi_fraction read the household's state for the guideline year, so a state supplied only for the coverage year fell back to the default state there. It now reads the state for the coverage year and lags only the guideline (26 CFR 1.36B-1(h)). Fixes #9482. Co-Authored-By: Claude Opus 5.5 --- .../aca-fpl-coverage-year-state.fixed.md | 1 + .../aca/eligibility/aca_magi_fraction.yaml | 60 +++++++++++++++++++ .../aca/eligibility/is_aca_ptc_eligible.yaml | 25 ++++++++ .../gov/aca/eligibility/aca_magi_fraction.py | 22 ++++++- 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 changelog.d/aca-fpl-coverage-year-state.fixed.md diff --git a/changelog.d/aca-fpl-coverage-year-state.fixed.md b/changelog.d/aca-fpl-coverage-year-state.fixed.md new file mode 100644 index 00000000000..354a748a253 --- /dev/null +++ b/changelog.d/aca-fpl-coverage-year-state.fixed.md @@ -0,0 +1 @@ +The premium tax credit's poverty-line fraction uses the Alaska and Hawaii guidelines for households in those states when the state is supplied only for the coverage year. diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml index b385dbecf65..7fde259bdab 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml @@ -40,3 +40,63 @@ state_code: TX output: aca_magi_fraction: 2.54 + +- name: Coverage in 2026 in Alaska uses Alaska's 2025 guideline. + # 30,000 / 19,550 = 1.5345, floored to 1.53. The contiguous states' + # guideline (15,650) would give 1.91. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 1.53 + +- name: Coverage in 2026 in Hawaii uses Hawaii's 2025 guideline. + # 30,000 / 17,990 = 1.6676, floored to 1.66. The contiguous states' + # guideline (15,650) would give 1.91. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: HI + output: + aca_magi_fraction: 1.66 + +- name: Coverage in 2026 for an Alaska family of three uses Alaska's per-person increment. + # 19,550 + 2 x 6,880 = 33,310; 50,000 / 33,310 = 1.5011, floored to 1.50. + period: 2026 + input: + people: + parent: + age: 40 + child1: + age: 10 + child2: + age: 8 + tax_units: + tax_unit: + members: [parent, child1, child2] + aca_magi: 50_000 + households: + household: + members: [parent, child1, child2] + state_code: AK + output: + aca_magi_fraction: 1.5 diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index 00ff4c05de5..79f25ed1cc3 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -512,3 +512,28 @@ output: is_medicare_eligible: false is_aca_ptc_eligible: true + +- name: 2026 Alaska single adult at $70,000 is under 400% of Alaska's guideline + # 70,000 / 19,550 (Alaska's 2025 guideline for one) = 3.58, inside the + # 1.00 to 4.00 range. The contiguous states' guideline (15,650) would give + # 4.47, above it. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 70_000 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 3.58 + is_aca_ptc_eligible: true diff --git a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py index 871a801df80..f67adecaedd 100644 --- a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py +++ b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py @@ -1,5 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.tools.parameters import FIRST_MODELED_YEAR +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class aca_magi_fraction(Variable): @@ -9,7 +10,10 @@ class aca_magi_fraction(Variable): documentation = ( "ACA-related MAGI as fraction of federal poverty line. The first " "reference documents the use of the prior-year FPL; the second " - "documents the truncation of the fraction." + "documents the truncation of the fraction; the third sets the state " + "whose guideline applies: where the taxpayer lives during the " + "coverage year, so Alaska and Hawaii households use their own " + "guidelines." ) reference = ( dict( @@ -20,6 +24,10 @@ class aca_magi_fraction(Variable): title="2022 IRS Form 8962 instructions, Line 5 Worksheet 2", href="https://www.irs.gov/pub/irs-pdf/i8962.pdf#page=8", ), + dict( + title="26 CFR 1.36B-1(h)", + href="https://www.law.cornell.edu/cfr/text/26/1.36B-1#h", + ), ) definition_period = YEAR @@ -34,5 +42,13 @@ def formula(tax_unit, period, parameters): guideline_period = period.last_year if guideline_period.start.year < FIRST_MODELED_YEAR: guideline_period = period - fpg = tax_unit("tax_unit_fpg", guideline_period) - return np.floor(100 * magi / fpg) / 100 + # The state is where the taxpayer lives during the coverage year + # (26 CFR 1.36B-1(h)); only the guideline lags. A state supplied only + # for the coverage year would otherwise fall back to the default + # state in the guideline year, giving an Alaska or Hawaii household + # the contiguous states' guideline. + size = tax_unit("tax_unit_size", guideline_period) + state_group = tax_unit.household("state_group_str", period) + year_lag = period.start.year - guideline_period.start.year + guideline = fpg(size, state_group, period, parameters, year_lag=year_lag) + return np.floor(100 * magi / guideline) / 100 From fa21a8006cf6663e91d6e898c6ef4a0e5047ee60 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 09:16:26 -0500 Subject: [PATCH 2/8] Use the coverage year's family size, handle territories, and fix references - Read tax_unit_size for the coverage year (26 CFR 1.36B-1(d)(1)); only the guideline lags. - Backdate the Guam, Puerto Rico and Virgin Islands guideline placeholders to 1992, so coverage before 2025 no longer raises ParameterNotFoundError for a territory household. Their fraction is infinite, and is_aca_ptc_eligible now requires a finite fraction, since the 2021-2025 eligibility rows have no upper limit and no Exchange operates in the territories (36B(c)(2)(A)). - Replace the titled Form 8962 references with a bare URL pinned to the 2025 instructions (Line 4 and Worksheet 2, file pages 8-9) and drop the documentation that counted reference entries. - Tests: a mover from Alaska to Texas, coverage-year family size, 2025 coverage in Alaska, Alaska and Texas in one simulation, Alaska's 100% and 400% edges, and a 2024 Puerto Rico household. Co-Authored-By: Claude Opus 5.5 --- .../aca-fpl-coverage-year-state.fixed.md | 2 +- policyengine_us/parameters/gov/hhs/fpg.yaml | 12 +-- .../aca/eligibility/aca_magi_fraction.yaml | 89 +++++++++++++++++++ .../aca/eligibility/is_aca_ptc_eligible.yaml | 74 +++++++++++++++ .../gov/aca/eligibility/aca_magi_fraction.py | 51 +++++------ .../aca/eligibility/is_aca_ptc_eligible.py | 8 +- 6 files changed, 201 insertions(+), 35 deletions(-) diff --git a/changelog.d/aca-fpl-coverage-year-state.fixed.md b/changelog.d/aca-fpl-coverage-year-state.fixed.md index 354a748a253..63fabce965c 100644 --- a/changelog.d/aca-fpl-coverage-year-state.fixed.md +++ b/changelog.d/aca-fpl-coverage-year-state.fixed.md @@ -1 +1 @@ -The premium tax credit's poverty-line fraction uses the Alaska and Hawaii guidelines for households in those states when the state is supplied only for the coverage year. +The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, are no longer eligible for the credit and no longer raise an error for coverage before 2025. diff --git a/policyengine_us/parameters/gov/hhs/fpg.yaml b/policyengine_us/parameters/gov/hhs/fpg.yaml index e2953113a7a..40cbb0e1111 100644 --- a/policyengine_us/parameters/gov/hhs/fpg.yaml +++ b/policyengine_us/parameters/gov/hhs/fpg.yaml @@ -50,11 +50,11 @@ first_person: # completeness and to avoid errors arising from uprating and breakdown # metadata fields. GU: - 2024-01-01: 0 + 1992-01-01: 0 PR: - 2024-01-01: 0 + 1992-01-01: 0 VI: - 2024-01-01: 0 + 1992-01-01: 0 additional_person: # Calculated by subtracting a two-person household's line from a one-person's. # This difference is always the same for larger households. @@ -105,11 +105,11 @@ additional_person: 2026-01-01: 6_530 # Add placeholders for Guam, Puerto Rico, and the Virgin Islands. GU: - 2024-01-01: 0 + 1992-01-01: 0 PR: - 2024-01-01: 0 + 1992-01-01: 0 VI: - 2024-01-01: 0 + 1992-01-01: 0 metadata: unit: currency-USD diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml index 7fde259bdab..05c64692d65 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml @@ -100,3 +100,92 @@ state_code: AK output: aca_magi_fraction: 1.5 + +- name: A household that moves from Alaska in 2025 to Texas in 2026 uses Texas's guideline for 2026 coverage. + # The state of residence in the coverage year decides the guideline, not + # the state in the guideline year. 30,000 / 15,650 = 1.9169, floored to + # 1.91. Alaska's 2025 guideline (19,550) would give 1.53. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: + 2025: AK + 2026: TX + output: + aca_magi_fraction: 1.91 + +- name: Family size is the coverage year's. + # tax_unit_size is 3 for 2026 only; 2025 counts the one listed member. + # Alaska's 2025 guideline for three: 19,550 + 2 x 6,880 = 33,310. + # 50,000 / 33,310 = 1.5011, floored to 1.50. The 2025 size of one would + # give 50,000 / 19,550 = 2.55. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 50_000 + tax_unit_size: + 2026: 3 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 1.5 + +- name: Coverage in 2025 in Alaska uses Alaska's 2024 guideline. + # 30,000 / 18,810 = 1.5949, floored to 1.59. + period: 2025 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 1.59 + +- name: Households in Alaska and Texas in one simulation each use their own state's guideline. + # 30,000 / 19,550 = 1.53 and 30,000 / 15,650 = 1.91. + period: 2026 + input: + people: + person1: + age: 40 + person2: + age: 40 + tax_units: + tax_unit1: + members: [person1] + aca_magi: 30_000 + tax_unit2: + members: [person2] + aca_magi: 30_000 + households: + household1: + members: [person1] + state_code: AK + household2: + members: [person2] + state_code: TX + output: + aca_magi_fraction: [1.53, 1.91] diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index 79f25ed1cc3..eb31c475098 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -537,3 +537,77 @@ output: aca_magi_fraction: 3.58 is_aca_ptc_eligible: true + +- name: 2026 Alaska single adult just under Alaska's guideline is below the 100% floor + # 19,549 / 19,550 = 0.99995, floored to 0.99, below the 1.00 floor; the + # below-poverty exception ends in 2026. The contiguous states' guideline + # (15,650) would give 1.24. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 19_549 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 0.99 + is_aca_ptc_eligible: false + +- name: 2026 Alaska single adult just under 400% of Alaska's guideline is eligible + # 78,199 / 19,550 = 3.99995, floored to 3.99, inside the 1.00 to 4.00 + # range. The contiguous states' guideline (15,650) would give 4.99. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 78_199 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 3.99 + is_aca_ptc_eligible: true + +- name: 2024 Puerto Rico single adult is not eligible + # HHS defines no guideline for Puerto Rico, and no Exchange operates there + # (26 U.S.C. 36B(c)(2)(A)). The placeholder guideline is 0, so the + # fraction is infinite; 2024 has no 400% limit, so the finite-fraction + # check is what makes the result false. + period: 2024 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: PR + output: + is_aca_ptc_eligible: false diff --git a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py index f67adecaedd..bd00a4ff585 100644 --- a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py +++ b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py @@ -8,26 +8,15 @@ class aca_magi_fraction(Variable): entity = TaxUnit label = "ACA-related modified AGI as fraction of prior-year FPL" documentation = ( - "ACA-related MAGI as fraction of federal poverty line. The first " - "reference documents the use of the prior-year FPL; the second " - "documents the truncation of the fraction; the third sets the state " - "whose guideline applies: where the taxpayer lives during the " - "coverage year, so Alaska and Hawaii households use their own " - "guidelines." + "ACA-related MAGI as a fraction of the federal poverty line for the " + "year before the coverage year, for the family size and state of " + "residence in the coverage year, truncated to whole percentage points." ) reference = ( - dict( - title="2022 IRS Form 8962 (ACA PTC) instructions, Line 4", - href="https://www.irs.gov/pub/irs-pdf/i8962.pdf#page=7", - ), - dict( - title="2022 IRS Form 8962 instructions, Line 5 Worksheet 2", - href="https://www.irs.gov/pub/irs-pdf/i8962.pdf#page=8", - ), - dict( - title="26 CFR 1.36B-1(h)", - href="https://www.law.cornell.edu/cfr/text/26/1.36B-1#h", - ), + # PDF pages 8-9: Line 4 (state of residence, prior-year guideline, + # Alaska and Hawaii tables) and Worksheet 2 (truncation). + "https://www.irs.gov/pub/irs-prior/i8962--2025.pdf#page=8", + "https://www.law.cornell.edu/cfr/text/26/1.36B-1#h", ) definition_period = YEAR @@ -42,13 +31,21 @@ def formula(tax_unit, period, parameters): guideline_period = period.last_year if guideline_period.start.year < FIRST_MODELED_YEAR: guideline_period = period - # The state is where the taxpayer lives during the coverage year - # (26 CFR 1.36B-1(h)); only the guideline lags. A state supplied only - # for the coverage year would otherwise fall back to the default - # state in the guideline year, giving an Alaska or Hawaii household - # the contiguous states' guideline. - size = tax_unit("tax_unit_size", guideline_period) + # Only the guideline lags. Family size is the coverage year's (26 CFR + # 1.36B-1(d)(1)), and so is the state: Form 8962 Line 4 uses the + # guideline for "your state of residence" in the coverage year, so + # Alaska and Hawaii households use their own guidelines. 1.36B-1(h) + # applies the higher guideline when the residence moves into or out of + # Alaska or Hawaii during the year, or spouses live in different + # states; the model has one household state a year and uses the first + # member's household. + size = tax_unit("tax_unit_size", period) state_group = tax_unit.household("state_group_str", period) - year_lag = period.start.year - guideline_period.start.year - guideline = fpg(size, state_group, period, parameters, year_lag=year_lag) - return np.floor(100 * magi / guideline) / 100 + guideline = fpg(size, state_group, guideline_period, parameters) + # HHS defines no guideline for Puerto Rico, Guam or the Virgin + # Islands, whose placeholder is 0, and no Exchange operates there + # (26 U.S.C. 36B(c)(2)(A)). Their fraction is infinite, above every + # income limit that reads it. + has_guideline = guideline > 0 + fraction = magi / where(has_guideline, guideline, 1) + return where(has_guideline, np.floor(100 * fraction) / 100, np.inf) diff --git a/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py b/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py index b21baa83d3c..a9192b52d94 100644 --- a/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py +++ b/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py @@ -14,7 +14,13 @@ def formula(person, period, parameters): # determine income eligibility for ACA PTC p = parameters(period).gov.aca magi_frac = person.tax_unit("aca_magi_fraction", period) - standard_income_eligible = p.ptc_income_eligibility.calc(magi_frac) + # The fraction is infinite in Puerto Rico, Guam and the Virgin Islands, + # which have no poverty guideline and no Exchange (26 U.S.C. + # 36B(c)(2)(A)). Without this check the 2021-2025 rows of + # ptc_income_eligibility, which have no upper limit, would pass it. + standard_income_eligible = p.ptc_income_eligibility.calc( + magi_frac + ) & np.isfinite(magi_frac) below_fpl_exception = person.tax_unit( "aca_ptc_below_fpl_immigration_exception", period ) From 391384776f1e2340047620e90acfe09ea1f6ecc1 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 09:35:45 -0500 Subject: [PATCH 3/8] Keep the 2024 territory guideline placeholder as the last value Moving the placeholders' only value to 1992 made uprating start in 1992, where gov.hhs.fpg_uprating is undefined, so the parameter tree failed to load. Keep 2024-01-01: 0 as the last value and add 1992-01-01: 0 before it. Co-Authored-By: Claude Opus 5.5 --- policyengine_us/parameters/gov/hhs/fpg.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/hhs/fpg.yaml b/policyengine_us/parameters/gov/hhs/fpg.yaml index 40cbb0e1111..4d52f8dacee 100644 --- a/policyengine_us/parameters/gov/hhs/fpg.yaml +++ b/policyengine_us/parameters/gov/hhs/fpg.yaml @@ -48,13 +48,18 @@ first_person: # Add placeholders for Guam, Puerto Rico, and the Virgin Islands. # Poverty guidelines do not apply to these areas, but we include them for # completeness and to avoid errors arising from uprating and breakdown - # metadata fields. + # metadata fields. The 1992 entry lets every guideline year resolve; the + # 2024 entry stays the last value, so uprating starts where its index is + # defined. GU: 1992-01-01: 0 + 2024-01-01: 0 PR: 1992-01-01: 0 + 2024-01-01: 0 VI: 1992-01-01: 0 + 2024-01-01: 0 additional_person: # Calculated by subtracting a two-person household's line from a one-person's. # This difference is always the same for larger households. @@ -106,10 +111,13 @@ additional_person: # Add placeholders for Guam, Puerto Rico, and the Virgin Islands. GU: 1992-01-01: 0 + 2024-01-01: 0 PR: 1992-01-01: 0 + 2024-01-01: 0 VI: 1992-01-01: 0 + 2024-01-01: 0 metadata: unit: currency-USD From a9cc99bc1390ec9c8ac96d4ffe24734179f2fc15 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 11:10:25 -0500 Subject: [PATCH 4/8] Charge no Marketplace premium in the territories; pin the zero-guideline guard - pays_aca_premium is false where aca_magi_fraction is infinite, which is where HHS publishes no guideline: Puerto Rico, Guam and the Virgin Islands have no Exchange (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). An infinite fraction was outside the coverage gap, so a territory household below the floor, $0 included, was treated as paying a Marketplace premium. - Drop the finite-fraction check in is_aca_ptc_eligible: core's single-amount scale already maps an infinite fraction to its false out-of-range cell, so the check changed nothing, and its comment said otherwise. - New 2026 Puerto Rico case at $0 of MAGI pins the zero-guideline guard: the required contribution is 0, where plain division gives NaN and a fraction of 0 gives 0.021. The 2024 case now asserts pays_aca_premium false. - Cite 1.36B-1(d) rather than (d)(1), and 42 U.S.C. 18024(d). Co-Authored-By: Claude Opus 5.5 --- .../aca-fpl-coverage-year-state.fixed.md | 2 +- .../aca/eligibility/is_aca_ptc_eligible.yaml | 39 +++++++++++++++++-- .../gov/aca/eligibility/aca_magi_fraction.py | 8 ++-- .../aca/eligibility/is_aca_ptc_eligible.py | 8 +--- .../gov/aca/eligibility/pays_aca_premium.py | 18 ++++++++- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/changelog.d/aca-fpl-coverage-year-state.fixed.md b/changelog.d/aca-fpl-coverage-year-state.fixed.md index 63fabce965c..365fb2041e9 100644 --- a/changelog.d/aca-fpl-coverage-year-state.fixed.md +++ b/changelog.d/aca-fpl-coverage-year-state.fixed.md @@ -1 +1 @@ -The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, are no longer eligible for the credit and no longer raise an error for coverage before 2025. +The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, pay no Marketplace premium and are not eligible for the credit, and their income fraction no longer raises an error for coverage before 2025. diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index eb31c475098..a626e4d30af 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -587,11 +587,13 @@ aca_magi_fraction: 3.99 is_aca_ptc_eligible: true -- name: 2024 Puerto Rico single adult is not eligible +- name: 2024 Puerto Rico single adult pays no Marketplace premium and is not eligible # HHS defines no guideline for Puerto Rico, and no Exchange operates there - # (26 U.S.C. 36B(c)(2)(A)). The placeholder guideline is 0, so the - # fraction is infinite; 2024 has no 400% limit, so the finite-fraction - # check is what makes the result false. + # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). The placeholder guideline + # is 0, so the fraction is infinite and pays_aca_premium is false. Without + # the no-Exchange test, the infinite fraction is outside the coverage gap + # and pays_aca_premium would be true. The 2023 guideline lookup raised + # ParameterNotFoundError before the placeholders were backdated to 1992. period: 2024 input: people: @@ -610,4 +612,33 @@ members: [person1] state_code: PR output: + pays_aca_premium: false + is_aca_ptc_eligible: false + +- name: 2026 Puerto Rico single adult with no income has a required contribution of 0 + # The guideline is 0, so the fraction is infinite rather than 0 / 0. An + # infinite fraction falls in the top bracket, above the 400% limit, and + # 2026 has no income eligibility there, so the percentage is capped out + # to 0. Plain division would give NaN here, and a fraction of 0 would give + # the 2026 initial rate of 0.021. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 0 + households: + household: + members: [person1] + state_code: PR + output: + aca_required_contribution_percentage: 0 + pays_aca_premium: false is_aca_ptc_eligible: false diff --git a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py index bd00a4ff585..b80540dcd29 100644 --- a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py +++ b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py @@ -32,7 +32,7 @@ def formula(tax_unit, period, parameters): if guideline_period.start.year < FIRST_MODELED_YEAR: guideline_period = period # Only the guideline lags. Family size is the coverage year's (26 CFR - # 1.36B-1(d)(1)), and so is the state: Form 8962 Line 4 uses the + # 1.36B-1(d)), and so is the state: Form 8962 Line 4 uses the # guideline for "your state of residence" in the coverage year, so # Alaska and Hawaii households use their own guidelines. 1.36B-1(h) # applies the higher guideline when the residence moves into or out of @@ -44,8 +44,10 @@ def formula(tax_unit, period, parameters): guideline = fpg(size, state_group, guideline_period, parameters) # HHS defines no guideline for Puerto Rico, Guam or the Virgin # Islands, whose placeholder is 0, and no Exchange operates there - # (26 U.S.C. 36B(c)(2)(A)). Their fraction is infinite, above every - # income limit that reads it. + # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). Their fraction is + # infinite rather than 0 / 0: pays_aca_premium reads it as no + # Exchange, the eligibility scale's out-of-range cell is false, and + # the required contribution percentage is capped out to 0. has_guideline = guideline > 0 fraction = magi / where(has_guideline, guideline, 1) return where(has_guideline, np.floor(100 * fraction) / 100, np.inf) diff --git a/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py b/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py index a9192b52d94..b21baa83d3c 100644 --- a/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py +++ b/policyengine_us/variables/gov/aca/eligibility/is_aca_ptc_eligible.py @@ -14,13 +14,7 @@ def formula(person, period, parameters): # determine income eligibility for ACA PTC p = parameters(period).gov.aca magi_frac = person.tax_unit("aca_magi_fraction", period) - # The fraction is infinite in Puerto Rico, Guam and the Virgin Islands, - # which have no poverty guideline and no Exchange (26 U.S.C. - # 36B(c)(2)(A)). Without this check the 2021-2025 rows of - # ptc_income_eligibility, which have no upper limit, would pass it. - standard_income_eligible = p.ptc_income_eligibility.calc( - magi_frac - ) & np.isfinite(magi_frac) + standard_income_eligible = p.ptc_income_eligibility.calc(magi_frac) below_fpl_exception = person.tax_unit( "aca_ptc_below_fpl_immigration_exception", period ) diff --git a/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py b/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py index 75c148f4766..7f1ef5cddc7 100644 --- a/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py +++ b/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py @@ -12,13 +12,20 @@ class pays_aca_premium(Variable): # The premium tax credit's income floor, and the below-poverty # exception for lawfully present immigrants. "https://www.law.cornell.edu/uscode/text/26/36B#c_1", + # The credit's plans are those enrolled in through an Exchange + # established by a State (36B(c)(2)(A)); "State" means the 50 States + # and the District of Columbia (42 U.S.C. 18024(d)). + "https://www.law.cornell.edu/uscode/text/26/36B#c_2_A", + "https://www.law.cornell.edu/uscode/text/42/18024#d", ) documentation = ( "Whether a person is enrolled in a Marketplace plan and charged an " "age-rated premium for it. This is an enrollment test, not a pure " "status test: alongside the TIN, immigration-status, other-minimum-" "essential-coverage and age-based premium conditions, it excludes the " - "Medicaid coverage gap, so it carries an income component. " + "Medicaid coverage gap, so it carries an income component, and it " + "excludes Puerto Rico, Guam and the Virgin Islands, which have no " + "Exchange. " "45 CFR 155.305(a) sets no income minimum for enrolling, so a person " "in the gap may buy a plan at full price; the model assumes none " "does, because the unsubsidized benchmark premium exceeds their " @@ -64,9 +71,18 @@ def formula(person, period, parameters): & ~below_fpl_exception ) + # Puerto Rico, Guam and the Virgin Islands have no Exchange: 36B(c)(2)(A) + # counts plans enrolled in through an Exchange established by a State, + # and 42 U.S.C. 18024(d) defines a State as the 50 States and the + # District of Columbia. HHS publishes no poverty guideline for them + # either, so aca_magi_fraction is infinite there, and nobody in them + # is charged a Marketplace premium. + has_exchange = np.isfinite(magi_frac) + return ( is_status_eligible & is_coverage_eligible & pays_age_based_premium & ~in_coverage_gap + & has_exchange ) From f72af6389bf5cce9258a9e541546a79f1b59830a Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 13:40:38 -0500 Subject: [PATCH 5/8] Multiply before dividing in the fraction; drop the fpg.yaml edit; test territories by state group Per Pavel's review: - C1: compute 100 * MAGI / guideline before flooring. Dividing first floored income exactly on a whole percentage one point low in every state (Texas, $35,369 in 2026: 2.25 instead of 2.26). New case pins it. - S1: restore fpg.yaml. Core backdates each parameter's first value to 2015, so the 2024 territory placeholder already answers every guideline year the credit reads; the 1992 entries only changed 2015 results outside the ACA. Correct the changelog and the 2024 Puerto Rico case's comment. - Suggestion 1: pays_aca_premium tests the state group for Guam, Puerto Rico and the Virgin Islands instead of reading an infinite fraction. - Add PR 10413's negative-income case. Co-Authored-By: Claude Opus 5.5 --- .../aca-fpl-coverage-year-state.fixed.md | 2 +- policyengine_us/parameters/gov/hhs/fpg.yaml | 10 +---- .../aca/eligibility/aca_magi_fraction.yaml | 38 +++++++++++++++++++ .../aca/eligibility/is_aca_ptc_eligible.yaml | 11 +++--- .../gov/aca/eligibility/aca_magi_fraction.py | 14 ++++--- .../gov/aca/eligibility/pays_aca_premium.py | 13 +++++-- 6 files changed, 64 insertions(+), 24 deletions(-) diff --git a/changelog.d/aca-fpl-coverage-year-state.fixed.md b/changelog.d/aca-fpl-coverage-year-state.fixed.md index 365fb2041e9..48038c1c800 100644 --- a/changelog.d/aca-fpl-coverage-year-state.fixed.md +++ b/changelog.d/aca-fpl-coverage-year-state.fixed.md @@ -1 +1 @@ -The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, pay no Marketplace premium and are not eligible for the credit, and their income fraction no longer raises an error for coverage before 2025. +The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, pay no Marketplace premium and are not eligible for the credit. Income exactly on a whole percentage of the poverty line is no longer floored one point low. diff --git a/policyengine_us/parameters/gov/hhs/fpg.yaml b/policyengine_us/parameters/gov/hhs/fpg.yaml index 4d52f8dacee..e2953113a7a 100644 --- a/policyengine_us/parameters/gov/hhs/fpg.yaml +++ b/policyengine_us/parameters/gov/hhs/fpg.yaml @@ -48,17 +48,12 @@ first_person: # Add placeholders for Guam, Puerto Rico, and the Virgin Islands. # Poverty guidelines do not apply to these areas, but we include them for # completeness and to avoid errors arising from uprating and breakdown - # metadata fields. The 1992 entry lets every guideline year resolve; the - # 2024 entry stays the last value, so uprating starts where its index is - # defined. + # metadata fields. GU: - 1992-01-01: 0 2024-01-01: 0 PR: - 1992-01-01: 0 2024-01-01: 0 VI: - 1992-01-01: 0 2024-01-01: 0 additional_person: # Calculated by subtracting a two-person household's line from a one-person's. @@ -110,13 +105,10 @@ additional_person: 2026-01-01: 6_530 # Add placeholders for Guam, Puerto Rico, and the Virgin Islands. GU: - 1992-01-01: 0 2024-01-01: 0 PR: - 1992-01-01: 0 2024-01-01: 0 VI: - 1992-01-01: 0 2024-01-01: 0 metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml index 05c64692d65..507ff462945 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml @@ -189,3 +189,41 @@ state_code: TX output: aca_magi_fraction: [1.53, 1.91] + +- name: Income exactly on a whole percentage of the guideline is not floored one point low. + # 100 x 35,369 / 15,650 = 226 exactly, so Worksheet 2 gives 226. Dividing + # first gives 35,369 / 15,650 = 2.2599999..., which floors to 2.25. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: 35_369 + households: + household: + members: [person1] + state_code: TX + output: + aca_magi_fraction: 2.26 + +- name: Negative ACA MAGI gives a fraction of 0. + # MAGI is floored at 0 before dividing (Form 8962 line 3 enters -0- for a + # negative total). From PR 10413's cases. + period: 2026 + input: + people: + person1: + age: 40 + tax_units: + tax_unit: + members: [person1] + aca_magi: -5_000 + households: + household: + members: [person1] + state_code: AK + output: + aca_magi_fraction: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index a626e4d30af..7f9e325d2c4 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -589,11 +589,12 @@ - name: 2024 Puerto Rico single adult pays no Marketplace premium and is not eligible # HHS defines no guideline for Puerto Rico, and no Exchange operates there - # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). The placeholder guideline - # is 0, so the fraction is infinite and pays_aca_premium is false. Without - # the no-Exchange test, the infinite fraction is outside the coverage gap - # and pays_aca_premium would be true. The 2023 guideline lookup raised - # ParameterNotFoundError before the placeholders were backdated to 1992. + # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)), so pays_aca_premium is + # false. Without the no-Exchange test, the infinite fraction (the + # placeholder guideline is 0) is outside the coverage gap and + # pays_aca_premium would be true. On main, with the state supplied for 2024 + # only, the 2023 guideline year fell back to the default state's guideline + # (30,000 / 14,580, floored to 2.05), so the household was eligible. period: 2024 input: people: diff --git a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py index b80540dcd29..de2e9b84e9d 100644 --- a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py +++ b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py @@ -45,9 +45,13 @@ def formula(tax_unit, period, parameters): # HHS defines no guideline for Puerto Rico, Guam or the Virgin # Islands, whose placeholder is 0, and no Exchange operates there # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). Their fraction is - # infinite rather than 0 / 0: pays_aca_premium reads it as no - # Exchange, the eligibility scale's out-of-range cell is false, and - # the required contribution percentage is capped out to 0. + # infinite rather than 0 / 0 (NaN at $0 of income): the eligibility + # scale's out-of-range cell is false, and the required contribution + # percentage is capped out to 0. pays_aca_premium excludes them by + # state group. has_guideline = guideline > 0 - fraction = magi / where(has_guideline, guideline, 1) - return where(has_guideline, np.floor(100 * fraction) / 100, np.inf) + # Multiply before dividing, so income exactly on a whole percentage + # (100 x 35,369 / 15,650 = 226) isn't floored one point low by a + # quotient that isn't exactly representable. + percent = 100 * magi / where(has_guideline, guideline, 1) + return where(has_guideline, np.floor(percent) / 100, np.inf) diff --git a/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py b/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py index 7f1ef5cddc7..457a36692ef 100644 --- a/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py +++ b/policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py @@ -74,10 +74,15 @@ def formula(person, period, parameters): # Puerto Rico, Guam and the Virgin Islands have no Exchange: 36B(c)(2)(A) # counts plans enrolled in through an Exchange established by a State, # and 42 U.S.C. 18024(d) defines a State as the 50 States and the - # District of Columbia. HHS publishes no poverty guideline for them - # either, so aca_magi_fraction is infinite there, and nobody in them - # is charged a Marketplace premium. - has_exchange = np.isfinite(magi_frac) + # District of Columbia. So nobody in them is charged a Marketplace + # premium. + state_group = person.household("state_group", period) + groups = state_group.possible_values + has_exchange = ( + (state_group != groups.GU) + & (state_group != groups.PR) + & (state_group != groups.VI) + ) return ( is_status_eligible From 1e306d09cc7c33f6c46ca2eb3b9f8f1b1a899462 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 13:41:49 -0500 Subject: [PATCH 6/8] Drop the changelog line about whole-percentage truncation main already multiplies before dividing, so the truncation order changes nothing against main; only this PR's earlier commit had regressed it. Co-Authored-By: Claude Opus 5.5 --- changelog.d/aca-fpl-coverage-year-state.fixed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/aca-fpl-coverage-year-state.fixed.md b/changelog.d/aca-fpl-coverage-year-state.fixed.md index 48038c1c800..b0d4eafb5a3 100644 --- a/changelog.d/aca-fpl-coverage-year-state.fixed.md +++ b/changelog.d/aca-fpl-coverage-year-state.fixed.md @@ -1 +1 @@ -The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, pay no Marketplace premium and are not eligible for the credit. Income exactly on a whole percentage of the poverty line is no longer floored one point low. +The premium tax credit's poverty-line fraction uses the state of residence and family size for the coverage year, so Alaska and Hawaii households use their own guidelines when the state is supplied only for the coverage year. Households in Puerto Rico, Guam and the Virgin Islands, which have no poverty guideline and no Exchange, pay no Marketplace premium and are not eligible for the credit. From 62e9ac802651b545dcbf2d46d7e39dccd73a05f5 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 15:53:57 -0500 Subject: [PATCH 7/8] Test the Guam and Virgin Islands exclusions from Marketplace premiums Per Pavel's re-review: the territory test is three state-group comparisons, and only Puerto Rico's had a case. Each new case fails without its comparison. Co-Authored-By: Claude Opus 5.5 --- .../aca/eligibility/is_aca_ptc_eligible.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index 7f9e325d2c4..94db5294432 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -643,3 +643,54 @@ aca_required_contribution_percentage: 0 pays_aca_premium: false is_aca_ptc_eligible: false + +- name: 2026 Guam single adult pays no Marketplace premium and is not eligible + # Guam has no Exchange (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). Without + # the Guam comparison in pays_aca_premium, the premium would be charged: + # the infinite fraction is outside the coverage gap. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: GU + output: + pays_aca_premium: false + is_aca_ptc_eligible: false + +- name: 2026 Virgin Islands single adult pays no Marketplace premium and is not eligible + # The Virgin Islands have no Exchange (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. + # 18024(d)). Without the Virgin Islands comparison in pays_aca_premium, the + # premium would be charged: the infinite fraction is outside the coverage + # gap. + period: 2026 + input: + people: + person1: + age: 30 + is_medicaid_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + is_chip_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 30_000 + households: + household: + members: [person1] + state_code: VI + output: + pays_aca_premium: false + is_aca_ptc_eligible: false From d5f3f7a6b837c3696554eedcb8faeb849930ce27 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Fri, 13 Nov 2026 17:07:29 -0500 Subject: [PATCH 8/8] Re-pin the multiply-first truncation; check aca_fpg against the coverage-year state Per Pavel's re-review at d5909647d8: - S1: since #10041, aca_magi_fraction divides by the stored aca_fpg, and in 32-bit arithmetic 35,369 / 15,650 x 100 rounds to exactly 226, so that case no longer caught a divide-first formula. Use 36,621 (234% of 15,650), which divide-first floors to 233. - The claimable-filer invariant compares aca_fpg with the prior year's guideline for the coverage year's tax family size and state group, not with tax_unit_fpg for the prior year, which reads the prior year's state. - The territory cases assert is_aca_marketplace_enrollee, and their comments name where the comparisons now live. - is_aca_marketplace_enrollee documents that a supplied pays_aca_premium still marks an enrollee in the territories, with no credit. Co-Authored-By: Claude Opus 5.5 --- .../core/test_claimable_filer_ptc_invariants.py | 15 ++++++++++----- .../gov/aca/eligibility/aca_magi_fraction.yaml | 9 +++++---- .../gov/aca/eligibility/is_aca_ptc_eligible.yaml | 16 +++++++++++----- .../gov/aca/eligibility/aca_magi_fraction.py | 2 +- .../eligibility/is_aca_marketplace_enrollee.py | 5 ++++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/policyengine_us/tests/core/test_claimable_filer_ptc_invariants.py b/policyengine_us/tests/core/test_claimable_filer_ptc_invariants.py index 3b3295eef5f..06d19911481 100644 --- a/policyengine_us/tests/core/test_claimable_filer_ptc_invariants.py +++ b/policyengine_us/tests/core/test_claimable_filer_ptc_invariants.py @@ -31,9 +31,8 @@ on a return with a head or spouse where neither can be claimed, `aca_ptc_slcsp` = `slcsp`, `aca_magi` = `medicaid_magi`, `aca_tax_family_size` = `tax_unit_size` and - `aca_fpg` = `tax_unit_fpg` for the prior year (these simulations supply no - prior-year size, so the two sizes agree, and every drawn state is in the - contiguous group, so the coverage-year and prior-year state groups agree). A unit with neither a head nor a + `aca_fpg` = the prior year's guideline for the coverage year's tax family + size and state group (Form 8962 lines 1 and 4). A unit with neither a head nor a spouse is filed by its oldest member, so a claimable minor changes it. 3. Monotonicity, for the drawn couples in every year and the seeded population: marking another filer as claimable never raises the tax @@ -51,6 +50,7 @@ from hypothesis import strategies as st from policyengine_us import Simulation +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg from policyengine_us.tests.core.test_dependent_elsewhere_head_spouse_swap_invariance import ( CLAIM_PATTERNS, TOLERANCE, @@ -344,9 +344,14 @@ def _identities(sim, year): np.testing.assert_array_equal( size[unclaimed], _calc(sim, "tax_unit_size", year)[unclaimed] ) + # Form 8962 lines 1 and 4: the prior year's guideline for the coverage + # year's tax family size and state of residence (26 CFR 1.36B-1(h)). + state_group = sim.populations["tax_unit"].household("state_group_str", str(year)) + expected_fpg = fpg( + size, state_group, str(year - 1), sim.tax_benefit_system.parameters + ) np.testing.assert_allclose( - _calc(sim, "aca_fpg", year)[unclaimed], - _calc(sim, "tax_unit_fpg", year - 1)[unclaimed], + _calc(sim, "aca_fpg", year)[unclaimed], expected_fpg[unclaimed] ) diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml index ae33087d3b7..f40f6970056 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml @@ -191,8 +191,9 @@ aca_magi_fraction: [1.53, 1.91] - name: Income exactly on a whole percentage of the guideline is not floored one point low. - # 100 x 35,369 / 15,650 = 226 exactly, so Worksheet 2 gives 226. Dividing - # first gives 35,369 / 15,650 = 2.2599999..., which floors to 2.25. + # 100 x 36,621 / 15,650 = 234 exactly, so Worksheet 2 gives 234. Dividing + # first, in the 32-bit arithmetic aca_magi and aca_fpg are stored in, gives + # 36,621 / 15,650 x 100 just under 234, which floors to 2.33. period: 2026 input: people: @@ -201,13 +202,13 @@ tax_units: tax_unit: members: [person1] - aca_magi: 35_369 + aca_magi: 36_621 households: household: members: [person1] state_code: TX output: - aca_magi_fraction: 2.26 + aca_magi_fraction: 2.34 - name: Negative ACA MAGI gives a fraction of 0. # MAGI is floored at 0 before dividing (Form 8962 line 3 enters -0- for a diff --git a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml index dbfe0453619..e18b4d49c2d 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/eligibility/is_aca_ptc_eligible.yaml @@ -933,8 +933,9 @@ - name: 2024 Puerto Rico single adult pays no Marketplace premium and is not eligible # HHS defines no guideline for Puerto Rico, and no Exchange operates there - # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)), so pays_aca_premium is - # false. Without the no-Exchange test, the infinite fraction (the + # (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)), so the adult isn't a + # Marketplace enrollee and pays_aca_premium is false. Without the + # no-Exchange test, the infinite fraction (the # placeholder guideline is 0) is outside the coverage gap and # pays_aca_premium would be true. On main, with the state supplied for 2024 # only, the 2023 guideline year fell back to the default state's guideline @@ -957,6 +958,7 @@ members: [person1] state_code: PR output: + is_aca_marketplace_enrollee: false pays_aca_premium: false is_aca_ptc_eligible: false @@ -985,12 +987,14 @@ state_code: PR output: aca_required_contribution_percentage: 0 + is_aca_marketplace_enrollee: false pays_aca_premium: false is_aca_ptc_eligible: false - name: 2026 Guam single adult pays no Marketplace premium and is not eligible # Guam has no Exchange (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. 18024(d)). Without - # the Guam comparison in pays_aca_premium, the premium would be charged: + # the Guam comparison in is_aca_marketplace_enrollee, the premium would be + # charged: # the infinite fraction is outside the coverage gap. period: 2026 input: @@ -1010,13 +1014,14 @@ members: [person1] state_code: GU output: + is_aca_marketplace_enrollee: false pays_aca_premium: false is_aca_ptc_eligible: false - name: 2026 Virgin Islands single adult pays no Marketplace premium and is not eligible # The Virgin Islands have no Exchange (26 U.S.C. 36B(c)(2)(A); 42 U.S.C. - # 18024(d)). Without the Virgin Islands comparison in pays_aca_premium, the - # premium would be charged: the infinite fraction is outside the coverage + # 18024(d)). Without the Virgin Islands comparison in + # is_aca_marketplace_enrollee, the premium would be charged: the infinite fraction is outside the coverage # gap. period: 2026 input: @@ -1036,5 +1041,6 @@ members: [person1] state_code: VI output: + is_aca_marketplace_enrollee: false pays_aca_premium: false is_aca_ptc_eligible: false diff --git a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py index 86395c12add..5d965ba851b 100644 --- a/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py +++ b/policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py @@ -32,7 +32,7 @@ def formula(tax_unit, period, parameters): # them by state group. has_guideline = guideline > 0 # Multiply before dividing, so income exactly on a whole percentage - # (100 x 35,369 / 15,650 = 226) isn't floored one point low by a + # (100 x 36,621 / 15,650 = 234) isn't floored one point low by a # quotient that isn't exactly representable. percent = 100 * magi / where(has_guideline, guideline, 1) return where(has_guideline, np.floor(percent) / 100, np.inf) diff --git a/policyengine_us/variables/gov/aca/eligibility/is_aca_marketplace_enrollee.py b/policyengine_us/variables/gov/aca/eligibility/is_aca_marketplace_enrollee.py index a64057ecfcd..7ed58ff3932 100644 --- a/policyengine_us/variables/gov/aca/eligibility/is_aca_marketplace_enrollee.py +++ b/policyengine_us/variables/gov/aca/eligibility/is_aca_marketplace_enrollee.py @@ -20,7 +20,10 @@ class is_aca_marketplace_enrollee(Variable): "immigration-status, other-minimum-essential-coverage and Medicaid " "coverage-gap conditions of pays_aca_premium, without its premium " "condition, outside Puerto Rico, Guam and the Virgin Islands, which " - "have no Exchange. A child beyond the three oldest is enrolled but pays no " + "have no Exchange. A supplied pays_aca_premium of true still marks an " + "enrollee there, as it does for every other condition; the credit " + "stays 0, because the territories' infinite income fraction fails " + "the income test. A child beyond the three oldest is enrolled but pays no " "age-rated premium (45 CFR 147.102(c)(1)); pays_aca_premium is this " "variable restricted to those who pay one. So a supplied " "pays_aca_premium of true also marks an enrollee."