Skip to content

Count car loan interest on the return of the taxpayer who incurred it - #10406

Open
MaxGhenis wants to merge 6 commits into
mainfrom
auto-loan-interest-per-tax-unit
Open

MaxGhenis wants to merge 6 commits into
mainfrom
auto-loan-interest-per-tax-unit

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Status: the round-1 Opus 5.5 review (APPROVE-WITH-NITS) is addressed in 04cbbdef, and the impact is in (below). Still waiting on the GPT-6.1 Sol review, which is queued until the Codex lanes reopen around 19:15Z, and on the affected-test batch.

Problem

qualified_passenger_vehicle_loan_interest is a Household input, and auto_loan_interest_deduction read it with add(tax_unit, period, [...]). For a group-to-group add, policyengine-core (commons/formulas.py, for_each_variable, 3.33.12) does three things:

  • it projects the household value to every member;
  • it divides by the household's member count;
  • it sums those shares over the tax unit.

So a household's car loan interest was split across its tax units by member count. On main:

  • two single roommates sharing $5,000 of household interest each deducted $2,500;
  • a 3-person tax unit and a 1-person roommate unit with $8,000 deducted $6,000 and $2,000.

This only arises in households with more than one tax unit. On the default dataset those hold 31.3% of the $12.16bn of weighted 2024 input, and $1.64bn of it was split to tax units other than the household head's.

The law ties the interest to the borrower:

  • 26 U.S.C. 163(h)(4)(B)(i) counts interest "on indebtedness incurred by the taxpayer".
  • 26 CFR 1.163-16(d)(1) (T.D. 10054, 91 FR 57237) repeats it.
  • 1.163-16(h)(1) (91 FR 57240–41) limits the amount to $10,000 "per Federal tax return regardless of filing status".
  • The payee statement must tell the payor of record that they "may deduct QPVLI only to the extent the SPVL was incurred by, and the QPVLI was actually paid by, the payor of record" (§1.6050AA-1(h)(2)(iv), 91 FR 57243).

Splitting one loan over several returns also multiplied the per-return limit.

Fix

  • New Person input qualified_passenger_vehicle_loan_interest_person: interest on loans the person incurred. It counts on the return of the tax unit the person heads or is the spouse in. A tax-unit dependent's own loans don't reach the filer's return.
  • New TaxUnit variable tax_unit_qualified_passenger_vehicle_loan_interest: the head's and spouse's own interest, plus the household-level amount in full for the one tax unit holding the household head. When no head is designated (API, YAML), that is the oldest member; with several flagged heads, the oldest flagged one; ties go to the first listed (get_rank, as in is_hud_dependent).
  • auto_loan_interest_deduction reads the new variable. The limit and the phase-out are unchanged.
  • Unchanged: the Household input keeps its entity and name, so the app, the API and Microcosm's household column still work. In a household with one tax unit, the result doesn't change.

Why the household head

The data can't say who took out the loan. Microcosm draws the household amount from the 2022 SCF, whose debts belong to the "primary economic unit": "an economically dominant single individual or couple ... and all other individuals in the household who are financially interdependent with that individual or couple" (2022 SCF codebook, "Unit of analysis"). It then scales by Treasury's six million qualifying loans a year.

The household head is the CPS householder, "the person (or one of the people) in whose name the housing unit is owned or rented". It is the model's closest observed stand-in for that economic unit. The default build sets exactly one head per household, and 98.9% are CPS line 1. The same Microcosm SCF stage also names it as the carrier of its household asset draws: head_carry, condition is_household_head, in us/source_stages.json.

Microcosm picks the person whose traits drive the imputation by its own rule: the one adult, the man of a married couple, otherwise the oldest adult. In multi-tax-unit households with this interest, that person is in the head's tax unit in 70.5% of records; aligning the two is a data follow-up, noted on PolicyEngine/microcosm#1215. The ruling is logged in us-hub/source-conflicts/RULINGS.md (us:car-loan-interest-household-attribution).

Invariants

These hold for every household. They are tested with Hypothesis plus seeded mixed batches in tests/core/test_auto_loan_interest_attribution_invariants.py:

  1. Conservation: a household's tax units together hold the household amount plus every head's and spouse's own interest. Only a dependent's own interest is dropped.
  2. One holder: the household amount lands on exactly one tax unit, the one holding the designated (or fallback) head.
  3. Locality: a tax unit's deduction depends only on its own filers' loans, and on the household amount when it holds the head. Zeroing other tax units' loans leaves it unchanged.
  4. Differential against the statute: the deduction is max(min(interest, 10,000) − 200 × ceil(max(MAGI − threshold, 0) / 1,000), 0), with a $200,000 threshold for a joint return and $100,000 otherwise. It is computed in the test from the statute's numbers.
  5. Bounds: 0 ≤ deduction ≤ min(tax unit interest, 10,000).

The tests mix households with and without flagged heads (the fallback runs beside flagged ones), include ties and adult dependents, and list people out of household order. Seven mutants are killed, each by the property tests and at least one YAML case:

  • the old member-count split;
  • counting dependents;
  • counting every adult's own loan;
  • giving every flagged head the amount;
  • a first-member fallback;
  • an oldest-filer fallback;
  • the youngest flagged head.

The round-1 review found that the oldest-filer and every-adult mutants survived; they are now caught.

