Skip to content

Count the premium tax credit in household net income as a refundable credit - #10773

Open
DTrim99 wants to merge 3 commits into
mainfrom
ptc-refundable-credit-net-income
Open

DTrim99 wants to merge 3 commits into
mainfrom
ptc-refundable-credit-net-income

Conversation

@DTrim99

@DTrim99 DTrim99 commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR counts the federal premium tax credit in household net income as a refundable tax credit. Until now it counted only when gov.simulation.include_health_benefits_in_net_income was on, and that flag is off by default. So with default settings a reform to the credit moved neither household net income nor the budget. policyengine.py's budgetary impact is built from household taxes and benefits, plus the cost of Medicaid, CHIP and the Medicare Savings Programs, and the credit appeared in none of them. In the app such a reform scored $0 and showed no gains by decile, no winners and no change in inequality.

What changes

  • household_refundable_tax_credits adds premium_tax_credit_in_force, beside the federal, state and local refundable credits. The new variable is the assigned credit (assigned_aca_ptc), a refundable credit under 26 U.S.C. 36B. Like the other federal refundable credits, it is zero in a year gov.contrib.ubi_center.flat_tax.abolish_federal_income_tax abolishes the federal income tax. The credit now reaches household_net_income and household_tax the way the EITC and refundable CTC do, whether or not health benefits count.
  • It doesn't go in income_tax. Most of the credit is paid monthly to the insurer in advance, and the return only reconciles the difference (net_premium_tax_credit, Form 8962 line 26). income_tax stays what the return shows. This keeps the household calculator and API partners from reporting the credit as a refund at filing, and it keeps federal income tax totals comparable to IRS and CBO figures, which book advance payments as outlays.
  • SPM resources don't change. They already count the credit through medical out-of-pocket spending, which includes the marketplace premium net of the credit (marketplace_net_premium, since Decompose SPM MOOP premiums #8178). They subtract income_tax, which this PR leaves alone.
  • No double counting:
    • assigned_aca_ptc leaves the gov.household.household_health_benefits list, so it counts once when the flag is on.
    • household_net_income_including_health_benefits subtracts premium_tax_credit_in_force, since household_net_income and healthcare_benefit_value both count it now. Its value, and the marginal rate built on it, are unchanged.
    • healthcare_benefit_value still includes the credit as the value of coverage.
  • 2015 credits read the 2015 guideline. The credit's FPL fraction uses the guideline for the year before coverage. Household net income now computes the credit for every household, so a 2015 household computed 2014 values, before the first modeled year (test_irmaa_first_modeled_year). For coverage in the first modeled year, the coverage year's guideline now stands in, as Colorado CCAP reads its parameters at the start of that year. No test pins a 2014 or 2015 credit.

Effects

  • Household net income rises by the assigned credit for marketplace enrollees, and marginal_tax_rate now includes the credit's phase-out and the 400% FPL cliff.
  • Household tax falls by the credit, so policyengine.py's total budgetary impact includes changes in it. They land in the unattributed share for now, like SNAP and SSI. A policyengine.py follow-up can attribute them to the federal government and add an ACA line to its program statistics.
  • The app shows this once policyengine.py pins a policyengine-us release that includes it (it pins 2.2.1).
  • No partner contract file asserts any variable this changes: household_net_income, household_tax, household_refundable_tax_credits, the health aggregates or marginal_tax_rate.

Tests

  • New household_refundable_tax_credits.yaml:
    • the credit counts as a refundable credit;
    • it counts once with the health-benefits flag on;
    • a derived credit for a single 45-year-old in Lebanon County, PA, earning $40,000 in 2026 reaches the household's refundable credits ($10,086).
  • New cases:
    • in household_net_income.yaml: net income and household tax;
    • in household_net_income_including_health_benefits.yaml: the credit counts once;
    • in marginal_tax_rate.yaml: a single $60,000 earner in Texas in 2024 faces 12% + 7.65% + 8.5% = 28.15%, the enhanced schedule's phase-out above 400% FPL.
  • Households that have a credit now count it. Each such case names premium_tax_credit_in_force as an output, so the difference shows:
    • two military retirement pay cases (Texas, 2024);
    • the Indiana county tax case (2021);
    • the two Montana 2021 rebate cases;
    • the payroll tax abolition case (Idaho, 2025).
  • Abolition. The federal income tax abolition case passes unchanged: abolishing the federal income tax takes the credit with the other federal refundable credits.
  • Employer coverage. Several tests give their households employer coverage, so the credit's phase-out doesn't blur the arithmetic they check:
    • the roommate marginal tax rate tests (YAML and test_marginal_tax_rate_coverage.py);
    • the zero-rate capital gains response test;
    • the estate income net income invariant;
    • the Social Security COLA differential, whose literals come from main.
  • household_health_benefits.yaml: with the flag on, the health benefits no longer include the credit.
  • test_household_spm_resource_parity.py: the one-person reconciliation takes the credit out of the household side, as one of the items the two measures define differently. test_aggregate_list_integrity.py adds the new variable to the household tax totals that reforms mustn't redefine.

Checks run before pushing, on Modal (the laptop hasn't the memory):

  • the 46 YAML files that assert a variable this changes or set the health-benefits flag;
  • the Python tests that touch net income, marginal rates or SPM resources: the parity, aggregate-list, SPM contract and system, marginal-rate coverage and branches, behavioral-response and capital-gains MTR, override, multi-year, cached-array and child-care tests.

All pass after the updates above. A run of 30 other Python test files failed the same tests on this branch as on main, from files the Modal run didn't mount (notebooks and CI config). CI runs the full suites.

axiom: n/a: accounting aggregate (household net income), not a statutory provision

🤖 Generated with Claude Code

DTrim99 and others added 3 commits November 9, 2026 16:49
…credit

The federal premium tax credit counted in household net income only when
gov.simulation.include_health_benefits_in_net_income was on, which it is
not by default. A reform to the credit then moved neither net income nor
policyengine.py's budgetary impact.

household_refundable_tax_credits now adds premium_tax_credit_in_force: the
assigned credit, a refundable credit under 26 U.S.C. 36B, zeroed like the
other federal refundable credits in a year the federal income tax is
abolished. It doesn't go in income_tax, which stays what the return shows
(most of the credit is paid in advance to the insurer) and what SPM
resources net; SPM resources already count the credit through the
marketplace premium net of it. The credit leaves the health-benefits list,
and household_net_income_including_health_benefits subtracts it, so it is
never counted twice.

Tests whose households have a credit now count it: each such case names
the credit as an output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…l rate

The roommate marginal tax rate tests and the zero-rate capital gains
response test check mechanics the premium tax credit's phase-out would
blur, so their households now have employer coverage. A separate case
pins the phase-out: a single $60,000 earner in 2024 faces 12% + 7.65% +
8.5% = 28.15%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Household net income now computes the premium tax credit for every
household, and the credit's FPL fraction read the prior year's poverty
guideline, so a 2015 household computed 2014 values, before the first
modeled year (test_irmaa_first_modeled_year). For coverage in the first
modeled year, the coverage year's guideline now stands in, as Colorado
CCAP reads its parameters at the start of that year.

The estate income invariant and the Social Security COLA differential
check net income against tax-only arithmetic and main's literals, so
their filers have employer coverage.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Contributor

An independent review at 460f68252e found two things to fix before this merges. I checked both against the code at that head.

1. With include_health_benefits_in_net_income on, the credit still counts twice. household_net_income now adds the credit through household_refundable_tax_credits. Under the flag it also subtracts household_health_costs, whose list still holds marketplace_net_premium, and that variable equals the premium less used_aca_ptc. So net income moves by $2 for each $1 of credit, as it does on main: with a $5,000 premium, raising aca_ptc from $1,000 to $2,000 raises net income by $2,000. A flag-on pair like that would pin the fix. One fix: have the costs list subtract the gross premium once the credit counts as cash.

2. premium_tax_credit_in_force adds assigned_aca_ptc, which has no premium cap. 26 U.S.C. 36B(b)(2) limits the credit to the premiums of the plan the household enrolled in. assigned_aca_ptc equals aca_ptc times take-up, so a household whose plan costs less than its credit counts more than the statute allows. used_aca_ptc already takes the lesser of the credit and the plan premium. Reading it, with a case where the premium falls short of the credit, would follow the statute.

Two smaller points:

@MaxGhenis

Copy link
Copy Markdown
Contributor

A household from the 2017 cliffs analysis shows finding 1 on PolicyEngine US 2.116.0: a single Texas adult aged 40 in 2025, with wages of $30,000 and then $31,000.

  • The credit falls by $94, from $5,606.58 to $5,512.58, and marketplace_net_premium rises by $94.
  • With the flag off, household_net_income rises by $803.50.
  • With the flag on, it rises by $615.50: household_health_benefits falls by $94 and household_health_costs rises by $94, so the $94 cut costs $188.

Counting the credit once gives $709.50. At this PR's head the first $94 arrives through household_refundable_tax_credits in place of the benefits list, and the second still comes through the costs list.

This branch has not been deployed

No deployments
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.

2 participants