Skip to content

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
DTrim99 merged 53 commits into
mainfrom
claimable-filer-ptc
Nov 13, 2026
Merged

DTrim99 merged 53 commits into
mainfrom
claimable-filer-ptc

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Someone another taxpayer can claim as a dependent gets no premium tax credit, and a joint return where another taxpayer can claim one spouse computes the credit for the other spouse alone. The model applied neither rule: is_aca_ptc_eligible ignored whether another taxpayer could claim a filer, and aca_ptc, the benchmark premium, the poverty line, household income and the premium cap all counted the whole tax unit.

The PR reads claimable_as_dependent_on_another_return (from #10024: a deduction for the person is allowable to another taxpayer, whether or not that taxpayer claims it). Form 1040 (2025) instructions, line 12a, p.33 has the box checked "If you or your spouse ... can be claimed as a dependent on someone else's return", with no refund-only carve-out, and Form 8962 line 1 keys on that box. The return still claims its dependents when the would-be claimant meets the refund-only filing exception (dependent_claimant_filing_exception; Form 1040 dependents worksheet, p.18; Publication 501's Dependent Taxpayer Test).

  • IRC 36B(c)(1)(D), "Denial of credit to dependents": "No credit shall be allowed under this section to any individual with respect to whom a deduction under section 151 is allowable to another taxpayer". 26 CFR 1.36B-2(b)(3) excludes such an individual from applicable taxpayers.
  • 26 CFR 1.36B-1(d)(2) (taxable years ending on or after 2020-12-31) defines the family as "the taxpayer, including both spouses in the case of a joint return, except for individuals who qualify as a dependent of another taxpayer under section 152", plus the dependents the taxpayer properly claims. IRC 152(b)(1) and Publication 501, p.11: "If you are filing a joint return and your spouse can be claimed as a dependent by another taxpayer, you and your spouse can't claim any dependents on your joint return."
  • Form 8962 instructions (2025), line 1, p.8: "if you are filing jointly and you checked the 'Someone can claim your spouse as a dependent' box on your tax return, you or your spouse is not included in the tax family size calculation". The 2021-2024 editions say the same (p.6 to p.7).
  • 26 CFR 1.36B-3(b)(2), (f)(1): the benchmark is the second-lowest-cost silver plan "offered to the taxpayer's coverage family", the family members enrolled that month.

From #10024, is_aca_tax_family_member reads claimable_as_dependent_on_another_return and is_dependent_elsewhere_without_filing_exception (claimable, and the would-be claimant doesn't meet the refund-only exception). No formula in this PR reads #10024's tax-unit helpers.

Federal change

Variable Rule now
is_aca_tax_family_member (new, person), is_aca_filer (new, person) A filer no other taxpayer can claim; a dependent on the return when it has an applicable taxpayer and no filer is claimable outside the refund-only exception. A return where another taxpayer can claim every filer has no family (36B(c)(1)(D)). is_aca_filer uses supplied or inferred head/spouse roles and completes an incomplete joint-filer set without an adult-age cutoff. It infers the oldest member, or the two oldest on a joint return, respecting supplied dependency hints when some member isn't a supplied dependent. The same inference identifies filers for household income. An age tie between a filer and dependent needs explicit head/spouse roles. A dependent another taxpayer could also claim stays in the family of the return that claims them.
aca_tax_family_size (new) Form 8962 line 1: the number of members. The PR leaves tax_unit_size alone.
aca_fpg (new) Form 8962 line 4: the prior-year guideline for aca_tax_family_size. A return where another taxpayer can claim every filer has no tax family; it keeps the tax unit's guideline as a numerical convention. The credit reads $0 there, and other readers of aca_fpg see the old value. In 2015, the first modeled year, the tax year's guideline stands in (the guard from #10773, moved here so California's two direct reads get it too).
aca_magi_fraction Household income over aca_fpg (was tax_unit_fpg for the prior year, which also took the household size from the prior year; the size now comes from the tax year, as line 1 requires, which differs only when a user supplies a prior-year size).
aca_magi Household income (1.36B-1(e)(1)) excludes tax-exempt interest and Social Security of dependents outside the tax family, while retaining both joint filers' MAGI additions. Return-level AGI still follows the model's general tax-role calculations; the ACA inference does not reconstruct it. With no out-of-family dependent it reads medicaid_magi directly, including a supplied value. When exclusion is necessary, it reconstructs AGI plus MAGI additions, removes the excluded amounts, then floors once at zero; this also handles signed Social Security. Medicaid calculations remain separate.
is_aca_ptc_eligible, is_aca_ptc_eligible_before_premium (new) Both require tax-family membership. The substantive eligibility variable includes the existing income and return-status requirements without the marginal-premium condition; is_aca_ptc_eligible additionally requires pays_aca_premium. This lets CSR distinguish an insured fourth child from someone outside the tax family.
aca_ptc_below_fpl_immigration_exception The lawfully present person must be in the tax family: "the taxpayer or a member of the taxpayer's family" (1.36B-2(b)(5)).
is_aca_coverage_family_member (new), is_aca_marketplace_enrollee (new) Coverage-family membership is Marketplace enrollment plus tax-family membership (1.36B-3(b)(2)). Enrollment is separate from marginal premium payment: a fourth covered child remains enrolled even though the three-child rating cap gives them no additional age-rated premium. A supplied pays_aca_premium of true marks an enrollee.
aca_child_index, pays_aca_premium The whole policy also ranks only Marketplace-enrolled children for its three-child premium limit. Adults and non-enrolled people receive the existing sentinel index 99; older children on employer coverage cannot remove a younger covered child's premium.
aca_ptc_slcsp (new), with aca_ptc_slcsp_family_tier_category and _multiplier The model prices the coverage family separately, using its three oldest covered children in age-rated states (45 CFR 147.102(c)(1)) or its own family tier in NY/VT. Non-enrolled children use no ranking slots. Supplied slcsp_age_curve_amount_person prices, including positive and zero quotes for enrolled nonpayers, flow through an uncapped-price variable. Without a supplied quote, the model reprices a child freed from the original family's cap. When every enrollee belongs to the tax family, aca_ptc_slcsp reads slcsp directly, including supplied values. slcsp still prices the whole enrollment.
aca_ptc, aca_ptc_before_filing_gate Main's aca_ptc filing-gate wrapper remains intact. Its aca_ptc_before_filing_gate calculation uses aca_ptc_slcsp; the filing decision and take-up behavior from main remain in the dependency path.
aca_ptc_coverage_family_premium (new), used_aca_ptc The credit cannot exceed the coverage family's enrollment premiums (1.36B-3(d)(1)(i)). Each month, allocate the selected plan premium by coverage_family_benchmark / (coverage_family_benchmark + other_family_benchmark), pricing the other enrollees separately; then sum the allocated monthly amounts (1.36B-3(a), (h)(1)). This applies to silver and bronze. The share is one when every enrollee is in the family. A premium supplied for the year or for any month, for a tax unit in which the model finds no enrollee and with is_aca_marketplace_enrollee not supplied, is the tax family's, as before enrollment was modeled; otherwise the allocation reads zero when no family member enrolls, and a tax unit with no tax family gets none.
marketplace_csr_eligible The model prices one policy per tax unit. Under 45 CFR 155.305(g)(3), every insured individual must satisfy the substantive PTC conditions for the shared policy to receive CSR. An enrolled claimable spouse blocks CSR even when the rating cap makes their marginal premium zero; an ordinary fourth covered child retains substantive eligibility. Supplied is_aca_ptc_eligible values continue to apply to premium-paying enrollees.
slcsp_family_tier_category, slcsp_family_tier_multiplier Shared functions price the requested enrollment group. Explicitly supplied family-child and NY age-29 predicates remain in the dependency graph, including for enrollees paying no marginal premium and returns with no claimable filer. Calculated family-child values retain the public premium-payment condition; separate-family repricing applies the uncapped child condition to calculated nonpayers.
aca_ptc_700_fpl_cliff, aca_ptc_additional_bracket, aca_ptc_simplified_bracket reforms Each overrides is_aca_ptc_eligible and none changes 36B(c)(1)(D), so each keeps the gate. Without it the reforms would pay claimable filers.
aca_ptc_minimum_premium reform Its benchmark is aca_ptc_slcsp. The enrolled premium in its cap is the larger of that benchmark and aca_ptc_coverage_family_premium, and its used_aca_ptc caps the credit at aca_ptc_coverage_family_premium less the minimum premium.

selected_marketplace_plan_premium_proxy keeps its annual input API and sums monthly_selected_marketplace_plan_premium_proxy. The monthly allocation uses the month-specific selected plan prices. When a user supplies only an annual selected premium, the model distributes that total in proportion to the monthly selected-plan proxies; if those proxies are all zero, it distributes the total evenly. This timing assumption preserves the supplied annual amount. A user who knows the timing can supply monthly premiums through the new monthly variable. Either way, a supplied premium for a tax unit in which the model finds no enrollee, with enrollment not supplied, goes to the tax family.

Cost-sharing eligibility per person requires APTC eligibility (45 CFR 155.305(g)(1)), and 155.305(f)(1)(ii) requires "a member of the tax filer's family within the meaning of 26 CFR 1.36B-1(d)".

Worked cases (aca_ptc.yaml), $500 a month benchmark per enrollee, contiguous US, both spouses aged 30:

Household 2025 (MAGI $22,590) 2026 (MAGI $23,475)
Single, claimable $0 $0
Joint, one spouse claimable (either labelling) family 1, 150% FPL, 0%: $6,000 family 1, 150% FPL, 4.19%: $5,016.40
Joint, both claimable $0 $0
Joint, neither (control) family 2, 110% FPL: $12,000 family 2, 110% FPL, 2.1%: $11,507.03
Joint, one claimable, MAGI $18,000 119% FPL for one: $6,000 (control at 88%: $0)
Joint, one claimable whose claimant meets the refund-only exception, child ($250 a month), MAGI $30,000 family 2 (spouse and child), 146%: $9,000
Joint, one claimable, AGI $20,000, child with $20,000 tax-exempt interest household income $20,000 (was $40,000): $6,000
Lone 17-year-old filer, claimable (no head inferred) $0 (was $6,000)
Joint, one claimable, silver plan at 0.75 of benchmark ($9,000) credit used $4,500 (was $6,000), net premium $4,500; no CSR

The joint fixtures with a positive credit test the rule given the flag; see the note under the readings.

Before this PR, the joint one-claimable couple got the control's credit, and the single claimable filer got $6,000 (2025).

State premium programs, each checked against its own law

Program Its rule for an enrollee outside the federal tax family Change
Massachusetts ConnectorCare Requires APTC eligibility: 956 CMR 12.04(3)(a)2 to 12.04(2)(b) to 45 CFR 155.305(f); "Household" is "the tax filer and the individuals for whom a tax filer properly expects to claim a personal exemption" (12.03, p.4) Gate flows; wrap uses aca_ptc_slcsp
Connecticut Covered Connecticut Enrollees must be "eligible for premium and cost-sharing subsidies for a qualified health plan" (PA 21-2 (JSS) §16(b)(1), p.22) Gate flows; wrap uses aca_ptc_slcsp
Colorado Premium Assistance Payments "for Coloradans who receive the premium tax credit" (C.R.S. 10-16-1205(1)(b)(II), 2024, PDF p.1002, the payment authority Regulation 4-2-78 cites) Gate flows; residual cap uses aca_ptc_slcsp. See reading 2
Maryland premium assistance and Young Adult Subsidy Enrollee must be "a member of a tax filer's tax household" and the filer must meet the APTC requirements (COMAR 14.35.21.03A(1)) Gate flows (including the young-adult reduction); wrap uses aca_ptc_slcsp
California Premium Subsidy (c)(2) "Meets all eligibility requirements of the federal advance premium tax credit"; (d)(7)(B) "No state premium assistance shall be allowed ... to any individual who is claimed as a dependent of another return filer"; (d)(5) benchmark and (d)(10) family size have their 36B meanings (2026 Program Design, p.2 for (c)(2) and (d)(5), p.3 for (d)(7)(B), (d)(10) and (d)(11)) Gate flows; subsidy uses aca_ptc_slcsp; eligibility comment updated. The band test and the rate lookup now divide by aca_fpg, the poverty line the federal credit uses. (d)(10) of the 2026 and Final 2027 Designs both give family size its section 36B meaning, and (d)(11) of both takes the guideline most recently published when open enrollment starts
New Mexico Premium Assistance, base "qualify for the federal Premium Tax Credit" (2026 MAP manual, p.4) Gate flows; benchmark uses aca_ptc_slcsp
New Mexico Middle Income Household (above 400% FPL) "Meet all eligibility criteria for the federal PTC except for household income requirements" (Addendum, p.2) nm_premium_assistance_mih_eligible now needs a coverage family member
Vermont Premium Assistance "An individual or family eligible for federal premium tax credits under 26 U.S.C. § 36B" (33 V.S.A. 1812(a)(1)) Gate flows; cap uses aca_ptc_slcsp
New Jersey Health Plan Savings Treasury's 1332 addendum describes the program as for enrollees "otherwise eligible for the Advanced Premium Tax Credit" (p.8), written for its first plan year; Get Covered NJ: "Tax households with annual incomes up to 600%". We found no current NJ rule on someone another taxpayer can claim, so the PR interprets the program (reading 4) nj_njhps_member_eligible now needs a tax family member (it does not read is_aca_ptc_eligible, to keep the 400-600% band); cap uses aca_ptc_slcsp. #9515 also edits these two files (citations only); land it first
Washington Cascade Care Savings Policy §4(1) needs QHP eligibility, not APTC eligibility, and §4(2)'s exclusions do not include being claimable (2026 policy, p.11); Group 2 is "QHP-eligible residents ... not eligible for APTC Federal subsidies but eligible for CCS" at $250 PMPM (2026 methodology, pp.1, 5) A QHP-eligible enrollee outside the tax family moves from Group 1 ($55) to Group 2 ($250); the cap keeps slcsp, since their premium is in the household's net premium. See reading 1

The PR leaves Colorado OmniSalud alone: it serves undocumented enrollees and reads neither the credit nor its eligibility. The Basic Health Program payment proxy reads aca_magi_fraction and so now uses the tax family's poverty line, as its documentation says it mirrors aca_ptc; the PR leaves its enrollee premiums alone (follow-up 2).

All modeled state programs start in 2026, so the state changes affect 2026 on.

The 2026 state cases use a joint couple aged 30 with one spouse claimable, MAGI of $23,475 and a $500 a month benchmark each, which gives a federal credit of $5,016.40. Each runs under both labellings, beside the nobody-claimable control and the both-claimable case. Before this PR the model treated this couple as the control in every program.

Program This couple Control
Massachusetts $983.60 $492.97
Connecticut $983.60 $492.97
Colorado $960 $492.97
Maryland $983.60 $492.97
New Mexico $1,583.60 $1,692.97
Vermont $352.13 $352.13
New Jersey $240 $0 (below 138% FPL)
Washington $3,660 $492.97
California, at MAGI $22,000 $785.92 $462

Readings

Max accepted readings 1 to 4 as built. Reading 5 sets a timing assumption for annual-only premiums.

  1. Washington, Group 2 poverty line: this PR measures the household's income against the tax family's poverty line for every Cascade Care member. The policy defines income as 36B(d)(2) household income and the FPL by 36B(d)(3)(B), but no retrieved WA source says which family size applies to a Group 2 member. Alternative: the tax unit's size for Washington only. The two readings differ only near the 250% ceiling.
  2. Colorado: this PR follows the statute (payments for "Coloradans who receive the premium tax credit"). Regulation 4-2-78 §4C defines an eligible enrollee as any QHP enrollee at or below 400% FPL, which would keep the claimable spouse in the count and cap: at the 2026 example above, $960 (this PR) against $1,308.
  3. Exchange "tax filer": 45 CFR 155.300 defines a tax filer as one expecting "that no other taxpayer will be able to claim him, her or them as a tax dependent". Read as "either spouse", an Exchange would deny advance payments to the whole couple, and the state wraps keyed to APTC eligibility with them. This PR follows the annual credit's per-member rule (1.36B-1(d)(2), Form 8962 line 1) for both.
  4. New Jersey: the NJHPS exclusion above interprets the program's federal alignment. We found no rule on it in current New Jersey authority.
  5. Timing of an annual-only selected premium: 26 CFR 1.36B-3(a), (h) require monthly amounts and don't identify payment timing when a user supplies only an annual premium. This PR weights that annual amount by monthly selected-plan price proxies, and equal monthly amounts offer an alternative. In the bronze example, the coverage-family allocation comes to $7,794.14 as built and $7,845.60 with equal monthly amounts.

Readings 2 and 3 are source conflicts, so source_conflicts/candidates.yaml records them as us-co:premium-assistance-ptc-recipients (now with its statute and regulation texts and code paths) and us:exchange-tax-filer-claimable-spouse. Readings 1 and 4 fill gaps where no source gives the alternative, so they aren't source conflicts and have no candidate.

Another taxpayer can claim a married person only if the couple files jointly "only to claim a refund of income tax withheld or estimated tax paid". Publication 501 Example 3 (p.11) holds that claiming a refundable credit defeats that. We infer that the example reaches the premium tax credit, so a joint return that claims a positive credit while one spouse is claimable may not arise in practice. The model takes the flag as given and applies the line 1 rule, as it does for recovery rebates. is_aca_tax_family_member's documentation now says so.

Invariants

policyengine_us/tests/core/test_claimable_filer_ptc_invariants.py checks items 1 to 3 with Hypothesis on generated roleful returns in each year 2021-2026, with spouses claimable, claimed or neither, and with and without the refund-only exception. It also runs a seeded population in 2025 and 2026, in Texas, New York, Vermont and the nine wrap states, on #10024's household builders. Separate YAML cases cover role-less minor filers, supplied inputs, changing monthly prices and empty enrollment:

  1. Swap invariance: exchanging the two adults' head and spouse labels leaves the tax family size, aca_fpg, aca_magi, aca_magi_fraction, slcsp, aca_ptc_slcsp and aca_ptc unchanged. It also leaves the coverage-family premium, the used credit, CSR eligibility, the number of eligible members and all nine state amounts unchanged.
  2. Identities:
    • aca_tax_family_size equals the filers no other taxpayer can claim, plus the dependents if one filer counts as an applicable taxpayer and none is claimable outside the exception.
    • Every eligible person belongs to the tax family, and a return with no family member gets no credit.
    • The generated benchmark falls between zero and the whole enrollment's benchmark.
    • ACA MAGI matches an independent income-removal calculation, including dependent additions and signed Social Security.
    • Used credit <= coverage-family premium <= selected premium.
    • CSR requires every enrollee to satisfy the substantive PTC conditions even if they pay no marginal premium.
    • On roleful returns where no other taxpayer can claim a filer, the benchmark, MAGI and family size keep their ordinary values.
    • aca_fpg.yaml tests the cases where the current-year family size differs from the old prior-year size. Separate YAML cases test role-less filer corrections and explicit supplied inputs.
  3. Monotonicity: marking another filer as claimed never raises the tax family size, never raises the benchmark for the same enrollees (checked person by person), and never raises the credit. Two intended exceptions, each tested in YAML: income crosses the 100% floor (below it for two, above it for one), or out-of-family dependents' income leaves household income. The test exempts those two.
  4. One poverty line: California's band test, its rate lookup and the federal credit all divide by aca_fpg. policyengine_us/tests/unit/test_ca_premium_subsidy_one_poverty_line_properties.py draws incomes in 2026 and 2027 for a couple with one claimable spouse (tax family of one, tax unit of two) and lets the model compute aca_fpg. It checks eligibility against the Design band and the rate against the Design interpolation over the one-person guideline, in exact arithmetic. YAML witnesses in ca_premium_subsidy.yaml cover $20,000, $25,000 (both labellings) and $27,000 in 2026 and $35,000 in 2027; each lands differently over the tax unit's two-person line.

The seeded test also asserts it reaches the rule: returns with one claimable filer and a positive credit, and in 2026 a positive state wrap.

YAML cases also pin each condition of the supplied-premium rule (enrollment supplied, an enrollee found outside the family, no tax family), a premium supplied by month, the even split of an annual premium with no monthly price pattern, the CSR test of a premium payer's supplied is_aca_ptc_eligible, is_aca_filer's fallback when every member is a supplied dependent, and the minimum premium reform's used_aca_ptc cap. The comments of the last three cases (even split, CSR, filer fallback) say what the changed line would return.

Tests

At e03b7ec97a (main merged at 0315452583), 1,121 tests passed across 88 files, one file per process with numeric threads capped.

Files Count Passed
California Premium Subsidy YAML (tests/policy/baseline/gov/states/ca/hbex/premium_subsidy/) and its three unit files (test_ca_premium_subsidy_applicable_percentage.py, test_ca_premium_subsidy_eligible_properties.py, test_ca_premium_subsidy_one_poverty_line_properties.py) 7 114
Federal ACA YAML (tests/policy/baseline/gov/aca/) 48 463
Massachusetts ConnectorCare 4 49
Connecticut Covered Connecticut 3 43
Colorado Premium Assistance 3 54
Maryland premium assistance 5 101
New Mexico Premium Assistance 5 97
Vermont Premium Assistance 3 38
New Jersey Health Plan Savings 4 70
Washington Cascade Care Savings 5 78
tests/core/test_claimable_filer_ptc_invariants.py 1 14

At 18385c186b, Pavel's runs (Python 3.13, policyengine-core 3.34.10) passed the federal ACA and ACA contrib directories (570), the nine state premium program directories (624), tests/policy/baseline/household/income/ (224), the invariant file with the three California unit files (34) and the partner contract tests (623), and CI passed 36 of 36 checks. 6d336a28dd adds five cases for Pavel's Should address 1-3. d4ec8c5cf8 renames twelve cases that carried review-round names, writes out the six cases that used YAML anchors, and adds three cases. CI runs them.

California now divides by aca_fpg in both its band test and its rate lookup. Its tests supply that denominator as aca_fpg for the test's own period (63 YAML pins and both unit files), and no expected output changes. Five YAML witnesses (2026 at $20,000, $25,000 under both labellings and $27,000; 2027 at $35,000) and the new property file fail if either California read returns to the tax unit's guideline or aca_fpg takes the tax unit's size.

Two fixtures needed new inputs; their assertions did not change. Three New Mexico MIH integration scenarios supply aca_magi without income, so Medicaid treats them as having no income and they don't enroll. They have failed since this PR's MIH gate began requiring a coverage-family member; CI skipped the draft at 09544440a7, so no run showed it. They now supply Marketplace enrollment. The invariant file now supplies its signed Social Security dependent as the social_security total, because #10484 requires reported components to be nonnegative.

This comment holds the review history through r4 and the earlier merge with main.

Microsimulation impact

Pavel Makarchuk ran this PR against its merge base (ac280b06ed) on the default dataset, one run each, at 18385c186b (review of 2026-11-13):

Weighted total 2025 merge base 2025 this PR 2026 merge base 2026 this PR
aca_ptc $170.0219bn $170.0219bn $144.7502bn $144.7502bn
used_aca_ptc $60.2158bn $60.2158bn $47.0997bn $47.0997bn
selected_marketplace_plan_premium_proxy $79.4978bn $79.4978bn $100.7288bn $100.7288bn
marketplace_net_premium $19.2820bn $19.2820bn $53.6291bn $53.6291bn
slcsp $352.3184bn $352.4290bn $444.5247bn $444.6575bn
pays_aca_premium (people) 52.7722m 52.7994m 53.4732m 53.5006m

slcsp rises by $0.11bn in 2025 and $0.13bn in 2026, and about 27,000 more people pay an age-rated premium (27,200 in 2025, 27,400 in 2026). Pavel notes that this agrees with the covered-child ranking, under which an older child who isn't enrolled no longer takes one of the three child-premium slots; he didn't break the difference down by cause. The credit totals (aca_ptc, used_aca_ptc) and the premium totals don't move at this precision.

The default dataset sets the dependency inputs for nobody (follow-up 3) and has no column for pays_aca_premium, selected_marketplace_plan_premium_proxy, monthly_selected_marketplace_plan_premium_proxy or is_aca_marketplace_enrollee, so neither the claimable-filer rules nor the two supplied-input rules move it. The commits after 18385c186b weren't rerun; they add no dataset input and their formula changes compute the same values for it.

Follow-ups (separate work)

  1. Alaska and Hawaii households given a single year get the contiguous-US ACA poverty line, because the guideline lookup reads the prior year's state. Main's tax_unit_fpg(period.last_year) does the same, and this PR keeps that read so it changes nothing else. Use current ACA household with prior-year poverty guidelines #10413 (issue ACA applicable percentage uses the contiguous-states poverty guideline for Alaska and Hawaii #9482) fixes this in aca_magi_fraction; whichever lands second should move the fix into aca_fpg, so California's two direct reads get it too.
  2. Basic Health Program: the payment proxy's reference premium should cover only the hypothetical PTC coverage family, since the payment equals "95 percent of the premium tax credit for which the eligible individual would have qualified" (42 CFR 600.605(a)(1)). BHP eligibility also reads medicaid_income_level where 42 CFR 600.305 uses household income for "the applicable family size".
  3. The default dataset sets the dependency inputs for nobody, so none of the claimable-filer rules move a microsimulation. Setting them belongs in Microcosm.
  4. On ordinary returns, aca_magi (through medicaid_magi) adds dependents' tax-exempt interest and all their Social Security, but 1.36B-1(e)(1)(ii) counts another family member's income only if they "are required to file a return". Main does the same, and this PR removes only out-of-family dependents' amounts.
  5. Generic federal AGI role inference still treats a minor without an explicit spouse role as a tax-unit dependent, excluding their earnings. ACA filer inference here completes family membership and MAGI-addition retention; it does not reconstruct IRS AGI, deductions or loss limits. A separate generic-role correction should compare inferred 18/17 and 17/16 joint returns against explicit-role controls and preserve supplied AGI/MAGI precedence.
  6. Open drafts Treat income exactly at 400% FPL as premium tax credit eligible #10312, Decide Vermont Premium Assistance eligibility on the exact FPL ratio #10313, Decide Washington Cascade Care Savings eligibility on the exact FPL ratio #10314, Decide Covered Connecticut eligibility on the exact FPL ratio #10316, Decide Colorado Premium Assistance's 400% ceiling on the exact FPL ratio #10318, Decide Maryland Premium Assistance's 400% ceiling on the exact FPL ratio #10320 and Decide Marketplace CSR eligibility and tiers on the exact FPL ratio #10326 test the exact ratio over tax_unit_fpg(period.last_year). Whichever lands second should divide by aca_fpg, so each program uses the poverty line its credit uses (invariant 4).

axiom: us:statutes/26/36B/c/1/D queued (TheAxiomFoundation/rulespec-us#1627)

🤖 Generated with Claude Code

MaxGhenis and others added 12 commits October 8, 2026 11:03
The federal standard deduction and 17 state formulas read only whether the
tax-unit head is claimed elsewhere, so a joint return where one spouse can be
claimed was taxed differently depending on which spouse was labelled head.
Each formula now applies its jurisdiction's rule for that case (either spouse,
single filers only, per spouse, or only when every filer is claimed), using
two new label-independent helpers. Adds swap-pair YAML cases and a Hypothesis
property test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… barred

The New Mexico, Maine and Hawaii renters gates counted filers who are not
claimed elsewhere, so a tax unit with neither a head nor a spouse (252 in
the default dataset) looked like one whose every filer was claimed. A new
helper, every_filer_is_dependent_elsewhere, requires a claimed filer and no
unclaimed one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ss EITC

- Hawaii's dependent standard deduction uses earned income net of the
  deductible part of self-employment tax, as the N-11 worksheet does.
- New Mexico's medical care credit needs a filer who is both 65 or older
  and not a dependent when a filer is a dependent elsewhere (NMSA 7-2-18.13).
- The federal EITC without a qualifying child requires that no filer can be
  claimed as a dependent (IRC 32(c)(1)(A)(ii)(III), Publication 596), so
  Missouri's working family credit no longer reaches such a couple.
- Hawaii's food/excise credit keeps minor children receiving public support
  (HRS 235-55.85(c)) on a joint return where one spouse can be claimed.
- Comments label the readings the law does not settle, and the property
  test covers the EITC and the food/excise exemption amount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… meaning

- The federal and Oregon dependent worksheets use earned income net of the
  deductible part of self-employment tax, as Hawaii's now does.
- Hawaii's food/excise minor-child count excludes children who can be
  claimed by another taxpayer and the filers themselves (Form N-311 line 3).
- claimed_as_dependent_on_another_return documents what it stands for: a
  §151 deduction for the person is allowable to another taxpayer, so a
  person whose would-be claimer files only for a refund is not one
  (Publications 501 and 596); rules on actually being claimed read it too.
- Hawaii's renters comment labels the mixed joint reading as an inference;
  the Hawaii dependent threshold cites HRS 235-2.4(a)(3) on its page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A filer who can be claimed on another return is treated as having no
dependents (IRC 152(b)(1); on a joint return either spouse counts) and has no
personal exemption (IRC 151(d)(2)); one who is claimed gets no education
credits (25A(g)(3)), student loan interest deduction (221(c)) or recovery
rebate (6428(d)(2), 6428A(d)(2), 6428B(c)(2)). The model read none of these.
Now the child tax credit and credit for other dependents, the under-13 child
and dependent care qualifying person, exemptions_count and its RI, DE, OH, OR,
WI, HI and NM readers, the recovery rebates, the education credits and the
student loan interest deduction apply them symmetrically for joint returns.
Georgia keeps its own counts; the HI, WI and Treasury exemption reforms
follow the corrected count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it family

IRC 36B(c)(1)(D) excludes anyone another taxpayer can claim from being an
applicable taxpayer, and 26 CFR 1.36B-1(d)(2) leaves such a spouse out of a
joint return's family. Add is_aca_tax_family_member and aca_tax_family_size,
measure household income against the poverty line for that family (aca_fpg,
aca_magi_fraction), gate is_aca_ptc_eligible and the below-FPL immigration
exception on it, and price the credit on the coverage family's benchmark
(aca_ptc_slcsp, which equals slcsp whenever every enrollee is in the family).
The NY/VT family-tier logic becomes shared functions so the benchmark can
apply it to the coverage family. The three ACA reforms that override
is_aca_ptc_eligible keep the gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e tax family

Massachusetts, Connecticut, Colorado, Maryland, California, New Mexico and
Vermont require each enrollee to be eligible for the federal credit, so the
gate flows through is_aca_ptc_eligible and their wraps use the coverage
family's benchmark. New Mexico's Middle Income Household component and New
Jersey Health Plan Savings require every federal criterion except income, so
they now read the tax family too. Washington's Cascade Care Savings pays its
non-federally-subsidized amount (Group 2) to a QHP-eligible enrollee without
a federal subsidy, so a claimable enrollee moves from Group 1 to Group 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 12 commits October 10, 2026 09:35
- Child and dependent care: childcare expenses do not count on a return with a
  claimable filer unless a child under 13 still qualifies (IRC 21(b)(2)(A)(ii)).
- Tuition and fees deduction: not allowed to a filer who can be claimed (IRC
  222(c)(3)), so the education credit bar no longer makes such a filer eligible.
- Recovery rebates: the Armed Forces exception waives the identification rule
  only, not the exclusion of a spouse who is another taxpayer's dependent.
- Georgia: the contributed refundable credit and SB 520 reforms count the same
  children as ga_ctc_potential through a new ga_ctc_eligible_child.
- Education credits and student loan interest read the claimed input directly,
  since IRC 25A(g)(3) and 221(c) turn on an actual claim.
- One reference per source document; quote IRC 24(h)(4)(A) exactly.
- Tests: mixed-recipient care, tuition deduction, military rebates, Georgia
  refund cap, and the swapped New Mexico rebate case; the property test now
  covers the state readers of the exemption count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	policyengine_us/tests/policy/baseline/gov/states/oh/tax/income/exemptions/personal/oh_personal_exemptions_eligible.yaml
#10024 now separates three facts: is claimed, can be claimed, and the would-be
claimant's filing exception.

- IRC 152(b)(1) gates (child tax credit, credit for other dependents, under-13
  care child and its expenses, dependents in exemptions_count, ARPA dependents,
  Ohio dependents, the Hawaii and Wisconsin reforms) read
  head_or_spouse_is_dependent_elsewhere_without_filing_exception, so a filer
  within the Publication 501 exception keeps their dependents.
- Own exemptions, recovery rebates and the tuition deduction read claimability.
- Education credits and student loan interest keep reading the claimed input.
- Tests: the filing exception and a filer who can be claimed but is not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#10024's food/excise credit, Act 115 rebate and renters credit took claimable
filers off exemptions_count themselves. exemptions_count now leaves them out
(and, outside the filing exception, the return's dependents), so those credits
read it as it is; the filing-exception cases were one exemption short.

The renters credit also leaves out a dependent who can be claimed by another
taxpayer (Schedule X counts only people who cannot be), as the food/excise
credit already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Education credits: a dependent student is not eligible on a return whose
  filer can be claimed outside the filing exception (IRC 25A(f)(1)(A)(iii),
  152(b)(1)); the filer's own eligibility still turns on an actual claim.
- Recovery rebates: no credit when every filer is a dependent of another
  taxpayer (IRC 6428B(a)); the CARES and CAA child prerequisite is a spouse
  with a valid SSN, whether or not that spouse is an eligible individual.
- Child and dependent care: cdcc_qualifying_childcare_expenses holds the
  expense rule, and the single-parent work requirement reform and California's
  expenses read it.
- References: Georgia HB 136 page 3; one entry per recovery rebate statute;
  quote IRC 24(h)(4)(A) exactly in the test comment.
- Tests: dependent students, the two rebate cases, the reform and California
  expenses, and the Hawaii, Wisconsin and Treasury reforms with a claimable
  filer under both labellings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Education credits: is_barred_from_education_credits_by_dependency holds the
  claimed-filer and dependent-student rules; both eligibility formulas and the
  reconciliation SSN reform's Lifetime Learning Credit read it, so the reform
  no longer turns a barred student's tuition into that credit.
- Child and dependent care: childcare counts when any child under 18 is still a
  qualifying individual, so an incapacitated child aged 13 to 17 keeps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Setting meets_eitc_identification_requirements on one person made it an input
for every person, so the other spouse lost their SSN too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…role-less units

- is_aca_tax_family_member reads claimable_as_dependent_on_another_return
  (#10024's split input: 36B(c)(1)(D) and 1.36B-1(d)(2) turn on being
  claimable, and Form 1040 line 12a, which Form 8962 line 1 keys on, has no
  refund-only carve-out). The return keeps its dependents when the would-be
  claimant meets the refund-only filing exception (Pub 501, Dependent
  Taxpayer Test), a return with no applicable taxpayer has no family, and a
  tax unit with neither a head nor a spouse uses each member's own
  claimability (a lone claimable minor no longer gets the credit).
- aca_magi leaves out the tax-exempt interest and Social Security of the
  return's dependents who are outside the tax family (26 CFR 1.36B-1(e)(1)).
- used_aca_ptc is capped at the coverage family's share of the selected plan
  premium (new aca_ptc_coverage_family_premium; 26 CFR 1.36B-3(d)(1)(i),
  (h)(1)), not the whole policy's premium.
- marketplace_csr_eligible applies 45 CFR 155.305(g)(3): the model prices one
  policy per tax unit, and a policy that also covers an enrollee without
  credit eligibility gets no cost-sharing reductions.
- aca_fpg documents that it sizes the family for the tax year; the NJHPS
  exclusion is labelled an interpretation; one reference per source in
  is_aca_coverage_family_member.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
YAML: refund-only exception (family keeps the child), single claimable filer
with the exception (no family), claimable but not claimed, lone claimable
and unclaimed 17-year-old filers, household income without an out-of-family
child's tax-exempt interest, the coverage-family premium cap (silver and
bronze), CSR under the family-policy rule, Colorado's binding residual
($420), and the tax year's family size against a supplied prior-year size.
Test inputs now use claimable_as_dependent_on_another_return.

Property test: Hypothesis runs every year 2021-2026 and draws claimable-but-
unclaimed spouses and the filing exception; the credit's monotonicity
exemption is limited to crossing the 100% floor or dependents' income leaving
household income; the benchmark check compares enrolled people, not counts;
new identities for household income, the premium cap and CSR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cation, MAGI floor

- A tax unit with neither a head nor a spouse is filed by its oldest member.
  When that member can be claimed elsewhere, nobody is in the tax family.
  Before, a claimable 17-year-old filing with an infant got $12,000.
- The coverage family is priced on its own. Its children are ranked for the
  three-oldest-children rule (45 CFR 147.102(c)(1)), and its family tier is
  built from its own members.
- A shared premium is allocated by each family's benchmark (26 CFR
  1.36B-3(h)(1)). The denominator is the coverage family's benchmark plus the
  benchmark of the enrollees outside it, not the whole policy's tier. An empty
  coverage family gets no share of the premium.
- Out-of-family dependents' tax-exempt interest and Social Security are
  removed before the single zero floor. A negative benefit no longer cuts
  household income.
- is_aca_marketplace_enrollee (enrolled) is split from pays_aca_premium
  (charged an age-rated premium). The family-tier child tests no longer
  depend on payment.
- Tests: YAML for each case under both head/spouse labellings. The property
  test now gives dependents tax-exempt interest and Social Security. It checks
  aca_magi against an expectation built from the inputs, and it exempts only
  that removal from monotonicity. Monotonicity runs under Hypothesis in every
  year 2021-2026. The "unchanged" identities apply only to returns with a
  head or spouse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from claimable-filer-federal to main October 16, 2026 06:10
DTrim99 and others added 2 commits November 12, 2026 14:27
The reform came from main (#10681) in the last merge and still read the
whole tax unit's benchmark (slcsp) and selected premium. It now reads the
coverage family's benchmark (aca_ptc_slcsp) and its share of the premium
(aca_ptc_coverage_family_premium), as the baseline credit does, so an
enrollee who can be claimed on another return stays out of the credit.

The reform's tests state enrollment (is_aca_marketplace_enrollee), as this
branch's used_aca_ptc tests do, and a new case covers a claimable spouse:
$5,016.40, the baseline credit, where the reform gave $11,016.40.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the conflict in is_aca_ptc_eligible.yaml by keeping both sides'
new cases: this branch's claimable-filer cases and main's permanent
resident barred from Medicare at 70 (#10632).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DTrim99 and others added 2 commits November 12, 2026 18:34
Main now has #10773, which guarded aca_magi_fraction's prior-year guideline
for the first modeled year. This branch reads aca_fpg there, so the guard
moves into aca_fpg (guideline year and state group). The aca_magi_fraction
and is_aca_ptc_eligible tests keep both sides' cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

@PavelMakarchuk, thanks for the review. This push fixes the Critical item and does suggestion 6. The other items are still open.

Critical: the minimum premium reform now uses the coverage family (0675e97)

policyengine_us/reforms/aca/aca_ptc_minimum_premium.py, as you suggested:

  • aca_ptc_before_filing_gate takes the benchmark from aca_ptc_slcsp in place of slcsp (26 CFR 1.36B-3(d)(1) and (f)).
  • enrolled_premium is the larger of that benchmark and aca_ptc_coverage_family_premium, in place of selected_marketplace_plan_premium_proxy.
  • The reform's used_aca_ptc caps the credit at aca_ptc_coverage_family_premium less the minimum premium.
  • Both variables' documentation now says this.

policyengine_us/tests/policy/contrib/aca/ptc_minimum_premium.yaml:

  • New case "A claimable spouse's benchmark and premium stay out of the credit". It is your witness with the reform on: a 2026 Texas joint return whose head can be claimed elsewhere.
    • aca_tax_family_size is 1 and aca_ptc_slcsp is 6,000.
    • aca_ptc is 6,000 - 0.0419 × 23,475 = 5,016.40, where it was 11,016.40.
    • aca_ptc_coverage_family_premium is 6,000 (half of the 12,000 policy), so used_aca_ptc is also 5,016.40. The 6,000 - 60 cap doesn't bind.
  • The seven existing cases now set is_aca_marketplace_enrollee: true. aca_ptc_coverage_family_premium is 0 unless a tax-family member enrolls, and these cases had relied on the premium proxy. Their expected values are unchanged.
  • Before the merge below, all 10 cases passed on Modal, as did the other ACA contrib reform tests and used_aca_ptc.yaml.

Suggestion 6: the first-modeled-year guard is in aca_fpg now

Main now includes #10773, so I merged main again. This branch's aca_magi_fraction reads aca_fpg, so the merge keeps that read, and #10773's guard moves into aca_fpg.py:

  • The guideline year is period.last_year, except in the first modeled year (2015), where the tax year's guideline stands in.
  • The state group is read for the guideline year, and year_lag follows from it, so California's two direct reads of aca_fpg get the guard too.
  • The documentation and a comment note that for 2015 coverage this uses a later guideline than 36B(d)(3)(B) calls for.

Tests from the merge:

I haven't rerun tests since this merge, so CI is the check. If it fails, I'll fix it on this branch.

Still open

Should address 1–7, suggestions 1–5 and the five items from my 11/7 review are untouched. They're for Max.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST_CHANGES (head b7841c8935)

The push fixes the Critical item from my first review, and the first-modeled-year guard from #10773 is kept with the same effect. But the merge with main turns Should address 1 from that review into a CI failure: four tests that #10773 added fail at this head and pass on main. Should address 2 to 7 are unchanged.

Round-1 items

Item Status
Critical: the minimum premium reform bypassed the coverage-family benchmark and premium Resolved (0675e97286). aca_ptc is 5,016.40 with the reform on, where it was 11,016.40. One test gap remains (Should address 1 below).
Should 1: a supplied pays_aca_premium no longer implies enrollment Not resolved, now Critical. It causes the failing CI check (see Critical).
Should 2: stale TheAxiomFoundation/rulespec-us#1627 Not resolved. The issue is open, last updated 2026-10-10.
Should 3: source-conflict register Not resolved. No change to source_conflicts/candidates.yaml.
Should 4: California reference pages Not resolved. The three files are unchanged.
Should 5: helpers and the private Core call Not resolved. slcsp_age_curve_amount_person_uncapped.py:24-27 and is_aca_tax_family_member.py:44-45 are unchanged; the PR body still names the two unused helpers.
Should 6: three unpinned branches Not resolved. No test for those branches changed.
Should 7: microsimulation impact "Pending" Not resolved. The PR body still says "Pending".
Suggestions 1 to 5 Not addressed. The reply says they are left for the PR author.
Suggestion 6: move the #10773 guard into aca_fpg.py Resolved. Same results as main for 2015, 2016 and 2017.

Critical

Four household net income tests from #10773 fail at this head. This PR causes the failure.

  • Check: "Baseline tests / Full Suite - Baseline (household)", completed 2026-11-12 6:48pm EST. I reproduced it: policyengine_us/tests/policy/baseline/household/income/ gives 4 failed, 220 passed at this head. The same four cases pass on main (ca7a470271).

    Test file Case Result at this head
    household/income/household/household_health_costs.yaml:26 "The premium tax credit doesn't lower household health costs ..." marketplace_net_premium 5,000, expected 4,000
    household/income/household/household_net_income.yaml:206 "... a $1,000 premium tax credit against a $5,000 premium." premium_tax_credit_in_force 0, expected 1,000
    household/income/household/household_net_income.yaml:224 "... a $2,000 premium tax credit against the same premium." premium_tax_credit_in_force 0, expected 2,000
    household/income/household/household_refundable_tax_credits.yaml:28 "Case 3, the credit counted is limited to the premium of the plan ..." used_aca_ptc 0, expected 800
  • Cause: each case supplies aca_ptc and selected_marketplace_plan_premium_proxy for a default household. aca_ptc_coverage_family_premium.py:32-34 and :65-69 return $0 unless is_aca_marketplace_enrollee is true for a tax-family member. For the default household that variable computes to false (the person is Medicaid-eligible). So used_aca_ptc is $0, and #10773's premium_tax_credit_in_force (which adds used_aca_ptc) drops the credit from household net income.

  • With is_aca_marketplace_enrollee: true added to the Case 3 inputs, used_aca_ptc is 800 again. That confirms the cause.

  • This is the behavior in Should address 1 of my first review. The witness there still holds: 2026, New Mexico, one adult aged 30, pays_aca_premium: true, slcsp_age_curve_multiplier: 1, aca_magi: 23_475, slcsp_age_0: 6_000 gives used_aca_ptc 5,016.40 on main and 0 at this head.

  • The effect is larger since #10773: a caller who supplies a premium or pays_aca_premium without is_aca_marketplace_enrollee now loses the credit in household_refundable_tax_credits and household_net_income, not only in marketplace_net_premium.

  • Fix, one of:

    1. Let a supplied premium or a supplied pays_aca_premium: true count as enrollment (for example with has_input_for_period in is_aca_marketplace_enrollee). The four tests then pass unchanged. I recommend this one: the PR has already added is_aca_marketplace_enrollee to the three New Mexico MIH fixtures and to seven minimum premium cases for the same reason.
    2. Add is_aca_marketplace_enrollee: true to the four cases, and state the new input contract in the changelog and in the documentation of used_aca_ptc.

Should address

  1. The new reform case does not pin two of the three changed lines. In "A claimable spouse's benchmark and premium stay out of the credit" (policyengine_us/tests/policy/contrib/aca/ptc_minimum_premium.yaml:202), the cap does not bind. With the old reads at aca_ptc_minimum_premium.py:58 and :76 (whole-policy premium 12,000), the case still gives 5,016.40 (min(5,016.40, 12,000 - 60)). Add the same household with aca_required_contribution_percentage: 0: I get aca_ptc 5,940 and used_aca_ptc 5,940 at this head, and the old reads give 6,000 for both.
  2. Round-1 Should address 2 to 7 are open and unchanged. See my first review for the details: the stale axiom issue, the source-conflict register, the California reference pages, the helpers and the private Core call, the three unpinned branches, and the "Pending" microsimulation impact.

Suggestions

  1. Round-1 suggestions 1 to 5 are unchanged.
  2. Not caused by this PR, and the same on main: aca_fpg.py:43 reads state_group_str for the guideline year. When the state is supplied only for the tax year, the prior year falls back to the default state group. A 2026 Alaska filer with $30,000 of MAGI gets aca_magi_fraction 1.91 (the contiguous guideline, 15,650) on main and at this head; the 2025 Alaska guideline (19,550) gives 1.53. Reading the state group for period would fix it. A separate issue is enough.

What I verified

Minimum premium reform (policyengine_us/reforms/aca/aca_ptc_minimum_premium.py)

  • I read the file at this head. The three reads now match the baseline aca_ptc_before_filing_gate and used_aca_ptc at this head, plus the minimum.

  • 2026 joint return with one claimable spouse, reform on:

    Output Minimum does not bind Minimum binds (contribution 0)
    aca_tax_family_size 1 1
    aca_ptc_slcsp 6,000 6,000
    aca_ptc_coverage_family_premium 6,000 6,000
    aca_ptc 5,016.40 5,940
    used_aca_ptc 5,016.40 5,940
    marketplace_net_premium (whole 12,000 policy) 6,983.60 6,060
  • Hand check of the new case: 6,000 - 0.0419 x 23,475 = 5,016.40; the coverage family's share is 12,000 x 6,000 / 12,000 = 6,000; 5,016.40 is below 6,000 - 60.

  • Ordinary households, reform on, this head against main: identical aca_ptc, used_aca_ptc and marketplace_net_premium for the same joint return with no claimable spouse (11,507.03; 11,940 when the minimum binds) and for five computed households (Texas, Florida and Pennsylvania; one adult, or one adult with one or two children).

aca_fpg.py and the merge with #10773

  • The guard is the same logic as main's aca_magi_fraction.py. For $30,000 of MAGI, aca_magi_fraction at this head equals main in every case I ran: 2015 (Texas 2.54, Alaska 2.03, Hawaii 2.21), 2016 (2.54) and 2017 (2.52). In 2015 aca_fpg is 11,770, the 2015 guideline.
  • California's ca_premium_subsidy_eligible and ca_premium_subsidy_applicable_percentage compute for 2015, 2016, 2021 and 2026 without error, with the same values as main for a single adult earning $30,000.
  • aca_magi_fraction.yaml, is_aca_ptc_eligible.yaml and ptc_minimum_premium.yaml lose no line of main's version. All other files of #10773 are identical to main, except aca_magi_fraction.py (reads aca_fpg, as intended) and the changelog fragment that main's version bump consumed.
  • The credit counts once. premium_tax_credit_in_force adds used_aca_ptc, and household_health_costs subtracts the whole premium. For the claimable-spouse return, a $1,000 rise in MAGI lowers used_aca_ptc by 112.68 and household_net_income by 112.68, with health benefits counted or not. household_refundable_tax_credits is 5,016.40, equal to used_aca_ptc.

Tests run at this head (Python 3.13, policyengine-core 3.34.10)

Tests Result
policyengine_us/tests/policy/baseline/gov/aca/ and policyengine_us/tests/policy/contrib/aca/ 567 passed
The nine state premium program directories (CA, CO, CT, MA, MD, NJ, NM, VT, WA) 624 passed
policyengine_us/tests/policy/baseline/household/income/ 4 failed, 220 passed (on main, household/income/household/: 83 passed)
tests/core/test_claimable_filer_ptc_invariants.py and the three tests/unit/test_ca_premium_subsidy_*.py files 34 passed
policyengine_us/tests/policy/baseline/partners/ 623 passed
  • Partner contract tests: the PR edits none of them, and all pass.
  • CI at this head: 26 checks passed, 1 failed (the household shard above), 1 skipped ("Contrib tests").
  • Mergeability: a trial merge with main at ca7a470271 has no conflict.
  • The changelog fragment changelog.d/claimable-filer-ptc.fixed.md is unchanged and at the top level.

Not verified

  • Population impact (no microsimulation run).
  • The sources that I could not open in the first review.
  • I did not run a mutation of aca_ptc_minimum_premium.py:58 and :76; Should address 1 rests on the arithmetic above.

A supplied pays_aca_premium of true marks an enrollee, since everyone who
pays an age-rated premium is one. A supplied premium for a tax unit in which
the model finds no enrollee, with enrollment not supplied, is the tax
family's, as before enrollment was modeled. This restores #10773's four
household tests unchanged. The minimum premium reform gets a case in which
the cap binds, so the coverage family's premium is pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

@PavelMakarchuk, thanks. 18385c1 takes your option 1 for the Critical item and adds the case from Should address 1.

Critical: supplied inputs count as enrollment again

  • is_aca_marketplace_enrollee: a supplied pays_aca_premium of true now marks an enrollee, since pays_aca_premium is this variable restricted to those who pay an age-rated premium. Only a supplied value is read (has_input_for_period), because pays_aca_premium's formula reads this variable. This covers your round-1 witness.
  • aca_ptc_coverage_family_premium: a supplied premium for a tax unit in which the model finds no enrollee is the tax family's, as it was before enrollment was modeled. This applies only when enrollment isn't supplied and the tax unit has a tax-family member, so "r3 no Marketplace enrollees receive none of a supplied annual premium" still gives 0.
  • I didn't make a supplied premium mark people as enrollees. That would add Medicaid-eligible members to slcsp through pays_aca_premium and change computed credits.
  • Count the premium tax credit in household net income as a refundable credit #10773's four household tests are unchanged and should pass again.
  • New cases:
    • used_aca_ptc.yaml: your New Mexico witness. is_aca_marketplace_enrollee is true and used_aca_ptc is 5,016.40.
    • aca_ptc_coverage_family_premium.yaml: a supplied $5,000 premium for the default household, which computes as no enrollee, is the tax family's $5,000.

Should address 1: the cap now binds in a reform case

ptc_minimum_premium.yaml adds the claimable-spouse household with aca_required_contribution_percentage: 0, expecting:

  • aca_ptc and used_aca_ptc of 5,940 (6,000 - 60);
  • aca_ptc_coverage_family_premium of 6,000.

The whole-policy reads would give 6,000.

Not in this push

  • Suggestion 2 (Alaska and Hawaii state group in aca_fpg): Agreed. It's the same on main, so it can go in a separate issue. The ACA calculator works around it by setting the state for both years.
  • Round-1 Should address 2–7 and suggestions 1–5: still Max's.

As with the last push, I haven't run tests locally, so CI is the check.

@DTrim99

DTrim99 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

@PavelMakarchuk, your suggestion 2 (the Alaska and Hawaii state group in aca_fpg) is already tracked in #9482, opened in September from the same symptom in the API. I've added the cause and the fix there.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST_CHANGES (head 18385c186b)

The push fixes the Critical item from my last review: the four household tests pass again, CI is green, and a supplied pays_aca_premium or a supplied premium gives the same credit as main. No Critical item is open. The verdict stays REQUEST_CHANGES because of test gaps (one line of the reform, and the three conditions of the new premium rule), one gap in the new rule for monthly premiums, and round-1 Should address 2 to 7, which are unchanged.

Resolved since the last review

Item Status
Critical: four household tests from #10773 failed Resolved (18385c186b). policyengine_us/tests/policy/baseline/household/income/ gives 224 passed, 0 failed. The four cases at household_health_costs.yaml:26, household_net_income.yaml:206 and :224, and household_refundable_tax_credits.yaml:28 pass unchanged.
Round-1 Should 1: a supplied pays_aca_premium no longer implied enrollment Resolved. The New Mexico witness (2026, one adult aged 30, pays_aca_premium: true, slcsp_age_curve_multiplier: 1, aca_magi: 23_475, slcsp_age_0: 6_000) gives is_aca_marketplace_enrollee true, used_aca_ptc 5,016.40 and marketplace_net_premium 983.60, the same as main. The case is now in used_aca_ptc.yaml:164.
Round-2 Should 1: the reform case did not pin aca_ptc_minimum_premium.py:58 and :76 Partly resolved. The new case pins line 58. Line 76 is still not pinned (Should address 1).
Round-1 Should 2 to 7 Not resolved (Should address 4).
Suggestion 2 of the last review (state group in aca_fpg) Tracked in #9482, as the reply says. No action needed here.

Should address

  1. The reform's used_aca_ptc read at policyengine_us/reforms/aca/aca_ptc_minimum_premium.py:76 is still not pinned.

    • I changed one line at a time to the old whole-policy read and ran policyengine_us/tests/policy/contrib/aca/ptc_minimum_premium.yaml:

      Line changed back Result
      :52 (aca_ptc_slcsp to slcsp) 2 failed, 9 passed
      :58 (enrolled_premium) 1 failed, 10 passed
      :76 (used_aca_ptc) 11 passed
    • Cause: in the new case at ptc_minimum_premium.yaml:255, aca_ptc is already capped at 5,940 by line 58. With the whole-policy read at line 76, used_aca_ptc is min(5,940, 12,000 - 60) = 5,940, the same value. My last review gave 6,000 for the old reads; that holds only when both lines are changed together. That was my error in the witness.

    • A case that pins line 76: the same household (2026 Texas joint return, head claimable elsewhere, aca_magi: 23_475, aca_required_contribution_percentage: 0, slcsp_age_0: 6000) with selected_marketplace_plan_premium_proxy: 8_000. At this head I get aca_ptc_coverage_family_premium 4,000, aca_ptc 5,940 and used_aca_ptc 3,940 (4,000 - 60). With the old read at line 76, used_aca_ptc is 5,940 and the case fails.

  2. No test pins the three conditions of the new supplied-premium rule (policyengine_us/variables/gov/aca/ptc/aca_ptc_coverage_family_premium.py:43-48).

    The reply says the rule applies only when enrollment is not supplied and the tax unit has a tax-family member. I removed each condition in turn and ran the ACA, ACA contrib and household income directories. All 794 tests pass each time.

    Condition removed Tests A household that the condition protects (2026, Texas, selected_marketplace_plan_premium_proxy: 5_000, aca_ptc: 1_000) This head Without the condition
    not has_input_for_period(sim, "is_aca_marketplace_enrollee", period) (:45) 794 passed One adult aged 40 with is_aca_marketplace_enrollee: false used_aca_ptc 0 1,000
    ~tax_unit.any(enrolled) (:47) 794 passed Joint return. The head can be claimed elsewhere and is the only enrollee; the spouse has is_aca_eshi_eligible: true used_aca_ptc 0, marketplace_net_premium 5,000 1,000 and 4,000
    tax_unit.any(member) (:47) 794 passed One adult aged 30, claimable elsewhere, is_medicaid_eligible: true used_aca_ptc 0 1,000

    The existing case "r3 no Marketplace enrollees receive none of a supplied annual premium" (aca_ptc_coverage_family_premium.yaml:525) supplies enrollment and has no tax-family member, so two conditions protect it at the same time and it pins neither. Add the three households above to aca_ptc_coverage_family_premium.yaml.

  3. The new rule reads only the annual premium input, so monthly premiums get $0.

    • aca_ptc_coverage_family_premium.py:43-45 tests has_input_for_period(sim, "selected_marketplace_plan_premium_proxy", period). This PR adds monthly_selected_marketplace_plan_premium_proxy as the monthly input for the same premium. A caller who supplies the premium by month does not reach the rule.

    • Witness: 2026, California, one adult aged 40 with no other inputs (the model computes Medicaid eligibility, so no enrollee), aca_ptc: 1_000.

      Premium input selected_marketplace_plan_premium_proxy aca_ptc_coverage_family_premium used_aca_ptc marketplace_net_premium
      Annual, 5,000 5,000 5,000 1,000 4,000
      Monthly, 500 in each month 6,000 0 0 6,000
    • Fix: also treat a supplied monthly premium as a supplied premium (for example, test has_input_for_period for any month of the year), or state in the documentation of aca_ptc_coverage_family_premium that the rule applies to the annual input only. Add the case.

    • The monthly variable is new in this PR, so no existing caller changes.

  4. Round-1 Should address 2 to 7 are open. The push changes none of the files they concern. I checked each again at this head:

    • 2, axiom issue: TheAxiomFoundation/rulespec-us#1627 is open with the pe-parity label and was last updated on 2026-10-10. It predates the refund-only exception and the later rules in this PR.
    • 3, source-conflict register: source_conflicts/candidates.yaml (us-co:premium-assistance-ptc-recipients) still has code: [] at line 2085 and url: null at lines 2074 and 2077.
    • 4, California reference pages: ca_premium_subsidy_eligible.py:14 and ca_premium_subsidy.py:23 still point to #page=1; ca_premium_subsidy_applicable_percentage.py:15-16 to #page=2. No reference reaches PDF page 3.
    • 5, helpers: slcsp_age_curve_amount_person_uncapped.py:24-27 still calls the private simulation._get_set_input_periods with a fallback for older Core releases. This push uses has_input_for_period in two more places, so the helper is the pattern of the PR. is_aca_tax_family_member.py:44-45 still computes the exception inline. The PR body (line 17) still names two helpers that no ACA formula reads.
    • 6, three unpinned branches: marketplace_csr_eligible.py:27-31, the even-split fallback at aca_ptc_coverage_family_premium.py:64, and is_aca_filer.py:32. I changed each branch in turn (the where to the before-premium test only; the fallback to zero; the True fallback to ~dependent) and ran the ACA, ACA contrib and nine state directories: 1,194 passed each time, so no test pins any of the three.
    • 7, microsimulation impact: the PR body still says "Pending". I ran the comparison below; the body can use it or its own run.

Suggestions

  1. changelog.d/claimable-filer-ptc.fixed.md says "with zero allocation for empty enrollment". After this push a supplied premium with no enrollee found goes to the tax family. Add a sentence for the two supplied-input rules (pays_aca_premium and the premium).
  2. Round-1 suggestions 1 to 5 are unchanged. The push changes none of the files they concern.

What I checked

The new rules, by household (2026; this head against main at a93cc58d7e)

Household is_aca_marketplace_enrollee aca_ptc used_aca_ptc Same as main
New Mexico witness, pays_aca_premium: true true 5,016.40 5,016.40 yes
Adult aged 67, pays_aca_premium: true, is_medicare_eligible: true true 5,016.40 5,016.40 yes
Adult aged 30, pays_aca_premium: true, is_aca_eshi_eligible: true true 5,016.40 5,016.40 yes
Couple, pays_aca_premium: true for one spouse only, aca_magi: 31_725 true, true 4,670.72 4,670.72 yes
Adult, pays_aca_premium: false, computed as enrolled true 0 0 yes
Adult, pays_aca_premium: true, claimable elsewhere true 0 0 no (intended: this is the rule of the PR)
Default adult, premium 5,000, aca_ptc: 1_000 false 1,000 1,000 yes
Adult with Medicare, premium 5,000, aca_ptc: 1_000 false 1,000 1,000 yes
Adult with an employer offer, premium 5,000, computed credit false 0 0 yes
Adult computed as enrolled, premium supplied as 0 true 5,016.40 0 yes
Adult claimable elsewhere, premium 5,000, aca_ptc: 1_000 false 1,000 0 no (intended)
  • A supplied pays_aca_premium: true for a person with Medicare or an employer offer gives a credit of 5,016.40. Main does the same, so the push does not cause it. The push only makes is_aca_marketplace_enrollee agree with the supplied value.
  • A supplied pays_aca_premium: false does not remove a computed enrollee. That is also the same as the last head.
  • A supplied premium does not make anyone an enrollee, so it adds no member to slcsp and creates no computed credit. For the default adult with only a premium supplied, aca_ptc and used_aca_ptc are 0.
  • has_input_for_period answers for the whole simulation. I found no household where this gives a wrong amount: a tax unit with no supplied premium has a premium of 0, and a person with no supplied pays_aca_premium has false.

Microsimulation

  • The default dataset has no column for pays_aca_premium, selected_marketplace_plan_premium_proxy, monthly_selected_marketplace_plan_premium_proxy or is_aca_marketplace_enrollee. Its ACA inputs are takes_up_aca_if_eligible and selected_marketplace_plan_benchmark_ratio. Its health premium columns (health_insurance_premiums and others) are not read by either rule.

  • In a Microsimulation on the default dataset, has_input_for_period is false for all four variables, before and after the calculation. So both new rules are inactive for population results: nobody becomes an enrollee or receives a credit because of an imputed premium.

  • I confirmed it with a run. Weighted 2026 totals are identical at this head and at the last head (b7841c8935): aca_ptc $144.7502bn, used_aca_ptc $47.0997bn, aca_ptc_coverage_family_premium $100.7288bn, 54.0744m enrollees.

  • For round-1 Should 7, the whole PR against its merge base (ac280b06ed), default dataset, one run each:

    Weighted total 2025 merge base 2025 this head 2026 merge base 2026 this head
    aca_ptc $170.0219bn $170.0219bn $144.7502bn $144.7502bn
    used_aca_ptc $60.2158bn $60.2158bn $47.0997bn $47.0997bn
    selected_marketplace_plan_premium_proxy $79.4978bn $79.4978bn $100.7288bn $100.7288bn
    marketplace_net_premium $19.2820bn $19.2820bn $53.6291bn $53.6291bn
    slcsp $352.3184bn $352.4290bn $444.5247bn $444.6575bn
    pays_aca_premium (people) 52.7722m 52.7994m 53.4732m 53.5006m

    The credit totals do not move at this precision. slcsp rises by about $0.11bn (2025) and $0.13bn (2026) and about 27,000 more people pay an age-rated premium; this agrees with the covered-child ranking change. I did not break the difference down by cause.

Since-prior diff, line by line

  • is_aca_marketplace_enrollee.py:57-63: the read of pays_aca_premium happens only for a supplied value, so there is no cycle with the formula of pays_aca_premium. The result is the union of the computed and the supplied value.
  • aca_ptc_coverage_family_premium.py:39-48 and :82: the Python boolean combines correctly with the two arrays. The first branch of the where is unchanged, so households with a covered enrollee get the same result as at the last head.
  • The three new YAML cases match my hand checks: 6,000 - 0.0419 x 23,475 = 5,016.40; 6,000 - 60 = 5,940.
  • Defects found: Should address 1 to 3 above.

Tests run at this head (Python 3.13, policyengine-core 3.34.10)

Tests Result
policyengine_us/tests/policy/baseline/household/income/ 224 passed
policyengine_us/tests/policy/baseline/gov/aca/ and policyengine_us/tests/policy/contrib/aca/ 570 passed
The nine state premium program directories (CA, CO, CT, MA, MD, NJ, NM, VT, WA) 624 passed
tests/core/test_claimable_filer_ptc_invariants.py and the three tests/unit/test_ca_premium_subsidy_*.py files 34 passed
policyengine_us/tests/policy/baseline/partners/ 623 passed
  • Partner contract tests: the PR edits none of them, and all pass.
  • CI at this head: 36 of 36 checks passed; the last one completed on 2026-11-12 at 9:41pm EST.
  • GitHub reports the PR as mergeable (no conflict with main).
  • The changelog fragment is at the top level of changelog.d/.

Not verified

  • I ran the tests on Python 3.13; CI uses 3.14. CI is green at this head.
  • The microsimulation runs are one local run per commit on the default dataset. I did not run the microsimulation test suite locally (the CI check passed), and I did not run any other dataset.
  • The sources that I could not open in the first review.
  • For Should address 2, I did not run the nine state directories with each condition removed.

… review

A supplied monthly premium now counts as a supplied premium. New cases pin
each condition of the rule (enrollment supplied, an enrollee found, a tax
family present), the monthly input, and the minimum premium reform's
used_aca_ptc cap. The changelog names the two supplied-input rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

@PavelMakarchuk, thanks. 6d336a2 covers Should address 1–3 and suggestion 1.

Should address 1: the reform's used_aca_ptc read. ptc_minimum_premium.yaml adds your household on an $8,000 policy, expecting:

  • aca_ptc_coverage_family_premium of 4,000;
  • aca_ptc of 5,940;
  • used_aca_ptc of 3,940 (4,000 - 60).

With the whole-policy read at line 76, the used credit would be 5,940.

Should address 2: the three conditions. aca_ptc_coverage_family_premium.yaml adds one case per condition. Each has a supplied $5,000 premium and a $1,000 credit in Texas:

  • Enrollment supplied as false gives used_aca_ptc of 0.
  • A joint return whose only enrollee is the claimable head, with an employer offer for the spouse, gives used_aca_ptc of 0 and marketplace_net_premium of 5,000. My case sets aca_magi: 30_000 and the coverage inputs, so the head computes as an enrollee.
  • A claimable single filer, so there is no tax family, gives used_aca_ptc of 0.

Should address 3: monthly premiums. The rule now treats a supplied monthly_selected_marketplace_plan_premium_proxy for any month of the year as a supplied premium, and the documentation says so. Your California witness, at $500 a month, is a new case: the coverage family's premium is 6,000, used_aca_ptc is 1,000 and marketplace_net_premium is 5,000.

Suggestion 1: the changelog now names both supplied-input rules.

Round-1 Should address 2–7 and suggestions 1–5 are still open and unchanged in this push. Thanks for running the microsimulation comparison for Should address 7.

As before, I didn't run these locally, so CI is the check.

Address the round-1 items that Pavel's 2026-11-13 review carried forward.

- New cases pin the even-split fallback for a supplied annual premium, the
  CSR test of a premium payer's supplied PTC eligibility, and is_aca_filer's
  fallback when every member is a supplied dependent.
- aca_ptc_coverage_family_premium returns early when no tax unit has an
  enrollee outside its tax family; the result is unchanged.
- slcsp_age_curve_amount_person_uncapped uses has_input_for_period, and
  is_aca_tax_family_member reads is_dependent_elsewhere_without_filing_exception.
- California references reach PDF page 3 for (d)(7)(B), (d)(10) and (d)(11).
- The Colorado source-conflict candidate gets its URLs, quotes and code paths,
  and a new candidate records the Exchange "tax filer" reading.
- Case names drop review-round prefixes, is_aca_tax_family_member.yaml drops
  its YAML anchors, and the NM and tax-family-size wording is fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator

@PavelMakarchuk, a follow-up to my 16:29 reply. d4ec8c5 takes round-1 Should address 2-7 and suggestions 2-5. Round-1 suggestion 1 isn't done (below). I haven't run tests locally; each new expected value is worked by hand in its case's comment, and CI will run them.

Round-1 Should address

  • 2, axiom issue: Updated the body of Encode IRC 36B(c)(1)(D) and the 36B(d)(1) family size; wire both into the ACA premium tax credit pipeline TheAxiomFoundation/rulespec-us#1627 to this head. It now covers:

    • the refund-only exception;
    • household income, with three new companion cases.

    It lists premium allocation, covered-child rating, shared-policy CSR and the nine state programs as separate issues. The 1.36B-2(b)(5) gate is moot for TY2026, since Pub. L. 119-21 repealed 36B(c)(1)(B).

  • 3, source-conflict register: Fixed.

    • source_conflicts/candidates.yaml:2064 (us-co:premium-assistance-ptc-recipients) now has both URLs: the 2024 C.R.S. Title 10 PDF (p.1002), and Amended Regulation 4-2-78 (p.2), the version the Division of Insurance's adoption notice links. It also has the quoted text, three code paths and a new note.
    • Reading 3 is a new candidate, us:exchange-tax-filer-claimable-spouse (:13984), with state unreviewed: it hasn't been put to the project lead.
    • Readings 1 and 4 get none: each fills a gap where no source gives the alternative, so neither is a source conflict.
  • 4, California pages: Fixed.

    • ca_premium_subsidy_eligible.py:11-20: each Design is one entry opening p.2, with a # PDF pages 2-3 comment; the comments place (c)(2) and (d)(7) on p.2, and (d)(7)(B), (d)(10) and (d)(11) on p.3.
    • ca_premium_subsidy_applicable_percentage.py:12-19: the same, for the (d)(4) table (p.2) and (d)(10) (p.3). ca_premium_subsidy.py:20-23: # PDF pages 1-2, with (d)(5) on p.2.
    • threshold.yaml:30-35: each Design's title now ends #page 2-3 and names (d)(10) and (d)(11).
    • One reference entry per document, as CLAUDE.md asks. I checked each page against the PDF text.
  • 5, helpers: Fixed.

    • slcsp_age_curve_amount_person_uncapped.py:23 uses has_input_for_period; the private call and the fallback are gone.
    • is_aca_tax_family_member.py:53-56 reads is_dependent_elsewhere_without_filing_exception. That is the same claimable & ~exception, and no test supplies it.
    • The PR body's helper sentence now names what the PR reads.
  • 6, unpinned branches: Fixed.

    • marketplace_csr_eligible.py:27-31: marketplace_csr_eligible.yaml:292 has two premium payers supplied with is_aca_ptc_eligible true and false. CSR is false; with the before-premium test only, it would be true.
    • The even-split fallback (aca_ptc_coverage_family_premium.py:78): aca_ptc_coverage_family_premium.yaml:691 has a supplied $7,000 bronze premium with lcbp: 0. The result is 3,500; with a zero fallback it would be 0.
    • is_aca_filer.py:32: is_aca_tax_family_member.yaml:644 has a 17-year-old and an infant, both supplied as dependents. is_aca_filer is true for the 17-year-old and the family size is 2; with ~dependent the family size would be 0.
  • 7, microsimulation: Fixed in the PR body, using your table and crediting your run. It says what moves: slcsp rises by $0.11bn (2025) and $0.13bn (2026), and about 27,000 more people pay an age-rated premium. The credit totals don't move at that precision.

Round-1 suggestions

  • 1, the two tier variables: Not done. No formula or reform reads them, but aca_ptc_slcsp.yaml:743-744 and :817-820 assert both, to show the NY tier the coverage family is priced at, and removing them drops those checks. If you'd rather lose them, I'll remove the variables and those four lines.
  • 2, NM comment: Fixed. nm_premium_assistance_mih_eligible.py:32-35, plus the same name at :16 and :27.
  • 3, names and anchors: Fixed.
  • 4, wording: Fixed.
    • aca_tax_family_size.py:16-20 and is_aca_tax_family_member.py:22-24 say "a filer (is_aca_filer: a head or spouse, including one inferred ...)".
    • is_aca_tax_family_member.py:32-38 adds the Publication 501 joint-return caveat and says the model takes the flag as given.
  • 5, early return: Done. aca_ptc_coverage_family_premium.py:60-64 returns before the monthly benchmarks when no tax unit has an enrollee outside its tax family. That return gives the same value as the full formula, since the share is 1 there.
  • 6, the Count the premium tax credit in household net income as a refundable credit #10773 guard: Still resolved, nothing to change. It's in aca_fpg.py:40-44, and California's two reads (ca_premium_subsidy_eligible.py:58, ca_premium_subsidy_applicable_percentage.py:34) go through aca_fpg.

No formula result changes in this commit outside the new test cases.

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE (head d4ec8c5cf8)

The two pushes (6d336a28dd, d4ec8c5cf8) close every open item from my last review and from my first review. I changed each pinned line back and a test now fails each time. I found no new defect in the diff since 18385c186b. No Critical or Should address item is open. Four optional suggestions remain.

Resolved since the last review

Item Status How I checked
Last review, Should 1: the reform's used_aca_ptc read at aca_ptc_minimum_premium.py:76 was not pinned Resolved The new case at ptc_minimum_premium.yaml:303 gives aca_ptc_coverage_family_premium 4,000, aca_ptc 5,940, used_aca_ptc 3,940. With the whole-policy read at line 76: 1 failed, 11 passed (used_aca_ptc 5,940, expected 3,940).
Last review, Should 2: the three conditions of the supplied-premium rule were not pinned Resolved Three new cases in aca_ptc_coverage_family_premium.yaml (from line 569). I removed each condition in turn; each time 1 failed, 16 passed (table below).
Last review, Should 3: monthly premium inputs did not reach the rule Resolved aca_ptc_coverage_family_premium.py:45-52 now also tests each month of monthly_selected_marketplace_plan_premium_proxy. The California witness gives 6,000 / 1,000 / 5,000 (table below). Without the monthly test: 1 failed, 16 passed.
First review, Should 2: axiom issue was stale Resolved TheAxiomFoundation/rulespec-us#1627 is open with the pe-parity label. Its body was updated on 2026-11-13 at 11:46am EST to this head. It now has the refund-only exception (required output 2, case H) and household income (required output 3, cases I and J). It gives a reason for each rule it leaves to separate issues.
First review, Should 3: source-conflict register Resolved us-co:premium-assistance-ptc-recipients has both URLs, quoted text and three code paths. Reading 3 is the new candidate us:exchange-tax-filer-claimable-spouse. I opened both Colorado sources (below).
First review, Should 4: California reference pages Resolved The references open PDF page 2 and name pages 2-3. I read pages 1 to 3 of both Designs (below).
First review, Should 5: helpers and private Core API Resolved slcsp_age_curve_amount_person_uncapped.py:23 calls has_input_for_period; the private call and the fallback for older Core releases are gone. is_aca_tax_family_member.py:53-56 reads is_dependent_elsewhere_without_filing_exception. The PR body names the helpers that the PR reads.
First review, Should 6: three unpinned branches Resolved One new case for each branch. I changed each branch back; a test fails each time (table below).
First review, Should 7: microsimulation impact was "Pending" Resolved The PR body has the table and says which totals move. I ran this head again; the totals did not change (below).
Last review, suggestion 1: changelog Resolved changelog.d/claimable-filer-ptc.fixed.md names both supplied-input rules.
First review, suggestions 2 to 5 Resolved The New Mexico comment, the twelve case names, the YAML anchors, the wording and the early return are done. Suggestion 1 is open (below).

Suggestions

  1. The two tier variables have no reader (first review, suggestion 1). No formula or reform reads aca_ptc_slcsp_family_tier_category or aca_ptc_slcsp_family_tier_multiplier; only aca_ptc_slcsp.yaml asserts them. It is acceptable to keep them as outputs for the tests. If they stay, say in their documentation that they are reporting outputs and that aca_ptc_slcsp does not read them.
  2. Follow-up 1 of the PR body names only #10413. #10860 is also open for the same problem (issue #9482). Name both. The PR that merges second (this one or #10860) must put the coverage-year state lookup and the zero-guideline handling of #10860 into aca_fpg, with aca_tax_family_size as the size, and keep aca_magi_fraction reading aca_fpg, so the federal fraction and California's two direct reads use one poverty line.
  3. The new register candidate is unreviewed. us:exchange-tax-filer-claimable-spouse says the reading was not put to the project lead. Put it to them after the merge; the model result for a joint return with one claimable spouse depends on it.
  4. The microsimulation paragraph of the PR body says that the commits after 18385c186b were not rerun. They can now say that the 2026 totals at d4ec8c5cf8 are the same (below).

What I checked

Mutation checks at this head. I changed one line at a time and ran the test file that holds the new case.

Line changed Change Result Failing value
aca_ptc_minimum_premium.py:76 Read selected_marketplace_plan_premium_proxy 1 failed, 11 passed used_aca_ptc 5,940, expected 3,940
aca_ptc_coverage_family_premium.py:53-55 Remove "enrollment not supplied" 1 failed, 16 passed premium 5,000, expected 0
aca_ptc_coverage_family_premium.py:57 Remove ~tax_unit.any(enrolled) 1 failed, 16 passed premium 5,000, expected 0
aca_ptc_coverage_family_premium.py:57 Remove tax_unit.any(member) 1 failed, 16 passed premium 5,000, expected 0
aca_ptc_coverage_family_premium.py:47-52 Remove the monthly input test 1 failed, 16 passed premium 0, expected 6,000
marketplace_csr_eligible.py:27-31 Use the before-premium test only 1 failed, 7 passed marketplace_csr_eligible true, expected false
aca_ptc_coverage_family_premium.py:78 Zero fallback in place of the even split 1 failed, 16 passed premium 0, expected 3,500
is_aca_filer.py:32 ~dependent in place of the where 1 failed, 21 passed is_aca_filer [false, false], expected [true, false]
aca_ptc_coverage_family_premium.py:60-64 Remove the early return 224 passed (gov/aca/ptc and contrib/aca) none: the early return gives the same values

Monthly premium witness (2026, California, one adult aged 40, aca_ptc: 1_000, no other inputs, so the model finds no enrollee):

Premium input selected_marketplace_plan_premium_proxy aca_ptc_coverage_family_premium used_aca_ptc marketplace_net_premium
Annual, 5,000 5,000 5,000 1,000 4,000
Monthly, 500 in each month 6,000 6,000 1,000 5,000
Monthly, 500 for January to June only 3,000 3,000 1,000 2,000
Monthly, 500 for December only 500 500 500 0
Annual 5,000 and monthly 500 in each month 5,000 5,000 1,000 4,000
Annual 5,000 and monthly 500 for January to June 5,000 5,000 1,000 4,000
Monthly 500 in each month, is_aca_marketplace_enrollee: false 6,000 0 0 6,000
Monthly 500 in each month, adult claimable elsewhere 6,000 0 0 6,000
Monthly 0 in each month 0 0 0 0
Monthly 500 for each month of 2025 only 0 0 0 0
  • The second row was 6,000 / 0 / 0 / 6,000 at the last head.
  • When the annual and the monthly inputs are both supplied, the annual amount is the premium. This agrees with the documentation of selected_marketplace_plan_premium_proxy.
  • A premium supplied for another year does not start the rule.

Since-prior diff, line by line

  • aca_ptc_coverage_family_premium.py:44-55: the input test is a Python boolean for the whole simulation, and it combines correctly with the two arrays. I ran one simulation with four tax units in Texas (2026), each with a supplied premium, once with annual and once with monthly inputs. The results are the same for both input types:

    Tax unit Premium aca_ptc_coverage_family_premium
    Default adult, no enrollee found 5,000 5,000
    Joint; the claimable head is the only enrollee 4,000 0
    Joint; both enrolled, the head claimable (equal benchmarks) 7,000 3,500
    Single adult, no enrollee found 3,000 3,000
  • aca_ptc_coverage_family_premium.py:60-64: when no tax unit has an enrollee outside its tax family, the share is 1 for each unit, so the early return equals the full formula. The mutation check above confirms it.

  • is_aca_tax_family_member.py:53-56: is_dependent_elsewhere_without_filing_exception is claimable & ~exception at the person level (it is from #10024 on main). No ACA test supplies it. The projection through person.tax_unit.any is unchanged.

  • slcsp_age_curve_amount_person_uncapped.py:23: has_input_for_period is the shared helper in policyengine_us/tools/general.py. This PR does not change that file. No variable or reform of this PR calls a private Core method.

  • is_aca_tax_family_member.yaml:503-642: I parsed the file at both heads. The 21 old cases differ only in their names; one case is new.

  • New expected values, by hand: 8,000 x 6,000 / 12,000 = 4,000 and 4,000 - 60 = 3,940; 12 x 500 = 6,000; 12 x (7,000 / 12) x 0.5 = 3,500.

  • No parameter value changed. threshold.yaml changes only two reference titles.

  • Defects found: none.

Axiom line

  • The line axiom: us:statutes/26/36B/c/1/D queued (TheAxiomFoundation/rulespec-us#1627) has the correct form.

  • The issue is open, has the pe-parity label, and names this head.

  • I ran its companion cases B, F and H in the model (2026 joint return, aca_magi: 23_475, benchmark 6,000 for each member). The model agrees with the issue:

    Case aca_tax_family_size aca_fpg aca_ptc
    B: one claimable spouse 1 15,650 5,016.40
    F: as B with one child 1 15,650 5,016.40
    H: as F, the claimant files only for a refund 2 21,150 11,507.03
  • The issue leaves premium allocation, covered-child rating, shared-policy CSR and the nine state programs to separate issues. Its reason holds: the rulespec-us pipeline takes the enrolled premium and the benchmark from the caller, and it has no CSR module and no module for these state programs.

Source-conflict register

  • us-co:premium-assistance-ptc-recipients: PDF page 1002 of the 2024 C.R.S. Title 10 file shows 10-16-1205(1)(b)(II) with the quoted text. PDF page 2 of Amended Regulation 4-2-78 shows Section 4C ("Eligible enrollee") with the quoted text, and page 1 shows the quoted Section 2 text.
  • us:exchange-tax-filer-claimable-spouse: the entry has three sources, two readings, an example (5,016.40, the same as case B above) and two code paths. All code paths in both entries exist.
  • policyengine_us/tests/code_health/test_source_conflicts.py and test_known_dead_reference_urls.py: 143 passed.

California reference pages (2026 Program Design and Final 2027 Program Design)

Provision PDF page, 2026 PDF page, 2027 Value on the page
(a), (b) 1 1 subsidy formula
(c)(2) 2 2 "Meets all eligibility requirements of the federal advance premium tax credit"
(d)(4) table 2 2 2026: 0% to 150%, 3.19% to 3.91% above 150% up to 165%. 2027: 2.8% to 5.28% above 150% up to 200%
(d)(5) 2 2 benchmark has its section 36B meaning
(d)(7) 2 2 "at or below 165 percent" (2026), "at or below 200 percent" (2027)
(d)(7)(B), (d)(10), (d)(11) 3 3 dependent rule, family size, poverty level
  • ca_premium_subsidy_eligible.py:11-20, ca_premium_subsidy_applicable_percentage.py:12-19, ca_premium_subsidy.py:20-25 and threshold.yaml:30-35 agree with this table.
  • The thresholds 1.5, 1.65 (2026) and 2.0 (2027) are on page 2 of each Design.

Microsimulation (default dataset, weighted 2026 totals, one run for each commit)

Total 18385c186b d4ec8c5cf8
aca_ptc $144.7502bn $144.7502bn
used_aca_ptc $47.0997bn $47.0997bn
aca_ptc_coverage_family_premium $100.7288bn $100.7288bn
Marketplace enrollees 54.0744m 54.0744m
  • The totals did not change. They are the same as in my last review.
  • has_input_for_period is false for pays_aca_premium, selected_marketplace_plan_premium_proxy, monthly_selected_marketplace_plan_premium_proxy and is_aca_marketplace_enrollee on the default dataset, so the supplied-input rules stay inactive for population results.

Tests run at this head (Python 3.13, policyengine-core 3.34.10)

Tests Result
policyengine_us/tests/policy/baseline/household/income/ 224 passed
policyengine_us/tests/policy/baseline/gov/aca/ and policyengine_us/tests/policy/contrib/aca/ 578 passed
The nine state premium program directories (CA, CO, CT, MA, MD, NJ, NM, VT, WA) 624 passed
tests/core/test_claimable_filer_ptc_invariants.py and the three tests/unit/test_ca_premium_subsidy_*.py files 34 passed
policyengine_us/tests/policy/baseline/partners/ 623 passed
The two register test files in tests/code_health/ 143 passed
  • The ACA count is 570 at the last head plus the 8 new cases.
  • Partner contract tests: the PR edits none of them.

Merge with main

  • The PR merges into main at 5cc0d58afa with no conflict. On that merge, gov/aca, contrib/aca and household/income give 802 passed.
  • Main moved by six commits after 5cc0d58afa. One of them changes source_conflicts/candidates.yaml, which this PR also changes. GitHub reports the PR as mergeable.

CI and repository rules

  • CI at this head: 36 of 36 checks passed; the last one completed on 2026-11-13 at 1:00pm EST.
  • The changelog fragment is at the top level of changelog.d/.
  • The PR adds no program, so policyengine_us/programs.yaml needs no change.

Not verified

  • I ran the tests on Python 3.13; CI uses 3.14. CI is green at this head.
  • The microsimulation comparison is one local run for each commit on the default dataset, for 2026 only. I did not run the microsimulation test suite locally (the CI check passed), and I did not run another dataset or 2025 at this head.
  • For each mutation I ran the test file (or the two directories) named in the table, not the full suite.
  • I did not run the tests on a merge with the newest main, only with 5cc0d58afa. I did not run the register tests on the merge.
  • In TheAxiomFoundation/rulespec-us#1627, I ran cases B, F and H. I did not run cases A, C, D, E, G, I and J, and I did not check the corpus rows that the issue names.
  • For the register entry us:exchange-tax-filer-claimable-spouse, I did not open the three federal sources again; I read 26 CFR 1.36B-1(d) in my first review.
  • The sources that I could not open in the first review (956 CMR 12.00, Connecticut Public Act 21-2, the New Mexico MIH Addendum, Publication 501, the Form 1040 instructions).

@DTrim99
DTrim99 merged commit 383192a into main Nov 13, 2026
36 checks passed
@DTrim99
DTrim99 deleted the claimable-filer-ptc branch November 13, 2026 20:53
DTrim99 added a commit that referenced this pull request Nov 13, 2026
#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>
MaxGhenis added a commit that referenced this pull request Nov 13, 2026
Main's 19 new commits include the premium tax credit rules for a filer
who can be claimed as a dependent (#10041). They add four items to
source_conflicts/candidates.yaml and edit three others, none of which
this branch changes, and Git merged the file without a conflict.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
DTrim99 added a commit that referenced this pull request Nov 13, 2026
…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>
MaxGhenis added a commit that referenced this pull request Nov 14, 2026
Main's #10041 rewrote the note on Colorado's premium-assistance item,
directly above the District of Columbia childless EITC item this branch
adds. Keep main's note and the branch's item.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants