Repository navigation
Count ACA premium tax credit take-up as filing a return to claim it - #10140
Merged
Merged
Conversation
aca_ptc was zero for any tax unit that tax_unit_is_filer did not count as filing, even one that takes up the credit. A taxpayer who receives advance payments must file and reconcile them (26 CFR 1.6011-8(a)), and a credit not paid in advance is claimed on Form 8962. files_return_for_aca_ptc is tax_unit_is_filer or takes_up_aca_if_eligible; aca_ptc and the California, Maryland and New Mexico premium programs that copy its filing gate use it. A tax unit that neither files nor takes up the credit still gets none. Two Maryland cases expected 0 from an older formula and passed only because their units computed as non-filers; they now expect the COMAR .04B amounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 12, 2026 09:33
…g, add positive state cases - nm_premium_assistance_mih_eligible serves income above the federal PTC limit, where there is no federal credit to take up, so it keeps tax_unit_is_filer; Case 10b pins that taking up ACA does not make such a unit a filer. - Add a non-filer that takes up the federal credit to the CA, MD and NM base tests, each passing the filing gate. - Document why the invariants test is Python and what it costs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… credit is positive Taking up coverage when the federal credit is $0 gives no reason to file: there is nothing to claim or reconcile. aca_ptc_before_filing_gate carries the credit formula; aca_ptc is that amount times files_return_for_aca_ptc, which now requires a positive credit before take-up counts as filing. The federal credit is unchanged; the CA, MD and NM base gates no longer open for a non-filer with a $0 federal credit. - Zero-credit counterexamples with filer twins for the helper, CA, MD and NM. - Invariant 5: a non-filer that takes up a $0 credit does not file for it. - Correct the test docstring (one simulation per drawn example) and the MD comments that named the old gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Hand-off to the US + core hub (session
|
Contributor
Author
|
US + core hub merge audit, us#10140 at
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is wrong on main
aca_ptcis zero for every tax unit thattax_unit_is_filerdoes not count as filing (#8056 added the gate so the credit matches IRS data on filers).tax_unit_is_filercounts a unit as filing if it is required to, files for the EITC or refundable CTC, files to elect section 911, or files voluntarily. Taking up the premium tax credit itself is not a reason to file. So a tax unit that takes up the credit but meets none of those conditions gets $0 ofaca_ptcand ofassigned_aca_ptc. Example: a couple aged 62 with $30,000 of Social Security, enrolled through the Marketplace. The California, Maryland and New Mexico premium programs copy the same gate.The law:
Either way, a tax unit that takes up the credit files.
This matters now because #10135 removes a bogus unearned income filing test that made most units with interest, dividends or pensions "required to file". The independent review of #10135 (P1) found that, without this fix, units that take up the credit and lose that flag lose their credit. #10135 alone measured −$0.07bn of take-up-weighted PTC in 2025 and −$0.22bn in 2026.
Fix
aca_ptc_before_filing_gate(tax unit): the existing credit formula, the benchmark premium less the required contribution, floored at zero.aca_ptcis that amount times the filing condition below. Relative to the previous round of this PR the federal credit is identical for every unit, since a zero credit times anything is zero. Relative to main, a non-filer that takes up a positive credit now gets it, which is the point of this PR.files_return_for_aca_ptc(tax unit):tax_unit_is_filer | (takes_up_aca_if_eligible & (aca_ptc_before_filing_gate > 0)). Taking up coverage counts as filing only when there is a credit to claim or reconcile. At a $0 credit nothing is claimed. The model has no input for advance payments, so it also assumes none were made. (In reality, advance payments based on an estimate can exceed the final credit and still need reconciling.) On that assumption 26 CFR 1.6011-8(a) gives no reason to file. The review (round 2, P2) found this for a non-filer whose contribution exceeds the benchmark premium: unconditional take-up had opened the MD, NM base and CA gates for them.aca_ptcgates on it instead oftax_unit_is_filer. So does each state premium program that copies the federal filing gate for federally eligible enrollees:ca_premium_subsidy_eligible,md_premium_assistanceandnm_premium_assistance_eligible.nm_premium_assistance_mih_eligiblekeepstax_unit_is_filer. New Mexico's middle-income component serves income above the federal limit, where there is no federal credit to take up, so the federal take-up input says nothing about filing there. The MIH Addendum also says the state payment is not reconciled like APTC. Found by the review (round 1, P2).tax_unit_is_fileris unchanged. Adding the computed credit or its eligibility to it would create a cycle: filing → PTC eligibility →pays_aca_premium→ Medicaid eligibility → Medicaid household income and size →medicaid_uses_non_filer_rules→ filing. OR-ing the take-up input alone into it would be acyclic, but it would count households that are not eligible as filers (take-up defaults to true in household calculations) and change Medicaid's household rules for them. So Medicaid still sees onlytax_unit_is_filer. Whether a unit that files only for the credit should be a filer for Medicaid too is an open methodology question, queued for Max as decision d1415 (see Apply the unearned income filing test only to filers who can be claimed as dependents #10135). Nothing here implements it.Why not
would_file_if_eligible_for_refundable_credit, the propensityeitc.pyuses? The default dataset (populace_us_2024.h5@populace-us-2024-spm-20260909) does not carry it, so it defaults to true for every record. Gating the PTC on it would hand the credit to every non-filer and undo #8056.takes_up_aca_if_eligibleis in the dataset (4.6% of records).Tests
files_return_for_aca_ptc.yaml: the truth table, plus the Social Security couple. That couple is not required to file, is not a filer, and files for the credit.aca_ptc.yaml: a non-filer that takes up the credit gets it ($1,200 − 0.04 × $25,000 = $200, and the same assigned).A non-filer that takes up a positive federal credit passes each state gate: CA Case 13b (eligible), NM base Case 13b (eligible) and MD Case 1b ($2,500).
A non-filer that takes up coverage with a $0 federal credit does not pass. Each state case has a filer twin with the same inputs, so it cannot pass for another reason; the helper case's counterpart is the positive helper case and invariant 5:
files_return_for_aca_ptc.yaml($5,500 benchmark, 9.96% of $60,000 = $5,976; false); CA 13c/13d (false/true); NM base 13c/13d (false/true); MD 1c/1d ($0 and $89.50, where $5,500 − 0.090175 × $60,000 = $89.50).New Mexico MIH Case 10b: above 400% FPL, taking up ACA does not make a non-filer eligible.
Four existing non-filer cases now also set
takes_up_aca_if_eligible: false, so they still test the gate:aca_ptc.yaml,ca_premium_subsidy_eligible.yaml,nm_premium_assistance.yamlandnm_premium_assistance_eligible.yaml. The MIH non-filer case is unchanged.Two Maryland cases were stale.
md_premium_assistance.yamlCases 12 and 13 expected $0 "from the gap". Under the formula (COMAR 14.35.21.04B),max(0, max(0, slcsp − aca_ptc) − md_pct × income), they are $2,500 and $2,500 − 0.0622 × $39,125 = $66.43. On main they passed only because the unit, with no gross income, computed as a non-filer. They now statetax_unit_is_filer: trueand the formula's amounts.tests/core/test_aca_ptc_filing_gate_invariants.py(Hypothesis, 2025 and 2026, every unit in all four combinations of filing and take-up):aca_ptc.aca_ptcandassigned_aca_ptcdo not depend on whether it would file otherwise.aca_ptcwhether or not it takes it up.Every batch includes a unit with a positive credit and one with a zero credit, and the test asserts both. Why Python: each YAML case pins one combination with fixed amounts, while this checks all four combinations against drawn premiums, incomes and contribution rates in one vectorized simulation. Cost, measured locally (macOS, shared host at load 100 to 200): about 4.5 s of test calls, under 10 s in pytest, 26 to 39 s wall time including the model import, and a 1.6 GB peak memory footprint, in the
coregroup of the rest Python tests. These are not Linux CI measurements.Local runs at the head:
aca_ptc.yaml17 passed;files_return_for_aca_ptc.yaml5;assigned_aca_ptc3;integration4;used_aca_ptc5;premium_tax_credit1; CA 21; MD 29 and MDintegration13; NM 31, 16 and 11. The invariants test passes.Not changed here (pre-existing; found by the review). Vermont's
vt_premium_assistance_eligibleand Connecticut'sct_covered_connecticut_eligibleread federal PTC eligibility without any filing condition, so a non-filer who does not take up the federal credit can still receive them. Each program's own rule needs checking, so they are left for a separate change. Colorado, Washington, New Jersey and Massachusetts compute their residual amounts fromaca_ptc; that does not give them their own filing gates.Coordination
ca_premium_subsidy_eligible.py.Impact
Real microsimulations of the default dataset (
hf://datasets/policyengine/populace-us/populace_us_2024.h5@populace-us-2024-spm-20260909), policyengine-core 3.32.15, one run per side and year under the shared heavy-job lock; weighted sums. Run outputs and record-by-record comparisons are on the hub host in~/reviews/us-hub/fixes/10135-impact/and~/reviews/us-hub/fixes/aca-ptc-take-up-impact/.This PR alone: main
c09ffb4b30(policyengine-us 2.40.0) against7fed1d3d27aca_ptc(entitlement)assigned_aca_ptc(take-up weighted)used_aca_ptc#10135 with this PR: main
c09ffb4b30(policyengine-us 2.40.0) against the two merged (f9102afc46=7fed1d3d27+00570b48b7)aca_ptc(entitlement)assigned_aca_ptc(take-up weighted)used_aca_ptcOn the dataset almost no tax unit that takes up the credit is otherwise a non-filer, so this PR alone changes the credit by under $600 in total: it is a correction for household calculations and for future data. With #10135 it is what keeps the take-up-weighted credit unchanged. #10135 alone lowers
assigned_aca_ptcby $0.07bn (2025) and $0.22bn (2026); with this PR the change is under $500. The remaining fall inaca_ptcentitlement ($0.73bn and $1.00bn) and Maryland Premium Assistance ($13.4M in 2026) is for tax units that do not take up the credit and, after #10135, do not file. That is the filing gate from #8056 working as designed.axiom: n/a: this changes how the model decides whether a tax unit files to claim the credit (take-up implies filing), a microsimulation propensity. rulespec-us's section 36B modules compute the credit for a given return and have no filing gate.
🤖 Generated with Claude Code