Tests

  • tax_unit_qualified_passenger_vehicle_loan_interest.yaml (14 cases):

    • head attribution and the oldest-member fallback;
    • a parent's unit beside a roommate (was 6,000/2,000, now 8,000/0);
    • each filer's own loan;
    • a dependent's loan, and adult dependents' loans (a student and a grandparent);
    • the fallback when the oldest member is a dependent (it follows that member to the claiming return, not to the oldest filer);
    • person plus household amounts;
    • the per-return limit on the holder (was 7,500 + 7,500, now 10,000);
    • several flagged heads;
    • a head claimed as a dependent;
    • one tax unit (unchanged);
    • §1.163-16(h)(3) Example 1 (spouses' $6,000 and $5,000 loans on a joint return, deduction $10,000) and Example 2 (single, $7,000, MAGI $124,200, deduction $2,000).

    The examples live here, not in auto_loan_interest_deduction.yaml, so this PR doesn't conflict with Use the $100,000 car loan interest phase-out threshold for surviving spouses #10395, which appends to that file (git merge-tree is clean).

  • in_auto_loan_interest_deduction.yaml: Indiana follows the federal deduction to the head's return.

  • Revert check: with main's add() formula, 12 of the new cases fail (11 of the 14 here, plus Indiana's). The 3 that still pass read only the new tax-unit variable, or are the one-tax-unit case, which doesn't change.

Consumers

  • Indiana (in_auto_loan_interest_deduction), South Carolina (sc_obbba_addback), Montana (mt_federal_obbba_subtraction), and the Idaho and Arizona subtraction lists read the federal auto_loan_interest_deduction per tax unit, so they follow the fix unchanged.
  • No other variable reads the Household input.

Impact

This is a real microsimulation on the default dataset (populace_us_2024, sha 6496cc43…). It compares main at the merge base c5837fb00b with this PR at e5c1b8d16e, for 2025 and 2026. The runs went one at a time under the hub heavy lock. Later commits change only tests, documentation and references: the variables' formulas and attributes are AST-identical to e5c1b8d16e (formula_ast_check.txt in the evidence folder ~/reviews/auto-loan-attribution-2026-10-23/impact/). Sums are weighted.

Output 2025 2026
auto_loan_interest_deduction (baseline $10.72bn; $10.85bn) −$118.2M −$127.6M
tax units whose deduction rises: the holder of the household amount 4,896 (9.12M weighted), +$1,448M 4,831 (9.08M), +$1,474M
tax units whose deduction falls: other filers 7,770 (13.38M weighted), −$1,566M 7,720 (13.34M), −$1,602M
taxable_income +$2.6M +$16.6M
income_tax −$10.9M −$9.6M
state_income_tax +$0.67M +$0.34M
household_net_income +$10.2M +$9.3M
household_benefits 0 0

Why the total deduction falls. The interest is conserved: every household amount still lands on exactly one return. The largest household amount in the data is $2,456, so the $10,000 limit never binds. The whole net fall therefore comes from the phase-out: more of the moved interest now sits on returns whose MAGI is above the $100,000 or $200,000 threshold.

Why federal income tax falls even though the deduction falls.

  • Taxable income rises $1,104M on 9.65M weighted returns and falls $1,101M on 7.11M, a net of +$2.6M in 2025.
  • Federal tax rises $132.8M on the first group and falls $143.7M on the second. That is about 12.0% and 13.0% of the taxable income moved.
  • So the returns that take the household amount face slightly higher rates than the roommates, adult children and others who lose their share.
  • taxable_income is floored at zero. So the $1,566M of deduction taken off other returns raises taxable income by only $1,104M in all, and the $1,448M added to the holders lowers it by $1,101M.

States. State income tax moves mostly in Colorado (+$647K in 2025, +$648K in 2026), which starts from federal taxable income. Arizona is +$152K in 2025, when it subtracts the deduction. Indiana is −$108K in 2026, the year its conformity deduction is in in_deductions. Idaho is −$87K/−$89K. Every other state's change is under $70K. South Carolina's add-back falls $1.5M, but SC tax barely moves. Montana's subtraction is unchanged in total; it moves between people.

Comparison files are in fixes/10406-impact/ on the hub host (compare.<year>.json, by_state.<year>.json).

Axiom

axiom: us:statutes/26/163/h/4/B encoded-correct

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 23, 2026 09:22
26 U.S.C. 163(h)(4)(B)(i) counts interest on indebtedness incurred by the
taxpayer, and 26 CFR 1.163-16(h)(1) applies the $10,000 limit per return.
The household-level input was split across a household's tax units by
head count (core's group-to-group add()), so roommates and adult children
each deducted a share of one loan's interest.

Add a person-level input for each filer's own loans (head and spouse count;
a dependent's loans do not), and give a household-level amount in full to
the household head's tax unit (oldest member when no head is designated).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Multi-tax-unit households, each filer's own loans, a dependent's loan,
the per-return limit, several or no flagged heads, and 26 CFR
1.163-16(h)(3) Examples 1 and 2; Indiana follows the federal deduction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conservation of the household total, one holder per household amount,
locality across tax units, and a differential against the statute's
limit and phase-out.

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

Test an adult dependent's own loan and the oldest-member fallback when the
oldest member is a dependent, and draw adult dependents in the property
tests. Document the several-heads and dependent-head rules on the household
input, cite the payor statement legend (91 FR 57243) and point Cornell at
163(h)(4)(B)(i).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 23, 2026 14:51
Keeps auto_loan_interest_deduction.yaml as on main, so this PR no longer
conflicts with #10395, which appends cases to that file.

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

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.

1 participant