Repository navigation
Conversation
…t'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 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #10860 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 3 +2
Lines 37 59 +22
Branches 0 2 +2
=========================================
+ Hits 37 59 +22
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
PR Review: Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fractionPolicyEngine/policyengine-us #10860, head SummaryThe fix is right. Three things should change before merge:
Counts: 0 critical, 3 should address, 7 suggestions. Sources read
The regulation and the instructions agree on the lag, the state and the higher-guideline rule, so there is no source conflict to record under CONTRIBUTING.md#source-conflicts. CriticalNone. The PR reuses the existing Should addressS1. Territory households now reach the placeholder guideline: an error for coverage in 2015 to 2024, and an infinite fraction from 2025Where: Governing text:
Why it happens:
Household (hand trace): a single adult,
From 2025: the PR guideline is 0. The same household in 2026 gets 30,000 / 0 = inf, or NaN at $0 of MAGI. Coverage: CI doesn't exercise this path. The 2024 Puerto Rico tests compute Puerto Rico tax variables and never reach the ACA chain, and the territory shard passed. On Fix:
S2. The Form 8962 references point at the wrong pages, break the reference rules, and the new documentation depends on their orderWhere: Governing text:
What is wrong:
Concrete case: no household result changes. Opening Fix: collapse the tuple to bare URLs pinned to a dated edition, and stop counting entries in the documentation string. documentation = (
"ACA-related MAGI as a fraction of the federal poverty line for the "
"year before the coverage year, for the state of residence in the "
"coverage year, truncated to whole percentage points."
)
reference = (
# 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",
)The S3. The PR description overstates which households are unaffected and leaves out households that lose eligibilityWhere: the PR body. The last bullet of "What changes" says "Households in the contiguous states are unaffected, and so are households whose state is supplied for both years." The first bullet of "Effects" lists only gains: "a lower required contribution, a larger credit, and eligibility up to 400% of their own guideline." Governing text:
Household (a), a mover:
Household (b), pushed under the floor: 2026, Alaska, a single adult aged 30 with no Medicaid pathway. The inputs are
Also: the cost-sharing-reduction tiers ( Fix: rewrite the last bullet of "What changes" as "Households whose state is the same in both years, and households outside Alaska and Hawaii in both years, are unaffected." Add three items to "Effects": Alaska and Hawaii households between 100% of the contiguous guideline and 100% of their own lose eligibility from 2026; households whose state changed between the two years now use the coverage year's state; CSR tiers follow the fraction. Optionally, replace "I didn't measure the microsimulation effect" with the mechanism: input carry-over runs forward only, so a dataset household differs between SuggestionsG1. Record the mover and split-spouse simplification in the code, and cite Line 4 for the state rule
G2. Read family size for the coverage year
G3.
|
| Test (file:line at head) | MAGI | Guideline used | Arithmetic | Expected in PR | OK | Main gives |
|---|---|---|---|---|---|---|
aca_magi_fraction.yaml:44 Alaska, single |
30,000 | AK size 1: 19,550 | 1.53453 → 153 → 1.53 | 1.53 | ✓ | 30,000 / 15,650 = 1.91693 → 1.91 |
aca_magi_fraction.yaml:63 Hawaii, single |
30,000 | HI size 1: 17,990 | 1.66759 → 166 → 1.66 | 1.66 | ✓ | 1.91 |
aca_magi_fraction.yaml:82 Alaska, family of 3 |
50,000 | 19,550 + 2 × 6,880 = 33,310 (FR row 3: $33,310) | 1.50105 → 150 → 1.50 | 1.5 | ✓ | 26,650 → 1.87617 → 1.87 |
is_aca_ptc_eligible.yaml:516, aca_magi_fraction |
70,000 | AK size 1: 19,550 | 3.58056 → 358 → 3.58 | 3.58 | ✓ | 4.47284 → 4.47 |
is_aca_ptc_eligible.yaml:516, is_aca_ptc_eligible |
— | — | 3.58 is in [1.00, 4.00), which is true in 2026; coverage flags false, age 30, not separate | true | ✓ | 4.47 is in the 2026 false bracket → false |
aca_magi_fraction.yaml:5 (existing) Texas, 2016 |
30,000 | contiguous 2015: 11,770 | 2.54885 → 2.54 | 2.54 | ✓ unchanged | 2.54 |
aca_magi_fraction.yaml:24 (existing) Texas, 2015 guard |
30,000 | contiguous 2015: 11,770, lag 0 | 2.54885 → 2.54 | 2.54 | ✓ unchanged | 2.54 |
Every value of 100 × MAGI / guideline above is at least 0.05 away from the integer below it, so floating-point error can't flip a floor. The four new expectations fail on main and pass at the head.
Test coverage
Each row names a term at the head, a change to it, and the case that catches the change. All outcomes are hand traces. AMF is aca_magi_fraction.yaml and IAPE is is_aca_ptc_eligible.yaml.
Term (aca_magi_fraction.py) |
Mutation | Pinning case | Mutant gives |
|---|---|---|---|
:51 state group for period |
read for guideline_period |
AMF:44, AMF:63, AMF:82, IAPE:516 | 1.91, 1.91, 1.87, 4.47 (ineligible) |
:51 |
hard-code CONTIGUOUS_US |
the same four cases | the same values |
:52-53 year_lag |
force 0 | AMF:44, AMF:63, AMF:82, AMF:5, IAPE:516 | 1.50, 1.63, 1.46, 2.52, 3.50 |
:52-53 |
force 1 (ignore the guard) | AMF:24 | 2014's 10,890 → 2.75 |
:53 |
pass guideline_period and also lag |
AMF:5, AMF:44 | 2.75, 1.59 |
:53 |
pass guideline_period with no lag |
none: equivalent (G3) | — |
:50 size |
read for period |
none: equivalent for formula-computed size (G2) | — |
:50 |
hard-code 1 | AMF:82 | 2.55 |
helper additional_person[state_group] |
use the contiguous increment | AMF:82 | 30,550 → 1.63 |
:54 floor |
round instead | AMF:63, AMF:5 | 1.67, 2.55 |
:43-44 guard (unchanged) |
remove it | AMF:24 | 2.75 |
Every new term that changes a result is pinned. The cap in effect from 2026 is pinned through IAPE:516, and the 2025-to-2026 change in the Alaska and Hawaii guidelines pins the lag. Gaps that G4 would close: a mover, the 100% and 400% edges, a mixed-state simulation, and a year before 2026. The territory path is S1.
CI status
gh pr checks 10860, re-run at the end of this review: 22 pass, 6 pending, 0 fail (28 checks).
- Pass (22):
- Baseline: contrib-hhs, household, irs, reform, rest-a, rest-b, ssa-usda, states-shard-3
- Household API Partners
- PR validation (lock, guard tests, lint, changelog)
- Package and compatibility
- Python tests: Plan Rest Python shards; Full Suite Rest shards 2, 4, 5, 6, 7, 8 and 9
- Quick Feedback (selective tests + coverage)
- codecov/patch, codecov/project
- Pending (6):
- Baseline: states-shard-1, states-shard-2, states-shard-4
- Python tests: Full Suite - Microsimulation; Full Suite Rest shards 1 and 3
- Quick Feedback log:
gov/aca: 349 passedaca_magi_fraction.yaml: 5 passed, including the 3 new casesis_aca_ptc_eligible.yaml: 24 passed, including the new caseaca_magi_fraction.py: 100% coverage (20 statements, 2 branches)
- What the pending shards run: the states shards run the contiguous-state subsidy tests that read the fraction, and those shouldn't change. The microsimulation shard can move only for Alaska and Hawaii households in the dataset's own year.
Branch status
The head is 1 commit ahead of main (a93cc58d7e) and 0 behind. GitHub reports it as mergeable; the merge state is BLOCKED because required checks are pending and the review is outstanding.
Verdict: REQUEST_CHANGES
…rences - 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
PR Review (round 2): Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fractionPolicyEngine/policyengine-us #10860, head I ran no Python and no tests. Each claim rests on one of these, and I say which:
Where I say a changed version of the code would pass or fail a test, that is by my reading, not from a run. SummaryEvery round-1 item is closed:
The territory handling added this round brings four new should-address items:
Counts open: 0 critical, 4 should address, 3 suggestions. Prior findings
New findingsCriticalNone. The new code hard-codes no policy value, reinvents no variable and adds no untested formula. Every CI check that has finished passed (see CI). Should addressN1. Territory households are never in the coverage gap now, so one below the floor is treated as paying a Marketplace premium and, by my reading, reaches a lookup with no territory keyWhere:
What changes:
Where it leads (reading only):
Household (hand trace, reading only): 2026, a Puerto Rico citizen adult,
Above the floor, Caveat: I haven't traced every program that Why it matters for the PR's claims:
Fix:
N2. The
|
| Version | Fraction | Required contribution |
|---|---|---|
| Head | inf | 0 |
| Plain division | NaN | NaN |
0 in place of inf |
0 | the 2026 initial rate, 0.021 (required_contribution_percentage/initial.yaml, 2026, first bracket) |
N3. The np.isfinite check rests on a false premise, and three texts repeat it
Where:
is_aca_ptc_eligible.py:17-23: "Without this check the 2021-2025 rows of ptc_income_eligibility, which have no upper limit, would pass it."- The test comment at
is_aca_ptc_eligible.yaml:590: "2024 has no 400% limit, so the finite-fraction check is what makes the result false." - The PR body's Territories bullet: "because the 2021-2025 rows of
ptc_income_eligibilityhave no upper limit and would otherwise pass an infinite one."
The parameter: parameters/gov/aca/ptc_income_eligibility.yaml has three brackets:
| Threshold | Amount |
|---|---|
| 0 | false |
| 1.00 | true from 2014 |
| 4.00 | true for 2021-2025, false otherwise |
Its type is single_amount, so core builds a SingleAmountTaxScale (parameter_scale.py:133-140).
What core does with it: SingleAmountTaxScale.calc, the same in 3.34.2 (pinned) and 3.36.0 (installed by CI), reads:
guarded_thresholds = numpy.array([-numpy.inf] + self.thresholds + [numpy.inf])
bracket_indices = numpy.digitize(tax_base, guarded_thresholds, right=right)
...
guarded_amounts = numpy.concatenate([sentinel, amounts, sentinel])
return guarded_amounts[bracket_indices - 1]- For
tax_base = inf, the guarded thresholds are [-inf, 0, 1.00, 4.00, inf]. Withright=False,digitizereturns 5, since inf ≥ the last bin. guarded_amounts[4]is the trailing sentinel, afalseof the amounts' dtype.- So
calc(inf)is false in every year, 2021-2025 included. Only finite fractions see "no upper limit". This is what round 1 said ("numpy.digitize puts inf and NaN in the top sentinel bracket, which is false"). The premise of the added check contradicts it, and the core source bears round 1 out.
Consequences (reading):
- Deleting
& np.isfinite(magi_frac)changes no result. The fraction is never NaN at the head, and inf already maps to false, so it is an equivalent mutant. - The 2024 Puerto Rico case doesn't pin the check, although its comment says it does.
- The three contributed reforms (
aca_ptc_700_fpl_cliff,aca_ptc_additional_bracket,aca_ptc_simplified_bracket) overrideis_aca_ptc_eligiblewithout the check. They still deny territory households, because each of their scales ends in afalsebracket. Under the PR's stated premise they would need it too.
Fix:
- If N1's fix lands, drop the check, its comment, the test comment's second sentence, and the clause in the body.
- Otherwise, keep the check but reword all three. For example: "core's single-amount scale already maps an infinite fraction to its out-of-range cell, which is false; the check states the territory rule here rather than relying on that."
N4. The axiom: n/a line doesn't fit an eligibility change
Where: the PR body's last line, which says rulespec-us's pipeline takes the guideline as a caller-supplied fact "and treats the territories, which have no guideline, as ineligible".
Governing text:
- CONTRIBUTING.md, "Axiom parity": "Every policy change here must also be correct in rulespec-us. That covers a new program, a parameter or threshold update, an eligibility rule and a bug fix."
- The shared guide lists
n/afor "infrastructure, data, UI, microsimulation-only or emulator-mapping changes".
Why it doesn't fit:
- This PR is a bug fix that changes eligibility for Alaska and Hawaii households and for movers, and from this round for every territory household. None of the
n/acategories applies. - The stated reason is accurate as far as it goes. rulespec-us's
us/policies/aca/ptc_pipeline.yaml"accepts that exact annual amount as a trusted upstream fact for the household's family size and jurisdiction", and it defers "Exchange/QHP enrollment" under 36B(c)(2). - But that is a parity gap, not an exemption. The jurisdiction rule (1.36B-1(h) and Line 4) and the territory exclusion are encoded nowhere in rulespec-us.
- The pipeline also requires a positive guideline, so a territory household can't be represented there at all.
- The family-size change overlaps rulespec-us#1627 (OPEN,
pe-parity, updated 2026-10-10). It queues the 36B(d)(1) family size "for the taxable year" and keeps the caller-supplied poverty line.
Fix:
- Open a dispatch-ready
pe-parityissue covering:- the applicable poverty guideline: 1.36B-1(h) and Form 8962 Line 4, meaning residence in the taxable year, the HHS tables for the 48 states and DC, Alaska and Hawaii, and the higher table for movers and split-state spouses;
- territory residents: 36B(c)(2)(A) read with 42 U.S.C. 18024(d).
- Give it companion tests from the Form 8962 (2025) tables. For example, an Alaska single filer with $30,000 against $18,810 gives 159 on Worksheet 2.
- Record the blocker the shared guide requires.
- Then write
axiom: rulespec-us#<n> queued, and mention Add 2023 poverty line #1627 for family size. - If maintainers decide that a caller-supplied-input pipeline makes such changes
n/a, record that decision in the PR instead.
Suggestions
G8. Cite 1.36B-1(d), not (d)(1). The comment at aca_magi_fraction.py:34-35 cites "26 CFR 1.36B-1(d)(1)". Paragraph (d)(2) ("For taxable years to which section 151(d)(5) applies, a taxpayer's family means the taxpayer, ... for the taxable year") governs taxable years ending on or after 2020-12-31 (paragraph (o)(2)). Both say "for the taxable year", so no result changes. My round-1 G2 introduced the "(d)(1)".
G9. Three wording points in the PR body:
-
"Households whose state is the same in both years, and households outside Alaska, Hawaii and the territories in both years, are unaffected" has two exceptions:
- territory households whose state is supplied for both years, with coverage before 2025, which go from an error to a result;
- households whose
tax_unit_sizeinput differs between the two years.
Adjacent bullets describe both, so prefixing it with "Otherwise," would fix it.
-
The Tests bullet says the 2024 Puerto Rico case "raised an error before". That is relative to this PR's first commit. On
mainthe case got the contiguous 2023 guideline: 30,000 / 14,580 = 2.0576, floored to 2.05, so it was eligible. -
"That differs only when
tax_unit_sizeis supplied for the coverage year alone" also misses inputs that differ between the two years.
The Territories bullet's "Other readers of the placeholder get 0" could add that the readers that divide by it give inf, or NaN at $0, as they already do from 2024 (listed under Verified).
G10. Cite 42 U.S.C. 18024(d) with 36B(c)(2)(A). The comments and body rest "no Exchange" on 36B(c)(2)(A), which requires a plan "enrolled in through an Exchange established by the State under section 1311". ACA §1304(d), 42 U.S.C. 18024(d), closes the loop: "the term 'State' means each of the 50 States and the District of Columbia" (Cornell LII, read today). A reference to it makes the territory rule self-contained.
Verified
Hand-checked new expectations. Each row gives:
- the head's computation: the coverage-year state and size, the guideline at January 1 of the prior year, then floor(100 × MAGI / guideline) / 100;
- what the case pins;
- what
maingives for the same inputs. The state is supplied as in the test, so onmainthe guideline year falls back to the contiguous group.
| Case (file:line at head) | MAGI | Guideline used | Arithmetic | Expected | OK | Pins | main gives |
|---|---|---|---|---|---|---|---|
aca_magi_fraction.yaml:104 mover, AK 2025 → TX 2026 |
30,000 | TX (contiguous), size 1: 15,650 | 1.91693 → 191 → 1.91 | 1.91 | ✓ | coverage-year state; the 2025 state gives AK 19,550 → 1.53 | 1.53 |
aca_magi_fraction.yaml:126 size 3 for 2026 only, AK |
50,000 | 19,550 + 2 × 6,880 = 33,310 | 1.50105 → 1.50 | 1.5 | ✓ | coverage-year size; the 2025 size of 1 gives 2.55 | contiguous, size 1: 3.19489 → 3.19 |
aca_magi_fraction.yaml:149 2025, AK |
30,000 | AK at 2024-01-01: 18,810 (Form 8962 Table 1-2) | 1.59490 → 1.59 | 1.59 | ✓ | the lag before 2026 (no lag gives 19,550 → 1.53) and the coverage-year state | contiguous 15,060: 1.99203 → 1.99 |
aca_magi_fraction.yaml:167 AK and TX in one simulation |
30,000 each | 19,550 and 15,650 | 1.53453 / 1.91693 | [1.53, 1.91] | ✓ | per-household state indexing; one state for all gives [1.53, 1.53] | [1.91, 1.91] |
is_aca_ptc_eligible.yaml:541 AK, 2026 |
19,549 | 19,550 | 0.999949 → 99 → 0.99; [0, 1.00) is false; the exception is off from 2026 | 0.99, false | ✓ | the Alaska floor, and truncation (rounding gives 1.00, which is eligible) | 1.24914 → 1.24, true |
is_aca_ptc_eligible.yaml:566 AK, 2026 |
78,199 | 19,550 | 3.999949 → 399 → 3.99; [1.00, 4.00) is true | 3.99, true | ✓ | the Alaska 400% edge, and truncation (rounding gives 4.00, false in 2026) | 4.99674 → 4.99, false |
is_aca_ptc_eligible.yaml:590 PR, 2024 |
30,000 | PR at 2023-01-01: 0 (the new 1992 entry) | the guideline is 0 → inf; calc(inf) is false (N3); the exception: inf < 1 is false |
false | ✓ | the 1992 backdate (without it: ParameterNotFoundError); not the guard (N2), not isfinite (N3) |
contiguous 14,580: 2.05, true |
In every row, 100 × MAGI / guideline is at least 0.10 above the integer below it, so floating-point error can't move a floor. The 99.9949 and 399.9949 cases sit 0.005 under the next integer, far beyond float error. Quick Feedback ran all of them green (CI).
Mutation check at the head (reading).
| Term | Change | Caught by | Changed code gives |
|---|---|---|---|
aca_magi_fraction.py:42 size for period |
read for guideline_period |
AMF:126 | 2.55 |
:43 state for period |
read for guideline_period |
AMF:104, AMF:44/63/82, IAPE:516/541/566 | 1.53; 1.91 ... |
:44 guideline_period |
period (no lag) |
AMF:149, AMF:44 | 1.53; 30,000 / 19,950 = 1.50 |
:49-51 guard |
plain division | none (N2) | the same at $30,000; NaN at $0 |
:51 np.inf |
0 |
none (N2) | the 2024 PR case is in the gap → false anyway |
:51 np.inf |
a large finite value, such as 99 | IAPE:590 | true (2024's 4.00 bracket is true) |
is_aca_ptc_eligible.py:21-23 & np.isfinite |
delete | none: equivalent (N3) | the same |
fpg.yaml 1992 entries |
delete | IAPE:590 | ParameterNotFoundError |
fpg.yaml 2024 entries |
delete | every test (the tree fails to load) | — |
Readers of aca_magi_fraction with an infinite fraction (grep at the head, each read).
| Reader | What inf does |
|---|---|
is_aca_ptc_eligible |
false |
aca_ptc_below_fpl_immigration_exception |
inf < 1 is false, so no exception |
pays_aca_premium |
outside the gap (N1) |
aca_required_contribution_percentage |
last bracket, capped_out, returns 0; no NaN, since the 2015, 2021 and 2026 thresholds are finite, ending at 4.00 |
marketplace_csr_eligible |
inf <= maximum is false |
marketplace_csr_category |
NONE |
basic_health_program |
calc(inf) is false → 0, and it is defined_for enrollment |
| The three contributed reforms | their scales end in false, and np.interp gives the last amount |
- None of these pays a territory household. None produces NaN at the head, because the guard prevents 0 / 0.
- The state readers (CA, CO, CT, MA, MD, NJ, NM, VT, WA, 17 variables) are all
defined_fortheir state, so a territory household never computes them. - The infinite value itself is an output of
aca_magi_fraction.medicaid_income_level, the SNAP ratios andschool_meal_fpg_ratioalready return the same kind of value for territory households from 2024 onmain.
Readers of the gov.hhs.fpg placeholder before 2024:
- 33 files read
gov.hhs.fpgdirectly, and 87 import the helper. Before this PR, for a territory household before 2024, every one raisedParameterNotFoundError. Now they get 0. - These divide by it, and so give inf, or NaN with no income:
medicaid_income_level,tax_unit_medicaid_income_level,snap_gross_income_fpg_ratio,snap_gross_test_income_fpg_ratio,snap_net_income_fpg_ratio,school_meal_fpg_ratio,fcc_fpg_ratioandpell_grant_eligibility_type. - Their comparisons against limits (
<=) are false for both, so the household is income-ineligible.medicaid_income_level.yaml:378already pins that for PR in 2026. - This is exactly what those readers already return for territory households from 2024, where the placeholder was already 0. So the PR turns a pre-2024 error into the established 2024+ behavior, not into anything new. Pre-2024 territory households couldn't be computed at all before, so this matters little.
Other checks:
-
Uprating: with the 2024 entry last, uprating multiplies 0 by the index ratio from 2024, so the placeholders stay 0 in every year (reading core
uprate_parameter). -
Code shape:
- The guard keeps the denominator nonzero, so
aca_magi_fractionraises no numpy warning. whereandnp.isfiniteare vectorized.- No policy value is hard-coded.
- The documentation (
:10-14) is accurate.
- The guard keeps the denominator nonzero, so
-
References: see S2. The
1.36B-1#hanchor resolves, and paragraph (h) supports the lag and the higher-guideline rule (eCFR text). -
Changelog:
changelog.d/aca-fpl-coverage-year-state.fixed.mdis top-level, of typefixed. Its wording is accurate except for the "no longer raise an error" scope (N1). -
PR-body figures, by hand:
Claim Arithmetic OK The 400% cutoff 4 × 15,650 = 62,600 and 4 × 19,550 = 78,200 ✓ Alaska at $30,000 30,000 / 19,550 = 1.53453 → 1.53, against 30,000 / 15,650 = 1.91693 → 1.91 ✓ Alaska at $18,000 18,000 / 15,650 = 1.15016 → 1.15, against 18,000 / 19,550 = 0.92072 → 0.92; below the floor from 2026, since below_fpl_immigration_exception_in_effectis false from 2026-01-01✓ Mover 1.53 → 1.91 ✓ I carried the ACA-Calc figures ($9,947 against $9,177) over without re-deriving them; they need a simulation.
-
Partner tests: no partner file has a GU, PR or VI household (grep), and the PR edits none.
-
Guards:
- None of the new comments matches
CONFLICT_COMMENT_REintest_source_conflicts.py. "Form 8962 Line 4 uses" doesn't fitform\s+(uses|...), because "8962 Line 4" intervenes. - None of the changed files is in
KNOWN_UNMARKED. - The PR adds no income-source list or program entry, so Integrate modeled state unemployment insurance into income flows #9558, Count military SBP annuities in state benefit income lists #10317, Count each Social Security benefit once in Pennsylvania CCW countable income #10408 and the programs registry don't apply.
- None of the new comments matches
Axiom
The line at the head is axiom: n/a: .... It is not valid under either CONTRIBUTING file for a PR that changes eligibility (N4). Round 1 didn't assess the axiom line, and it should have. This round's territory clause adds a 36B(c)(2) eligibility outcome, which makes n/a harder to defend, not easier. The relevant rulespec-us items are:
ptc_pipeline.yaml, which takes the guideline as a caller-supplied fact and defers Exchange enrollment;- rulespec-us#1627 (OPEN,
pe-parity), for family size.
No queued issue covers the jurisdiction rule or the territory exclusion.
CI
Run 43444234312 at 391384776f, checked at the time of posting: 38 checks, 29 passed, 9 still running (the 8 Contrib shards and Microsimulation), none failed.
- Quick Feedback ran the two changed test files:
aca_magi_fraction.yaml9 passed,is_aca_ptc_eligible.yaml27 passed. - All 11 Baseline shards, the 11 Python shards, Household API Partners, and Package and compatibility passed.
- The run at the first fix commit (
fa21a8006c, run 43441186447) failed all 27 jobs when it loaded the parameter tree: "Failed to uprate using gov.hhs.fpg_uprating at 1992-01-01 for gov.hhs.fpg.additional_person.GU".391384776ffixed that.
Branch
- Position: the head is 3 commits ahead of
mainand 2 behind (47c4e8720b). The twomaincommits are Deduct a California couple's dependents' premiums whatever their marital unit #10855, a California Medi-Cal countable-income change, and its version bump. Neither touches ACA, FPG or the guard tests. - Merge:
git merge-tree --write-tree PolicyEngine/main 391384776fmerges cleanly (tree540d3fac02), and the merged-tree guard results are the same as at the head. - GitHub: reports
MERGEABLE, merge stateBLOCKED(review required). - Linked PR: Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041 (
aca_fpg) is still open, so the "whichever lands second" note in the body still holds.
Verdict: REQUEST_CHANGES
…ine 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 <noreply@anthropic.com>
|
Round-2 items, fixed at
Quick Feedback passed at this head: |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Review: Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fraction (#10860)
Verdict: REQUEST_CHANGES
The legal reading is right and the main change does what it says. I reproduced every number in the description. One line introduces a rounding regression that affects all states, the description of the fpg.yaml change is not what the code does, and two repository rules are open.
Which commit. I reviewed 391384776f. You pushed a9cc99bc13 at 11:10am EST on 2026-11-13, during the review. I applied that commit and re-ran the two changed test files, gov/aca, contrib/aca, the partner folder and my household set on it. Each item below says where it stands at both commits. Line numbers are for a9cc99bc13 unless I say otherwise.
Counts open at a9cc99bc13: 1 critical, 3 should address, 5 suggestions.
Critical
C1. The new truncation divides before it multiplies, so a household exactly on a whole percentage loses one point (all states)
policyengine_us/variables/gov/aca/eligibility/aca_magi_fraction.py:52-53 (:50-51 at 391384776f):
fraction = magi / where(has_guideline, guideline, 1)
return where(has_guideline, np.floor(100 * fraction) / 100, np.inf)main computes np.floor(100 * magi / fpg) / 100: it multiplies first. 100 * 35_369 / 15_650 is exactly 226. 35_369 / 15_650 is not representable, 100 * that is 225.99999..., and the floor gives 225. Worksheet 2 gives 226.
| Household | Worksheet 2 | main |
This PR |
|---|---|---|---|
| Texas, single, 2026, MAGI $35,369 (226% of $15,650) | 226 | 2.26; contribution 7.557%; aca_ptc $5,165.69 |
2.25; 7.520%; $5,178.71 |
| Texas, single, 2025, MAGI $30,873 (205% of $15,060) | 205 | 2.05; aca_ptc $5,515.38 |
2.04; $5,527.73 |
| Hawaii, family of eight, 2026, MAGI $125,223 (201% of $62,300), state supplied for 2025 and 2026 | 201 | 2.01; CSR 73% AV; aca_ptc $9,848.49 |
2.00; CSR 87% AV; $9,894.58 |
| Alaska, single, 2026, MAGI $44,183 (226% of $19,550), state supplied for 2026 | 226 | 2.82 (the bug this PR fixes) | 2.25 |
The first three households are ones the description calls unaffected.
Extent, from a grid I ran on both trees:
- Texas single filer, 2026, every whole-dollar MAGI that is an exact whole percentage from 95% to 405% (155 cases):
mainis right in all 155. This PR is wrong in 7 (114, 116, 226, 228, 230, 232 and 402%). - Family sizes 1 to 8, half-dollar MAGI included (2,488 exact-percentage cases):
mainwrong in 0, this PR wrong in 128. - One dollar either side of each boundary: no difference.
- Microdata: no difference. In
enhanced_cps_2024, no tax unit outside Alaska and Hawaii with a fraction between 0 and 8 gets a different value in 2024, 2025 or 2026. The regression needs MAGI exactly on a boundary, which household inputs and tests have and imputed incomes do not.
No test pins the order: with the fix below, all 37 cases in the two changed files still pass.
Fix (I ran it; it restores all four rows above and the grid):
percent = 100 * magi / where(has_guideline, guideline, 1)
return where(has_guideline, np.floor(percent) / 100, np.inf)Add a case to aca_magi_fraction.yaml: Texas, single, 2026, aca_magi: 35_369, expected 2.26.
Should address
S1. The fpg.yaml change does not do what the changelog, the description and a test comment say
Where:
policyengine_us/parameters/gov/hhs/fpg.yaml:51-62and:112-120(the1992-01-01: 0entries).changelog.d/aca-fpl-coverage-year-state.fixed.md: "their income fraction no longer raises an error for coverage before 2025".is_aca_ptc_eligible.yaml:595-596: "The 2023 guideline lookup raised ParameterNotFoundError before the placeholders were backdated to 1992."- The description: "reading it for an earlier guideline year raised
ParameterNotFoundError. That was already true onmainwhen a territory's state was supplied for both years", and the Tests bullet "which raised an error before".
What I ran:
mainraises no error here. Puerto Rico, single adult,state_code: PRfor 2023 and 2024, MAGI $30,000, 2024 coverage:maingivesaca_magi_fraction = infandis_aca_ptc_eligible = false. The same holds for 2015 coverage.system.pybackdates each parameter's first value to 2015-01-01 (#9628), so the 2024 placeholder already answers every date from 2015.- The 2024 Puerto Rico case with the state supplied for 2024 only gives 2.05 and eligible on
main(the 2023 contiguous guideline), not an error. - The entries are not needed by any test. With
main'sfpg.yaml, all 36 cases at391384776fand all 37 ata9cc99bc13pass. - Parameter values. I compared both parts for the six state groups for every year from 1992 to 2035. The only differences are GU, PR and VI for 1992 to 2014 (an error on
main, 0 here). The contiguous, Alaska and Hawaii values are identical in every year, including the uprated 2027 to 2035 values. The territory values stay 0 after 2026. - What the change really does: 2015 only, outside the ACA. SNAP reads the guideline for the fiscal year, dated 2014-10-01, which is before the backdated start. I compared all 7,644 variables for territory households on both trees:
- Guam, adult and child, $0, 2015: 80 variables raise on
main, 14 here. The 66 that now compute includesnap$6,312,household_net_income$6,312,is_snap_eligible,is_lifeline_eligible,is_acp_eligible,is_ebb_eligible(all true) andis_head_start_eligiblefor the child. - Virgin Islands, adult and child, $12,000, 2015: 80 raise on
main, 14 here;snap$3,120,household_net_income$18,602. - Puerto Rico, 2015: 96 raise on
main, 88 here (the rest fail onmainfor an unrelated reason). - 2016, 2023, 2025 and 2026 (five territory households): no variable outside the ACA variables changes, no error appears or goes away, and no new infinite or NaN value appears outside
aca_magi_fraction. The infinite ratios that the 0 placeholder produces (fcc_fpg_ratio,school_meal_fpg_ratio,medicaid_income_level,tax_unit_medicaid_income_level, the SNAP gross-income ratios) are the same onmain.
- Guam, adult and child, $0, 2015: 80 variables raise on
So the edit extends the placeholder's existing behaviour to 2015 for about 66 non-ACA variables. The PR does not describe or test that, and it is not needed for the premium tax credit.
Fix: take the fpg.yaml edit out of this PR, or keep it and describe and test the 2015 effect. In both cases correct the changelog sentence, the test comment and the two statements in the description.
S2. The axiom: n/a line is not valid for this change
CONTRIBUTING.md, "Axiom parity": "Every policy change here must also be correct in rulespec-us. That covers a new program, a parameter or threshold update, an eligibility rule and a bug fix." The shared guide allows n/a for "infrastructure, data, UI, microsimulation-only or emulator-mapping changes".
This PR is a bug fix that changes eligibility: the Alaska single filer at $70,000 gains the credit, the one at $18,000 loses it, and territory households lose it. That is none of the n/a categories.
Your description of rulespec-us is accurate. us/policies/aca/ptc_pipeline.yaml takes the poverty line as a caller-supplied fact, its 11 companion cases use only contiguous amounts (15,650, 21,150, 32,150), and nothing there encodes 26 CFR 1.36B-1(h), Form 8962 Line 4 or the territory rule. That is a gap to queue, not an exemption. rulespec-us#1627 (open, pe-parity) covers the 36B(d)(1) family size only.
Fix: open a dispatch-ready rulespec-us issue with the pe-parity label for the applicable poverty guideline (1.36B-1(h) and Line 4: residence in the taxable year, the three HHS tables, the higher table for movers and split-state spouses) and the territory exclusion. Take the companion values from the Form 8962 tables (Alaska, one person, $18,810 for 2025). Record the encoder blocker in it. Then write axiom: rulespec-us#<n> queued and mention #1627 for family size.
S3. An older open PR fixes the same issue with the same change
#10413 ("Use current ACA household with prior-year poverty guidelines", opened 2026-10-23, 3:02pm EDT) also says "Fixes #9482". It makes the same edit to aca_magi_fraction.py (coverage-year tax_unit_size and state_group_str, the fpg helper with a one-year lag) and adds eight cases to the same YAML file. The description here names #10041 but not #10413.
Two more open PRs touch the same code and are not named:
- #10312 (draft) is the "income exactly at 400%" item that the description leaves for a follow-up.
- #10846 (draft) adds the 2013 and 2014 guidelines to
fpg.yaml.
Fix: say in the description how this PR relates to #10413 (which one lands, and what the other one keeps), and link #10312 from the follow-up item.
Closed by a9cc99bc13 (open at 391384776f)
I checked these against your two self-review comments by running the code.
| Item | At 391384776f |
At a9cc99bc13 |
|---|---|---|
pays_aca_premium for a territory household under the floor |
Real. Puerto Rico, single, $0, 2026: main false, head true. Four variables changed with it (pays_aca_premium, is_aca_family_tier_dependent_child, selected_marketplace_plan_actuarial_value and marketplace_effective_actuarial_value 0 to 0.7). |
Fixed: false. Removing has_exchange fails 2 cases. |
The predicted ParameterNotFoundError from the SLCSP lookup |
Not real. slcsp, used_aca_ptc and selected_marketplace_plan_premium_proxy are 0 on both trees. household_net_income is the same on both trees for Guam and the Virgin Islands. For Puerto Rico it raises a ValueError on main too. |
Same. |
has_guideline guard not pinned |
Real. Plain division, and 0 in place of inf, both passed all 36 cases. |
Fixed. Each now fails the new 2026 Puerto Rico case. |
np.isfinite in is_aca_ptc_eligible |
Real: removing it passed all 36 cases, so the comment's premise was wrong. | Removed. |
| Cite 1.36B-1(d) and 42 U.S.C. 18024(d) | Open. | Done. |
| Axiom line | Open. | Open (S2). |
| Wording points in the description | Open. | Open. The description is unchanged, so it still says is_aca_ptc_eligible requires a finite fraction. |
Suggestions
- Territory test by state, not by an infinite fraction.
pays_aca_premium.py:80readsnp.isfinite(magi_frac)as "has an Exchange". A direct test on the state group says the rule where it is, and it survives a refactor of the fraction (see the #10041 note below). - The $18,000 example needs its inputs stated. With earnings in place of a MAGI input, an Alaska single adult at $18,000 is Medicaid-eligible, and
is_aca_ptc_eligibleis false onmainand here. Alaska and Hawaii are both expansion states, so the households that lose the credit under the floor are those barred from Medicaid for another reason. - The first-modeled-year guard after #10846. This PR no longer computes a variable for 2014; it reads only the parameter. When #10846 adds the 2014 guideline, the guard can go and 2015 coverage can use the guideline that 36B(d)(3)(B) calls for.
- The ACA calculator figures. For a single 40-year-old in Anchorage Municipality earning $40,000 in 2027 I get $11,969.37 on
mainand $12,739.84 here. The $770 change matches yours; the levels ($9,177 and $9,947) do not. - Update the description for
a9cc99bc13: the Territories bullet, the Tests list (37 cases, the new 2026 Puerto Rico case) and "I didn't run these locally".
Interaction with #10041
At its head (18385c186b), #10041's aca_fpg reads state_group_str for the guideline year and its aca_magi_fraction is np.floor(100 * magi / aca_fpg) / 100.
- If this PR merges first, #10041 must: read the state group for
periodinaca_fpg; keep the zero-guideline handling (its plain division gives NaN for a territory household with $0, andpays_aca_premiumhere depends on an infinite value); keep these test cases; and resolve the conflicts inaca_magi_fraction.py,pays_aca_premium.py,is_aca_ptc_eligible.pyand the two YAML files. - If #10041 merges first, this PR must move the coverage-year state into
aca_fpg, keep only the zero-guideline handling and the truncation inaca_magi_fraction, and drop the directfpgimport. Its multiply-first order then stays, which also settles C1.
What I checked
Law.
- 26 U.S.C. 36B(d)(3)(B): "the most recently published poverty line as of the 1st day of the regular enrollment period". Prior-year guideline: confirmed.
- 26 CFR 1.36B-1(h): the guideline in effect at the open enrollment before the taxable year, and the higher guideline for a mid-year move or spouses in separate states. Confirmed. The
#hanchor exists on the cited page. - 26 CFR 1.36B-1(d)(1) and (d)(2): family "for the taxable year". Confirmed. Paragraph (o)(2) applies (d)(2) from 2020.
- Form 8962 instructions (2025), file page 8: "Check the box to indicate your state of residence in 2025 ... (For 2025, the 2024 federal poverty lines are used ...) If you moved during 2025 and you lived in Alaska and/or Hawaii ... use the table with the higher dollar amounts". Page 9 has Tables 1-2 and 1-3 and Worksheet 2. The cited URL and page show the rule.
- 36B(c)(2)(A) and 42 U.S.C. 18024(d) ("each of the 50 States and the District of Columbia"): confirmed.
- One model limit, which the code comment states: a move between years uses the coverage year's state, and a move within the year cannot be represented.
Households (main then 391384776f; a9cc99bc13 is the same except pays_aca_premium in the territories). Coverage inputs are false where eligibility is shown.
| Case | main |
PR |
|---|---|---|
| Alaska single, $30,000, 2026 | 1.91; aca_ptc $10,799.92 |
1.53; $11,349.40 |
| Hawaii single, $30,000, 2026 | 1.91 | 1.66 |
| Alaska family of three, $50,000, 2026 | 1.87 | 1.50 |
| Alaska 2025, Texas 2026, $30,000 | 1.53 | 1.91 |
| Alaska, 2025 coverage, $30,000 | 1.99 | 1.59 |
| Alaska single, $18,000, 2026 | 1.15; eligible | 0.92; not eligible |
| Alaska single, $70,000, 2026 | 4.47; not eligible | 3.58; eligible; aca_ptc $5,677.78 |
| Alaska single, $78,199, 2026 | 4.99; not eligible | 3.99; eligible |
| Alaska single, $78,200, 2026 | 4.99; not eligible | 4.00; not eligible |
| Texas single, $62,600, 2026 (exactly 400%) | 4.00; not eligible | 4.00; not eligible |
| Puerto Rico single, $30,000, 2024 | 2.05; eligible | inf; not eligible |
| Alaska and Texas, 2015 coverage, $30,000 | 2.03 and 2.54 | 2.03 and 2.54 |
| Alaska supplied for both years | 1.53 | 1.53 |
| Alaska supplied for 2025 only | 1.53 | 1.91 (2026 is the default state) |
| Texas 2025, Alaska 2026 | 1.91 | 1.53 |
| Alaska 2025, Hawaii 2026 | 1.53 | 1.66 |
Alaska, tax_unit_size: 3 for 2026 only, $50,000 |
3.19 | 1.50 |
Alaska, tax_unit_size: 3 for 2025 only, $50,000 |
1.87 | 2.55 |
| Alaska, Hawaii, Texas, Puerto Rico and Guam in one simulation | 1.91 each | 1.53, 1.66, 1.91, inf, inf |
The issue's axis (Alaska, earnings $61,000 to $81,000), premium_tax_credit |
$5,059, 0, 0, 0, 0 | $5,059, $4,561, $4,063, $3,565, 0 |
| Alaska single, earnings $40,000, 2026 | 2.61; no CSR; aca_ptc $9,052.28 |
2.09; CSR 73% AV; $9,807.99 |
| Hawaii single, earnings $40,000, 2026 | 2.55; no CSR; $3,058.49 | 2.22; CSR 73% AV; $3,531.45 |
The 400% rows confirm that the cliff at exactly 400% is the same on main.
All 7,644 variables, main against the PR:
- Texas and California households (adult and child, $30,000, 2026): no variable differs. The state premium programs that read the fraction (California, Colorado, Connecticut, Maryland, Massachusetts, New Jersey, New Mexico, Vermont, Washington) are all in contiguous states.
- Alaska, adult and child, $30,000: 2 variables (the fraction and the required contribution).
- Hawaii, adult and child, $45,000: 27 variables, all in the credit's chain;
household_net_income$46,368.51 to $46,892.58; CSR 73% to 87% AV.
Microsimulation (enhanced_cps_2024, 55,264 tax units, at 391384776f):
| Year | Scope | aca_ptc, main |
PR | Tax units that differ |
|---|---|---|---|---|
| 2026 | All | $77,177.09m | $77,177.09m | 0 |
| 2026 | Alaska (156 rows) | $59.57m | $59.57m | 0 |
| 2026 | Hawaii (278 rows) | $11.08m | $11.08m | 0 |
| 2025 | All | $80,228.63m | $80,228.63m | 0 |
| 2024 | All | $62,540.51m | $62,543.44m | 18 |
| 2024 | Alaska | $183.82m | $186.53m | 8 |
| 2024 | Hawaii | $9.86m | $10.08m | 10 |
Weighted is_aca_ptc_eligible people are the same on both trees in each year (2026: 17,758,072). This confirms your mechanism: the dataset's state carries forward, so only the dataset's own year (2024) changes, by $2.93m. The dataset has no territory rows.
Tests (Python 3.13.3, policyengine-core 3.34.10):
| Suite | 391384776f |
Merged into main (47c4e8720b) |
a9cc99bc13 |
|---|---|---|---|
| The two changed YAML files | 36 passed | included below | 37 passed |
baseline/gov/aca |
356 passed | 356 passed | 424 passed, run together with contrib/aca |
contrib/aca |
67 passed | 67 passed | (in the 424) |
baseline/gov/hhs |
2,707 passed | 2,709 passed | not run |
baseline/partners |
623 passed | 623 passed | 623 passed |
No failures. The merge into main is clean. The PR edits no partner file.
Does a test pin each changed line? I changed one line at a time and ran the two files.
| Change | Result |
|---|---|
| Family size read for the guideline year | 1 fails: pinned |
| State read for the guideline year | 11 fail: pinned |
| Guideline not lagged | 11 fail: pinned |
| First-modeled-year guard removed | 1 fails: pinned |
| Contiguous guideline for everyone | 10 fail: pinned |
has_exchange removed (a9cc99bc13) |
2 fail: pinned |
Plain division; 0 in place of inf (a9cc99bc13) |
1 fails each: pinned |
| Multiply before dividing | all pass: not pinned (C1) |
fpg.yaml entries removed |
all pass: not pinned (S1) |
Repository rules. The changelog fragment is a top-level file, named for the branch, type fixed. The axiom line is S2.
CI. All 38 checks passed at 391384776f. At a9cc99bc13, at 11:37am EST: 17 passed, 9 running, none failed.
Not verified
- CI runs on Python 3.14 and may install a different policyengine-core. I ran Python 3.13.3 with policyengine-core 3.34.10.
- At
a9cc99bc13I did not runbaseline/gov/hhs, the merge intomain, the microsimulation or the all-variable comparison. Its code change touches onlypays_aca_premiumandis_aca_ptc_eligible. - The microsimulation is one dataset and three years. It has no territory households, so it says nothing about them.
- The rest of the suite (states, IRS, household, the other contrib reforms). CI covers it.
- The ACA calculator's dollar levels (suggestion 4).
- I read 26 CFR 1.36B-1 on Cornell LII, not on eCFR.
- I ran nothing in rulespec-us. I read its pipeline file, its test file and its open issues.
- I checked the all-variable comparison on twelve households with one adult and one child. Other family shapes in the territories are not covered.
…t 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
|
@PavelMakarchuk, thanks for running all of this. C1, truncation order: fixed. S1,
S2,
S3, related PRs: a new "Related PRs" section names them.
Suggestions:
A correction to my earlier comment: it said As before, I didn't run these locally, so CI is the check. |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Re-review: Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fraction (#10860)
Verdict: REQUEST_CHANGES
I reviewed 1e306d09cc (the two commits pushed at 1:40pm and 1:41pm EST on 2026-11-13). The critical item and the three should-address items from my last review are all resolved, and I confirmed each one by running the code. One small item is new: the last commit changed the territory test from one mechanism to three separate state comparisons, and two of the three have no test. Two cases close it.
Counts open at 1e306d09cc: 0 critical, 1 should address, 4 suggestions.
Resolved since the last review
| Item | Status | How I checked |
|---|---|---|
| C1 Truncation divided before it multiplied | Resolved | aca_magi_fraction.py:56-57 computes 100 * magi / guideline and then floors. All four households below match main or Worksheet 2. The grids match main in every case. The new Texas case at aca_magi_fraction.yaml:193 pins the order: with the line changed back to divide first, that case fails (1 failed, 38 passed). |
S1 fpg.yaml edit and the "raised ParameterNotFoundError" statements |
Resolved | fpg.yaml is identical to main. The PR now changes five files. The changelog, the test comment at is_aca_ptc_eligible.yaml:590-597 and the description no longer state an error, and what they state about main (2.05 and eligible with the state supplied for one year; an infinite fraction and not eligible with the state supplied for both) is what I get. |
S2 axiom: n/a |
Resolved | The line is axiom: TheAxiomFoundation/rulespec-us#2210 queued. The issue is open, has the pe-parity label, and covers the guideline by state of residence (1.36B-1(h), Form 8962 Line 4, the three tables, movers and split-state spouses), the Worksheet 2 truncation and the territory exclusion (36B(c)(2)(A), 42 U.S.C. 18024(d)), with 13 companion cases and the encoder blocker recorded. rulespec-us#1627 (open, pe-parity) is named for family size. |
| S3 #10413, #10312, #10846 not named | Resolved | The new "Related PRs" section names all three. It states that #10413 makes the same change, that only one of the two should land, and that this PR's cases cover #10413's. The negative-income case from #10413 is added (aca_magi_fraction.yaml:212). All three PRs are still open. |
| Suggestion 1: test the territory by state | Done, with one gap | pays_aca_premium.py:79-85. See S1 below. |
| Suggestion 2: state the inputs of the $18,000 example | Done | The description states the inputs and the Medicaid point. I get 1.15 to 0.92 with aca_magi: 18_000, and Medicaid eligibility on both trees from $18,000 of earnings. |
| Suggestion 3: first-modeled-year guard after #10846 | Done | Listed under follow-ups. |
| Suggestion 4: ACA calculator levels | Done | The description gives both sets of levels and says the difference is not traced. |
| Suggestion 5: update the description | Done | The description matches the head. Its test list (9 new cases and 5 new cases) matches the files. |
C1 witnesses (main at 5cc0d58afa, then the head):
| Household | Worksheet 2 | main |
1e306d09cc |
|---|---|---|---|
| Texas, single, 2026, MAGI $35,369 | 226 | 2.26; contribution 7.557%; aca_ptc $5,165.69 |
2.26; 7.557%; $5,165.69 |
| Texas, single, 2025, MAGI $30,873 | 205 | 2.05; $5,515.38 | 2.05; $5,515.38 |
| Hawaii, family of eight, 2026, MAGI $125,223, state supplied for both years | 201 | 2.01; aca_ptc $9,848.49 |
2.01; $9,848.49 |
| Alaska, single, 2026, MAGI $44,183, state supplied for 2026 | 226 | 2.82 (the bug this PR fixes) | 2.26; contribution 7.557%; aca_ptc $9,310.96 |
C1 grids:
- Single filer, every whole-dollar MAGI that is an exact whole percentage from 95% to 405%: Texas 2026 (155 cases), Texas 2025 (63), California 2026 (155), Alaska 2026 (155), Hawaii 2026 (31). The head is right in all 559. The fraction, eligibility, required contribution, CSR category and
aca_ptcequalmainin all of them. - Family sizes 1 to 8 in Texas, Alaska and Hawaii, at each exact percentage and one dollar either side (22,392 cases, state supplied for both years): the fraction equals
mainin every case. All 4,488 exact-percentage cases with a MAGI that the model stores exactly are right.
Should address
S1. The Guam and Virgin Islands exclusions have no test
policyengine_us/variables/gov/aca/eligibility/pays_aca_premium.py:79-85:
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)
)At a9cc99bc13 the test was np.isfinite(magi_frac), so one Puerto Rico case covered all three territories through the same zero guideline. The test is now three separate comparisons. The changelog and the description claim all three ("Households in Puerto Rico, Guam and the Virgin Islands ... pay no Marketplace premium"), and both territory cases are Puerto Rico (is_aca_ptc_eligible.yaml:590 and :619).
What I ran:
- With the Puerto Rico comparison removed, 2 cases fail. With the Guam comparison removed, all 39 pass. With the Virgin Islands comparison removed, all 39 pass.
- Witness: Guam, single adult aged 30, 2026,
aca_magi: 30_000, the Medicaid, CHIP, Medicare and employer-coverage inputs false. At the head:pays_aca_premiumfalse,selected_marketplace_plan_actuarial_value0. With the Guam and Virgin Islands comparisons removed:pays_aca_premiumtrue and the actuarial value 0.7, for Guam and for the Virgin Islands, and no test fails.is_aca_ptc_eligiblestays false through the infinite fraction, so the effect is on the premium variables, not on the credit.
Fix: add two cases to is_aca_ptc_eligible.yaml, the same as the 2024 Puerto Rico case with state_code: GU and state_code: VI, each expecting pays_aca_premium: false and is_aca_ptc_eligible: false. I ran both households at the head for 2026 and they give those values.
Suggestions
- Update rulespec-us#2210 for the head. Its "PolicyEngine implementation" section still says
pays_aca_premiumaddshas_exchange = isfinite(aca_magi_fraction)and counts 7 and 5 new cases. The head tests the state group and has 9 and 5. - The Northern Mariana Islands.
StateCodehasMP, andstate_groupputs it in the contiguous group. A 2026MPsingle adult with $30,000 of MAGI has a fraction of 1.91,pays_aca_premiumtrue andis_aca_ptc_eligibletrue, onmainand here (slcspandaca_ptcare 0). 42 U.S.C. 18024(d) excludes it in the same way as the three territories. This PR does not change it; it is a candidate for the follow-up list. - Puerto Rico in 2027 raises an unrelated error on both trees. A Puerto Rico single adult in 2027 raises
ValueError: Unsupported array dtype: float64fromsnap_regionwhenpays_aca_premium,is_aca_ptc_eligible,slcsporaca_ptcis computed.maindoes the same, so this PR does not cause it. It is worth a separate issue, because the description's "in any year" cannot be shown for Puerto Rico in 2027. - One wording point. The description says "core backdates each parameter's first value to 2015". The backdating is in this repository (
backdate_parameters, called fromsystem.py), not in policyengine-core.
Interaction with #10041
At its new head (d4ec8c5cf8), #10041's aca_fpg still reads state_group_str for the guideline year, its aca_magi_fraction is the plain np.floor(100 * magi / fpg) / 100, and it moves the enrollment conditions of pays_aca_premium into is_aca_marketplace_enrollee. The PR that merges second must read the state group for the coverage year in aca_fpg, keep the zero-guideline handling in aca_magi_fraction (plain division gives NaN for a territory household with $0), carry the has_exchange test to where the enrollment conditions then live, and keep these test cases.
What I checked
The diff since a9cc99bc13, line by line (122 lines, 5 files).
aca_magi_fraction.py:52-57: the order ismain's. Thewhere(has_guideline, guideline, 1)denominator stays, so the formula itself does not divide by zero. In the runs withaca_magisupplied,mainemits divide-by-zero and invalid-value warnings from the fraction for territory households and the head emits none. No NaN appears at the head in any run below;maingives NaN for a territory household with $0 and the state supplied for both years.pays_aca_premium.py:79-85: the state group is read for the coverage year, as an enum, and compared with the three enum members. It is vectorised: in one simulation with 42 households in Puerto Rico, Guam, the Virgin Islands, the Northern Mariana Islands, Texas, Alaska and Hawaii, each household gets its own result.is_aca_ptc_eligiblerequirespays_aca_premium, so the exclusion does not depend on how the eligibility scale treats an infinite fraction.- The comments and the two new cases say what the code does. The changelog fragment matches the head: it is a top-level file, type
fixed, it states the coverage-year state and family size and the territory rule, and it says nothing about an error or about truncation (the order is unchanged againstmain).
Territories, 2015 to 2027. Puerto Rico, Guam and the Virgin Islands; $0, $10,000 and $30,000; a single adult and an adult with a child; income as earnings and as an aca_magi input with the coverage inputs false; state supplied for the coverage year only and for both years.
| Check | main |
1e306d09cc |
|---|---|---|
| 2015 to 2026: errors (864 households, 1,296 people) | none | none |
is_aca_ptc_eligible |
false with the state for both years; true for 198 of 648 people with the state for one year | false for all 1,296 |
pays_aca_premium |
true for all 648 people with the state for both years; true for 252 of 648 with the state for one year | false for all 1,296 |
aca_ptc, premium_tax_credit, used_aca_ptc, slcsp |
0 | 0 |
aca_magi_fraction |
infinite or NaN (both years); the contiguous fraction (one year) | infinite for all 864 |
aca_required_contribution_percentage |
NaN at $0 (both years); the contiguous rate (one year) | 0 for all 864 |
| 2027, Guam and the Virgin Islands, single, $30,000 | fraction 1.87, eligible | infinite, not eligible, no error |
| 2027, Puerto Rico | ValueError from snap_region |
the same ValueError |
- The changes in
pays_aca_premiumandis_aca_ptc_eligibleare the ones the PR states. Three variables followpays_aca_premiumfor those people:selected_marketplace_plan_actuarial_value(0.7 to 0),is_aca_family_tier_dependent_child(true to false for the child) andmarketplace_csr_category(to 0). - Controls in the same simulations: Texas and Northern Mariana Islands households have the same value on both trees for all 11 variables I compared, in every year from 2015 to 2026. Alaska and Hawaii households differ only where the state is supplied for the coverage year alone.
Households. I re-ran the 74 households from the last review on main (5cc0d58afa) and the head. Every head value equals the value at a9cc99bc13, and every main value equals the value on the earlier main. The C1 households are separate and are in the table above. Examples: Alaska single $30,000 in 2026, 1.91 to 1.53 and aca_ptc $10,799.92 to $11,349.40; Alaska single $70,000, 4.47 and not eligible to 3.58 and eligible with $5,677.78; Alaska single $78,200, not eligible on both trees; Texas single $62,600, 4.00 and not eligible on both trees; negative MAGI in Alaska, 0 on both trees.
Does a test pin each changed line? One change at a time, on the two changed files (39 cases at the head).
| Change | Result |
|---|---|
| Divide before multiplying | 1 fails: pinned |
| Plain division, no zero-guideline handling | 1 fails: pinned |
| 0 in place of infinity for the territories | 1 fails: pinned |
has_exchange removed |
2 fail: pinned |
| Puerto Rico comparison removed | 2 fail: pinned |
| Guam comparison removed | all pass: not pinned (S1) |
| Virgin Islands comparison removed | all pass: not pinned (S1) |
| State read for the guideline year | 11 fail: pinned |
| Family size read for the guideline year | 1 fails: pinned |
| First-modeled-year guard removed | 1 fails: pinned |
| Guideline not lagged | 12 fail: pinned |
| Negative MAGI not floored at 0 | 1 fails: pinned |
Tests (Python 3.13, run locally):
| Suite | 1e306d09cc |
Merged into main (5cc0d58afa) |
|---|---|---|
| The two changed YAML files | 39 passed | 39 passed |
baseline/gov/aca |
359 passed | 359 passed |
contrib/aca |
67 passed | 67 passed |
baseline/partners |
623 passed | 623 passed |
No failures. The merge into main is clean (main is 14 commits ahead of the merge base; none touches the ACA variables, fpg.yaml or the partner folder). The PR edits no partner file.
CI. All 38 checks passed at 1e306d09cc, including Microsimulation and all the Contrib shards (checked at 3:12pm EST on 2026-11-13).
Repository rules. The changelog fragment and the axiom: line are valid (see the table above). #10413 is disclosed.
Not verified
- I ran the tests on Python 3.13. CI uses Python 3.14 and may install a different policyengine-core.
- I did not re-run the microsimulation at this head. The figures in the description (18 tax units and $2.93m in 2024, no change in 2025 or 2026) are from my run at
391384776f. The CI Microsimulation job passed at the head. The dataset has no territory households. - I did not re-run
baseline/gov/hhsor the all-variable comparison. The diff sincea9cc99bc13touches no parameter, andfpg.yamlnow equalsmain. - For 2027 I ran only single adults at $30,000 in Guam and the Virgin Islands. Puerto Rico in 2027 raises the unrelated error on both trees, so I could not check it.
- The ACA calculator's dollar levels. My run for a single 40-year-old Alaska adult earning $40,000 in 2027 in the default county gives $12,169.84 on
mainand $12,940.31 here; the change of $770.47 matches. - I ran nothing in rulespec-us. I read issue #2210 and the state and labels of #1627.
- The rest of the suite (states, IRS, household, the other contrib reforms). CI covers it.
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 <noreply@anthropic.com>
|
@PavelMakarchuk, thanks. S1: Suggestions:
The #10041 bullet also says the PR that lands second carries |
#10041 moved the guideline lookup into aca_fpg and the enrollment conditions of pays_aca_premium into is_aca_marketplace_enrollee. Resolve by carrying this PR's changes to where they now live, as Pavel's review laid out: - aca_fpg reads the state group for the coverage year and lags only the guideline (1.36B-1(h); Form 8962 line 4). - aca_magi_fraction reads aca_fpg and keeps the zero-guideline guard and the multiply-first truncation. - is_aca_marketplace_enrollee carries the no-Exchange test for Guam, Puerto Rico and the Virgin Islands; pays_aca_premium is main's. - The coverage-year family-size case now supplies aca_tax_family_size, which aca_fpg reads since #10041. - Both sides' test cases are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
#10041 merged, so
The description and the changelog are updated. As before, CI is the check; I haven't run anything locally. |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Re-review: Use the Alaska and Hawaii poverty guidelines in the premium tax credit's income fraction (#10860)
Verdict: REQUEST_CHANGES
I reviewed d5909647d8 (the commit at 3:53pm EST and the merge of main at 4:02pm EST on 2026-11-13). #10041 is now in main, so I reviewed the whole diff against main (a8ab4228b3) as new code, not only the delta.
The Guam and Virgin Islands item from my last review is resolved, and the move into aca_fpg, aca_magi_fraction and is_aca_marketplace_enrollee is correct in every household I ran. One small item is new, and the move caused it: the case that pinned the multiply-first truncation no longer pins it. One changed number in one test case closes it.
Counts open at d5909647d8: 0 critical, 1 should address, 5 suggestions.
Resolved since the last review
| Item | Status | How I checked |
|---|---|---|
| S1 The Guam and Virgin Islands exclusions had no test | Resolved | is_aca_ptc_eligible.yaml:991 and :1016. With the Guam comparison removed from is_aca_marketplace_enrollee.py:70-74, 1 case fails. With the Virgin Islands comparison removed, 1 case fails. With the Puerto Rico comparison removed, 2 fail. With has_exchange removed, 4 fail. |
| Suggestion 1: update rulespec-us#2210 | Done at 62e9ac8026, stale again after the move |
See suggestion 3. |
| Suggestion 2: the Northern Mariana Islands | Done | On the follow-up list. |
| Suggestion 3: Puerto Rico in 2027 | Done | On the follow-up list, and "in any year" is now "every year checked from 2015 to 2026". Both trees still raise the same ValueError from snap_region. |
| Suggestion 4: the backdating wording | Done | The description names backdate_parameters in this repository. |
| The move after #10041 merged | Done | The coverage-year state is in aca_fpg.py:51, the zero-guideline guard and the multiply-first line are in aca_magi_fraction.py:33-38, and the no-Exchange test is in is_aca_marketplace_enrollee.py:68-78. pays_aca_premium.py equals main. |
Should address
S1. The whole-percentage case no longer pins the multiply-first order
policyengine_us/tests/policy/baseline/gov/aca/eligibility/aca_magi_fraction.yaml:193-210:
- 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.
...
aca_magi: 35_369
...
aca_magi_fraction: 2.26The description says "A case now pins the order", and the comment in aca_magi_fraction.py:34-36 uses the same $35,369 example. That was true at 1e306d09cc. It is not true at this head.
- Why it changed. Before the merge, the fraction divided by a guideline that came straight from the parameter (64-bit). Since #10041 it divides by the
aca_fpgvariable, which is stored in 32 bits likeaca_magi. In 32-bit arithmetic 35,369 / 15,650 times 100 rounds to exactly 226, so dividing first also gives 2.26 for this household. - What I ran. I changed
aca_magi_fraction.py:37-38to divide first (fraction = magi / guideline, thenfloor(100 * fraction) / 100) and ran the two changed YAML files: all 55 cases pass. At1e306d09ccthe same change failed 1 case. - The order still matters. Of 4,608 single-state households on an exact whole percentage (2024 and 2025 guidelines, family sizes 1 to 8, 95% to 405%), dividing first in 32-bit arithmetic is one point low in 184. Multiplying first is right in all of them.
Witness (Texas, single adult aged 40, 2026 coverage, 2025 guideline $15,650):
aca_magi |
Worksheet 2 | main |
d5909647d8 |
Divide-first variant |
|---|---|---|---|---|
| $35,369 (the case in the file) | 226 | 2.26 | 2.26 | 2.26 (not caught) |
| $36,621 | 234 | 2.34 | 2.34 | 2.33 |
| $32,865 | 210 | 2.10 | 2.10 | 2.09 |
| $16,589 | 106 | 1.06 | 1.06 | 1.05 |
Fix: in the case at aca_magi_fraction.yaml:193, use aca_magi: 36_621 and expect aca_magi_fraction: 2.34 (100 x 36,621 / 15,650 = 234 exactly), and update the two comments to that example. I ran that case: it passes on main and at the head, and it fails with the divide-first variant.
Suggestions
-
The invariant test compares
aca_fpgwith the wrong quantity for Alaska and Hawaii.test_claimable_filer_ptc_invariants.py:347-350assertsaca_fpg(year) == tax_unit_fpg(year - 1). The docstring edit at:34-36is accurate: no drawn state is outside the contiguous group, so the test passes and hides no failure of the model. But the identity itself is the pre-fix one. I ran the test's own_identitieson an Alaska couple with no claimable filer and the state supplied for the coverage year, as the test supplies it:2026, Alaska couple, nobody claimable aca_fpgtax_unit_fpg(2025)_identitiesmain21,150 21,150 passes (both are the contiguous amount: the bug) d5909647d826,430 21,150 fails, 4 of 4 units 26,430 = 19,550 + 6,880 is the right value. The right invariant is:
aca_fpg(year)equals the prior year's guideline for the coverage year's state group and the coverage year's tax family size, that isfpg(aca_tax_family_size, state_group_str(year), year - 1). That identity holds at the head for every unit I drew in Texas, California, Alaska and Hawaii (32, 16 and 16 units, 2025 and 2026), and fails for Alaska and Hawaii onmain. With it,AKorHIcan joinSTATES, and the test then covers the claimable-filer rule and this PR together. Line 36 of the docstring is also longer than the lines around it. -
Two test comments name the old location.
is_aca_ptc_eligible.yaml:993and:1018say "Without the Guam comparison inpays_aca_premium". The comparison is inis_aca_marketplace_enrollee. Each of the four territory cases could also assertis_aca_marketplace_enrollee: false, which is the value the changelog names first. -
rulespec-us#2210 describes the old head again. Its "PolicyEngine implementation" section names
62e9ac8026, saysaca_magi_fractionlags the guideline and readstax_unit_size, and putshas_exchangeinpays_aca_premium. At this head the lookup is inaca_fpg, the size isaca_tax_family_size, andhas_exchangeis inis_aca_marketplace_enrollee. Its note 4 ("#1627's membership rules arrive with #10041") and its "Related" line about #10041 can now say that #10041 merged. -
Four points in the description.
- "A case now pins the order": see S1.
- The direct-run levels for the Anchorage example ($11,969.37 on
main, $12,739.84 here). I get $12,169.84 onmainand $12,940.31 here for a single 40-year-old Alaska adult earning $40,000 in 2027 in the default county. The change of $770.47 is the same. - #10413: since #10041 merged, GitHub reports it as conflicting with
main, and its change is toaca_magi_fraction, which no longer holds the lookup. "Only one of the two should land" can now say that #10413 needs a rebase ontoaca_fpgto do anything, or can close. - The microsimulation levels moved with #10041: I now get $62,542.37m to $62,545.30m for 2024 (the same +$2.93m and the same 18 tax units). The description does not list the docstring edit in
test_claimable_filer_ptc_invariants.py.
-
A supplied enrollment input in a territory.
is_aca_marketplace_enrollee.py:82-83adds a suppliedpays_aca_premiumafter the no-Exchange test, as #10041 designed it for every other condition. So a Guam adult withpays_aca_premium: truesupplied is an enrollee with a selected-plan actuarial value of 0.7. The credit is still 0 in every case I ran (see the table below), because the infinite fraction fails the income test. A sentence in the variable's documentation would make that choice explicit. No change to the code is needed.
What I checked
The whole diff against main, line by line (7 files, 437 added lines).
aca_fpg.py. The state group is read for the coverage year (:51) and only the guideline lags (:41-43,:52).fpg(size, state_group, guideline_period, parameters)reads the parameters at 1 January of the guideline year, which is whatmain'syear_lagcall read. The first-modeled-year guard is unchanged: Alaska in 2015 gives the same value on both trees, and Alaska in 2016 uses Alaska's 2015 guideline ($14,720, wheremaingave $11,770). The family size isaca_tax_family_sizefor the coverage year. A unit with no tax family keeps the whole-unit convention, now with the coverage-year state (Alaska couple, both filers claimable: 21,150 to 26,430; no credit on either tree).- Readers of
aca_fpg. Two formulas read it besidesaca_magi_fraction:ca_premium_subsidy_eligible.py:58andca_premium_subsidy_applicable_percentage.py:34. California is in the contiguous group, so both get the same value as onmain. A California household is identical on both trees for all 7,660 variables. - Readers of
aca_magi_fraction. The federal credit and cost-sharing variables, the Basic Health Program, the three contributed ACA reforms, and the Vermont, Massachusetts, Colorado, Connecticut, New Mexico, New Jersey, Maryland and Washington premium programs. None is an Alaska, Hawaii or territory program, and each is limited to its own state, so none sees a changed value. - Supplied inputs. A supplied
tax_unit_fpg(for 2025 or for 2026) does not reach the fraction on either tree, as since #10041. A suppliedaca_fpgis used as given on both trees (Alaska, $30,000,aca_fpg: 10_000: 3.00 on both). A suppliedtax_unit_sizedoes not reach the fraction for a return with a tax family, as the description says. aca_magi_fraction.py. The denominator iswhere(has_guideline, guideline, 1), so the formula does not divide by zero. Territories get an infinite fraction: no NaN at the head in any run below, wheremaingives NaN for a territory household with $0.is_aca_marketplace_enrollee.py. The state group is read for the coverage year as an enum and compared with the three members. It is vectorised: in one simulation with 42 households across Puerto Rico, Guam, the Virgin Islands, the Northern Mariana Islands, Texas, Alaska and Hawaii, each household gets its own result. Reading the state group for the prior year instead fails 4 cases, so the year is pinned. Because the test is on the state group and not on the guideline, a Guam household with a suppliedaca_fpgof 15,650 (fraction 1.91) is still not an enrollee.- The changelog fragment is a top-level file of type
fixed. It namesaca_fpg, the coverage-year state, and the territory rule in the order the code applies it (not enrollees, no premium, not eligible). It matches the head.
Territory households with supplied enrollment or premium inputs (single adult, 2026, aca_magi: 30_000, the coverage inputs false, state supplied for 2026; Guam shown, Puerto Rico and the Virgin Islands give the same):
| Supplied | main |
d5909647d8 |
|---|---|---|
| nothing | enrollee, eligible, aca_ptc 0 (slcsp 0) |
not an enrollee, not eligible, 0 |
is_aca_marketplace_enrollee: true or pays_aca_premium: true |
eligible, aca_ptc 0 |
enrollee as supplied, not eligible, 0 |
| premium $6,000 | coverage-family premium $6,000, used_aca_ptc 0 |
$6,000, used_aca_ptc 0 |
premium $6,000 and slcsp $7,000 |
aca_ptc $5,150.14, used_aca_ptc $5,150.14 |
aca_ptc 0, used_aca_ptc 0 |
premium, slcsp and enrollee |
aca_ptc $5,150.14 |
aca_ptc 0, used_aca_ptc 0 |
- The supplied-premium rule from #10041 (
aca_ptc_coverage_family_premium.py) still assigns a supplied premium to the tax family when the model finds no enrollee. That does not create a credit for a territory household:used_aca_ptcis capped byaca_ptc, which is 0. I got the same result for 2020, 2024 and 2025, with the state supplied for one year and for both. - Texas and Northern Mariana Islands households with the same supplied inputs are identical on both trees.
Households, main then the head.
| Household | main |
d5909647d8 |
|---|---|---|
| Texas, single, 2026, $35,369 | 2.26; aca_ptc $5,165.69 |
the same |
| Texas, single, 2025, $30,873 | 2.05 | the same |
| Hawaii, family of eight, 2026, $125,223, state for both years | 2.01; $16,343.79 | the same |
| The same, state for 2026 only | 2.31; $14,961.32 | 2.01; $16,343.79 |
| Alaska, single, 2026, $44,183 | 2.82; $8,490.92 | 2.26; $9,310.96 |
| Alaska, single, 2026, $30,000 | 1.91; $10,799.92 | 1.53; $11,349.40 |
| Alaska in 2025, Texas in 2026, $30,000 | 1.53; $6,538.08 | 1.91; $5,988.60 |
| Texas in 2025, Alaska in 2026, $30,000 | 1.91; $10,799.92 | 1.53; $11,349.40 |
| Alaska, single, 2026, $18,000 | 1.15; eligible | 0.92; not an enrollee, not eligible |
| Alaska, single, 2026, $70,000 | 4.47; not eligible | 3.58; $5,677.78 |
| Alaska couple, one filer claimable (tax family of 1 in a tax unit of 2), 2026, $29,325 | aca_fpg 15,650; 1.87; $10,898.08 |
aca_fpg 19,550; 1.50; $11,421.06 |
| The same, state for both years | 19,550; 1.50; $11,421.06 | the same |
| Hawaii couple, one filer claimable, 2025, $25,965 | 15,060; 1.72; $5,685.43 | 17,310; 1.50; $5,913.93 |
| Texas couple, one filer claimable, 2026, $23,475 | 15,650; 1.50; $6,854.85 | the same |
- The Hawaii family-of-eight figure differs from my last review ($9,848.49) because this run uses two adults and six children, not one adult and seven.
- In the claimable-filer cases the claimable spouse is not eligible and the other spouse is, on both trees;
aca_ptc_slcspis half ofslcspon both.
Exact-percentage grids. Single filers on every whole-dollar exact percentage from 95% to 405% (Texas 2026 and 2025, California 2026, Alaska 2026, Hawaii 2026: 559 cases): the head is right in all 559, and the fraction, eligibility, required contribution, CSR category and aca_ptc equal main in all of them. Family sizes 1 to 8 in Texas, Alaska and Hawaii at each exact percentage and one dollar either side (22,392 cases, state supplied for both years, aca_tax_family_size supplied): the fraction equals main in every case, and all 7,452 whole-dollar cases that the model stores exactly are right.
Territories, 2015 to 2027. Puerto Rico, Guam and the Virgin Islands; $0, $10,000 and $30,000; a single adult and an adult with a child; income as earnings and as an aca_magi input; state supplied for one year and for both.
| Check | main |
d5909647d8 |
|---|---|---|
| 2015 to 2026: errors (864 households, 1,296 people) | none | none |
| NaN | fraction and required contribution at $0 (156 households) | none |
is_aca_marketplace_enrollee, pays_aca_premium |
true for all 648 people with the state for both years; true for 252 of 648 with one year | false for all 1,296 |
is_aca_ptc_eligible |
true for 198 of 648 people with the state for one year | false for all 1,296 |
aca_ptc, used_aca_ptc, premium_tax_credit, slcsp, aca_ptc_slcsp |
0 | 0 |
aca_magi_fraction; aca_required_contribution_percentage |
infinite, NaN or the contiguous value | infinite; 0 |
| 2027, Guam and the Virgin Islands (48 households) | eligible at $30,000 with the state for one year | no error, no NaN, not eligible, all amounts 0 |
| 2027, Puerto Rico | ValueError from snap_region |
the same ValueError |
Controls: no value differs from main outside Alaska, Hawaii and the territories.
- In the territory simulations, the Texas and Northern Mariana Islands households have the same value on both trees for all 16 variables, in every year from 2015 to 2027. Alaska and Hawaii households differ only where the state is supplied for the coverage year alone.
- All 7,660 variables, an adult with a child: Texas 2026, California 2026 and New York 2025 are identical on both trees. Alaska 2026 and Hawaii 2024 with the state supplied for both years are identical too.
- Alaska 2026 with the state for one year (earnings of $30,000, Medicaid-eligible): 3 variables differ (
aca_fpg,aca_magi_fraction,aca_required_contribution_percentage). - Guam 2026: 16 variables differ, all of them enrollment, eligibility, cost-sharing and actuarial-value variables that follow
is_aca_marketplace_enrollee.
Does a test pin each changed line? One change at a time, on the two changed YAML files (55 cases at the head).
| Change | Result |
|---|---|
| Divide before multiplying | all pass: not pinned (S1) |
| Plain division, no zero-guideline handling | 1 fails: pinned |
| 0 in place of infinity for the territories | 1 fails: pinned |
has_exchange removed |
4 fail: pinned |
| Puerto Rico comparison removed | 2 fail: pinned |
| Guam comparison removed | 1 fails: pinned |
| Virgin Islands comparison removed | 1 fails: pinned |
has_exchange reads the prior year's state group |
4 fail: pinned |
aca_fpg reads the state for the guideline year |
11 fail: pinned |
aca_fpg reads the family size for the guideline year |
33 fail: pinned |
| First-modeled-year guard removed | 1 fails: pinned |
| Guideline not lagged | 21 fail: pinned |
| Negative MAGI not floored at 0 | 1 fails: pinned |
Tests at d5909647d8 (Python 3.13, policyengine-core 3.34.10, run locally):
| Suite | Result |
|---|---|
| The two changed YAML files | 55 passed |
baseline/gov/aca |
518 passed |
contrib/aca |
76 passed |
State premium programs that read aca_fpg or aca_magi_fraction (California, Vermont, Massachusetts, Colorado, Connecticut, New Mexico, New Jersey, Maryland, Washington) and the Basic Health Program |
835 passed |
test_claimable_filer_ptc_invariants.py, test_aca_ptc_filing_gate_invariants.py, test_aca_required_contribution_percentage_properties.py, test_premium_input_contract.py and the three California premium subsidy unit tests |
38 passed |
baseline/household/income |
224 passed |
baseline/partners |
623 passed |
No failures. The PR edits no partner file.
Microsimulation (enhanced_cps_2024, 55,264 tax units, main then the head):
| Year | aca_ptc, all |
aca_ptc, Alaska |
aca_ptc, Hawaii |
used_aca_ptc, all |
|---|---|---|---|---|
| 2024 | $62,542.37m to $62,545.30m (+$2.93m) | $183.82m to $186.53m (+$2.71m) | $9.86m to $10.08m (+$0.22m) | $43,665.21m, unchanged |
| 2025 | $80,230.82m, unchanged | $246.97m, unchanged | $10.87m, unchanged | $55,740.67m, unchanged |
| 2026 | $77,178.31m, unchanged | $59.57m, unchanged | $11.08m, unchanged | $54,887.60m, unchanged |
- 2024:
aca_fpgchanges in 434 tax units, all in Alaska (156) and Hawaii (278).aca_ptcchanges in 18 of them (8 in Alaska, 10 in Hawaii), always upward.used_aca_ptcchanges in 9 rows, all with a weight of 0. No row outside Alaska and Hawaii changes inaca_fpg, the fraction,aca_ptcorused_aca_ptc. Eligibility changes for no person. - 2025 and 2026: no row changes in any of those variables. The dataset has no territory rows.
CI. At 4:58pm EST on 2026-11-13: 33 checks passed, 6 Contrib shards were still running, none had failed. Quick Feedback, PR validation (lock, guard tests, lint, changelog), Household API Partners, Microsimulation and every Baseline shard had passed.
Repository rules. The changelog fragment is valid and matches the code. The axiom: line is valid: rulespec-us#2210 is open with the pe-parity label and covers the guideline by state of residence, the Worksheet 2 truncation and the territory exclusion; rulespec-us#1627 (open, pe-parity) is named for family size. #10413 is disclosed (see suggestion 4 for its new state). #10312 and #10846 are still open drafts.
Not verified
- I ran the tests on Python 3.13. CI uses Python 3.14 and may install a different policyengine-core.
- The six Contrib CI shards had not finished when I checked.
- Microsimulation: the default dataset only, 2024 to 2026, and only
aca_fpg,aca_magi_fraction,is_aca_ptc_eligible,is_aca_marketplace_enrollee,aca_ptcandused_aca_ptc. I did not compare state premium programs, taxes or net income in the microsimulation. The dataset cannot show the territory change. - For 2027 I ran Guam and the Virgin Islands only. Puerto Rico in 2027 raises the unrelated error on both trees.
- The all-variable comparison used one household shape (an adult with a child) in five states. I did not run it for Hawaii with the state supplied for one year or for Puerto Rico and the Virgin Islands.
- The ACA calculator's dollar levels (PolicyEngine/ACA-Calc#37). Only the change of $770.47 is confirmed.
- I ran nothing in rulespec-us. I read issue #2210 and the state and labels of #1627.
- The rest of the suite (the other state, IRS and contrib tests). CI covers it.
…age-year state Per Pavel's re-review at d590964: - 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 <noreply@anthropic.com>
|
@PavelMakarchuk, thanks. S1: the whole-percentage case now uses Suggestions:
CI is the check, as before. |
PavelMakarchuk
left a comment
There was a problem hiding this comment.
Verdict: APPROVE (head d5f3f7a6b8)
The one open item from my last review is closed, and the five suggestions are taken. I found no new defect in the commit. Nothing is open.
Resolved since the last review
| Item | Status |
|---|---|
| S1: the whole-percentage case did not pin the multiply-first order | Resolved. aca_magi_fraction.yaml now uses aca_magi: 36_621 and expects 2.34. I changed aca_magi_fraction.py to divide first and ran the file: 1 failed, 14 passed. At the head: 15 passed. |
Suggestion 1: the invariant compared aca_fpg with the prior-year tax_unit_fpg |
Done. The identity now uses the prior year's guideline for the coverage year's tax family size and state group. For an Alaska couple in 2026 both sides give 26,430; the old right-hand side gives 21,150. The 14 tests in test_claimable_filer_ptc_invariants.py pass. |
Suggestion 2: territory test comments named pays_aca_premium |
Done. The comments name is_aca_marketplace_enrollee, and the four territory cases now also assert is_aca_marketplace_enrollee: false. |
| Suggestion 3: TheAxiomFoundation/rulespec-us#2210 described an older commit | Done. The issue is open with pe-parity and was updated on 2026-11-13 at 5:08pm EST. The axiom line names it as queued. |
| Suggestion 4: stale points in the description | Done. The description has the $36,621 example, and it says that #10413 conflicts with main. |
Suggestion 5: a supplied pays_aca_premium in a territory |
Done as documentation in is_aca_marketplace_enrollee. |
What I checked
-
The diff since
d5909647d8(5 files, 31 lines added, 16 removed), line by line. The only change to a formula file is a comment inaca_magi_fraction.pyand the documentation string inis_aca_marketplace_enrollee.py. No value changes. -
Households at this head (2026, single adult aged 40 unless stated).
Household aca_fpgaca_magi_fractionTexas, MAGI $36,621 15,650 2.34 Texas, MAGI $35,369 15,650 2.26 Alaska, MAGI $44,183 19,550 2.26 Alaska, MAGI $30,000 19,550 1.53 Hawaii, MAGI $30,000 17,990 1.66 Alaska, MAGI $18,000 19,550 0.92 Alaska, two adults, MAGI $40,000 26,430 1.51 These equal the values from my last review.
-
Tests at this head. The two changed YAML files: 55 passed.
baseline/gov/acaandcontrib/aca: 594 passed.test_claimable_filer_ptc_invariants.py: 14 passed. -
Partner contract tests. The PR edits no file under
policyengine_us/tests/policy/baseline/partners/. The folder passes at this head (623 passed). -
Merge. The head merges into current
main(f926615321) without conflicts. The five commits thatmainadded since the merge base do not touch ACA files. -
CI. All 38 checks pass at
d5f3f7a6b8, the microsimulation job included. The last check finished on 2026-11-13 at 6:15pm EST.
Not verified
- I ran the tests on Python 3.13; CI uses 3.14.
- I did not run the microsimulation again at this head. The commit changes no formula, and the run at
d5909647d8gave the same 2025 and 2026 totals asmain. - I did not run the state premium program directories or the household income directory again; they passed at
d5909647d8, and no formula changed since.
Summary
Fixes #9482. The premium tax credit's income fraction divides ACA MAGI by the prior year's poverty guideline (
aca_fpg, since #10041). The lookup also read the household's state for the prior year, so a household whose state is supplied only for the coverage year fell back there to the default state, which is in the contiguous group. Every API request is such a household, and so are most YAML tests. As a result, Alaska and Hawaii households got the contiguous states' guideline. In the issue's example, an Alaska single filer's credit ended at 4 × $15,650 = $62,600 of MAGI instead of 4 × $19,550 = $78,200.What changes
aca_fpgreads the state group for the coverage year and lags only the guideline, through the existingfpghelper. (Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041, now merged, moved the lookup fromaca_magi_fractionintoaca_fpg; this PR is merged with it.)aca_fpgreadsaca_tax_family_sizefor the coverage year, so this PR doesn't change it; a case pins it.is_aca_marketplace_enrollee, where Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041 put the enrollment conditions, now excludes the three by state group, so their residents aren't enrollees,pays_aca_premiumis false, and they aren't eligible for the credit. Their placeholder guideline is 0, and the fraction is now infinite there rather than 0 / 0, which is NaN at $0 of income.fpg.yamlis unchanged: this repository'sbackdate_parameters, called fromsystem.py, backdates each parameter's first value to 2015, so the 2024 placeholder already answers every guideline year the credit reads.floor(100 × MAGI / guideline) / 100, multiplying before dividing asmaindoes. An earlier commit here divided first, which floored income exactly on a whole percentage one point low in every state (Texas, $36,621 in 2026: 2.33 instead of 2.34). A case pins the order; since Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041 the guideline is the storedaca_fpg, so the case uses an income that divide-first gets wrong in 32-bit arithmetic too.Effects
main(from the review). The change of about $770 matches; I haven't traced the difference in levels. The calculator worked around the bug by setting the state for both years.aca_magi: 18_000and the Medicaid, CHIP, Medicare and employer-coverage inputs false goes from 1.15 to 0.92. Alaska and Hawaii expanded Medicaid, so a household computed from earnings at that income is Medicaid-eligible and ineligible for the credit on both trees; the households that lose the credit under the floor are those barred from Medicaid for another reason.marketplace_csr_eligible,marketplace_csr_category) move with the fraction.ValueErrorfromsnap_regiononmainand here). Onmain, one whose state was supplied only for the coverage year got the contiguous guideline and could be eligible (2024 coverage at $30,000: 30,000 / 14,580, floored to 2.05); one whose state was supplied for both years already had an infinite fraction and was not eligible.mainonly when the coverage year is the dataset's own year and its state isn't visible a year earlier. The review ranenhanced_cps_2024after Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041 merged: in 2024,aca_fpgchanges for 434 tax units, all in Alaska and Hawaii;aca_ptcchanges for 18 of them, always upward, and rises by $2.93m ($62,542.37m to $62,545.30m); 2025 and 2026 don't change. The dataset has no territory rows.aca_fpg, the zero-guideline guard and multiply-first truncation intoaca_magi_fraction, and the no-Exchange test intois_aca_marketplace_enrollee. California's premium subsidy readsaca_fpgtoo, so it also takes the coverage-year state; California is contiguous, so no result changes.partners/analytics_coverage/edge_cases/federal/nutrition/{wic,school_meals}/federal.yaml), which don't readaca_magi_fraction. None has a Hawaii or territory household. The PR edits no partner file.Related PRs
aca_magi_fractionand eight cases. This PR also has the territory handling and the zero-guideline guard. This PR's cases cover Use current ACA household with prior-year poverty guidelines #10413's (Alaska, Hawaii, the contiguous states, a changed family size, changes of state between years), and it adds Use current ACA household with prior-year poverty guidelines #10413's negative-income case. Since Keep a filer who can be claimed as a dependent out of the premium tax credit family, and apply each state premium program's rule #10041 merged, Use current ACA household with prior-year poverty guidelines #10413 conflicts withmainand editsaca_magi_fraction, which no longer holds the lookup, so it would need a rebase ontoaca_fpgto do anything; with this PR it can close.fpg.yaml.Left for follow-ups
ptc_income_eligibilityisfalsefrom 2026, so MAGI of exactly $78,200 for an Alaska single filer is denied. That needs the unrounded comparison and is outside this PR; Treat income exactly at 400% FPL as premium tax credit eligible #10312 makes it.state_groupputsMPin the contiguous group, so a 2026MPsingle adult at $30,000 has a fraction of 1.91 andis_aca_ptc_eligibletrue, onmainand here. 42 U.S.C. 18024(d) excludes it as it does the other territories.snap_regionraisesValueError: Unsupported array dtype: float64when the ACA variables are computed, onmaintoo.pell_grant_eligibility_typereadstax_unit_fpgtwo years back with the same lagged-state pattern; I haven't checked which state and year the Pell rules call for.Tests
aca_magi_fraction.yaml:aca_tax_family_sizeof 3 supplied for 2026 only (1.50; the 2025 size would give 2.55).test_claimable_filer_ptc_invariants.py: the identity for returns with no claimable filer now comparesaca_fpgwith the prior year's guideline for the coverage year's tax family size and state group, in place of the prior-yeartax_unit_fpg, which reads the prior year's state. Its drawn states are all contiguous, so it passed either way; AK or HI could now join them.is_aca_ptc_eligible.yaml, all with the Medicaid, CHIP, Medicare and employer-coverage inputs false:mainthe state supplied for one year gave the contiguous guideline (2.05, eligible); without the no-Exchange testpays_aca_premiumwould be true;axiom: TheAxiomFoundation/rulespec-us#2210 queued (the applicable poverty guideline by state of residence under 26 CFR 1.36B-1(h) and Form 8962 Line 4, and the territory exclusion under 36B(c)(2)(A) and 42 U.S.C. 18024(d); the coverage-year family size is TheAxiomFoundation/rulespec-us#1627)
🤖 Generated with Claude Code