Skip to content

Mortgage debt cap loses grandfathered debt when equal balances put the newer loan first #10011

Closed

Description

@MaxGhenis

Problem

_limited_mortgage_balance in policyengine_us/variables/household/expense/tax_unit/mortgage_interest_structure.py picks each loan's cap by balance size, not by which debt is grandfathered. With equal balances, first_is_smaller is false, so the result depends on loan order.

Take a JOINT tax unit with two $775,000 acquisition loans, one originated in 2017 (grandfathered, $1,000,000 cap) and one in 2018 ($750,000 cap):

Slot order Helper result Statute
2018 loan first, 2017 loan second min(750,000, max(775,000, 1,000,000)) = 750,000 775,000
2017 loan first, 2018 loan second min(1,000,000, max(775,000, 750,000)) = 775,000 775,000

Under 26 U.S.C. 163(h)(3)(F), grandfathered acquisition debt keeps the $1,000,000 limit, and it reduces the $750,000 limit for newer debt, to no less than zero:

min(775,000, 1,000,000) + min(775,000, max(0, 750,000 − 775,000)) = 775,000

This assumes both loans are qualifying acquisition debt, the 2017 loan was incurred on or before December 15, 2017, and there's no refinancing or binding-contract exception. The model stores only the origination year.

Reproduction (current main)

Calling the helper directly on main, with both orders as one vector, returns [750000, 775000]. The expected result is [775000, 775000].

At the household level, take 2024, JOINT, $62,000 of mortgage interest ($31,000 per loan), balances of 775,000 each, and origination years 2018/2017. The deductible interest comes to $30,000, where the statute gives $31,000. With the order swapped, it's $31,000.

Sources

  • 26 U.S.C. 163(h)(3)(B)(ii) and (F). P.L. 119-21 §70108(a)(1)(B) renumbers the grandfathered-debt clause for tax years beginning after 2025 (text).
  • Parameters: policyengine_us/parameters/gov/irs/deductions/itemized/interest/mortgage/ (cap.yaml, pre_tcja_cap.yaml, pre_tcja_origination_year.yaml).

Acceptance criteria for a fix

  • Regression: require $775,000 of qualified balance, and $31,000 deductible interest in the example above, in both slot orders.
  • Property and differential tests:
    • a slot-swap property;
    • a differential test against a reference that caps grandfathered debt first, then reduces the newer-debt allowance;
    • ties, and balances below, at and above both caps;
    • JOINT and SEPARATE caps.
  • Bounds:
    • 0 ≤ qualified balance ≤ total balance;
    • 0 ≤ deductible interest ≤ total interest;
    • deductible + nondeductible = total interest.
  • No-balance convention: keep the existing full deduction when no balance is supplied.

Found while reviewing #9605, which removes inputs and doesn't change this helper. The bug predates it.

Activity

  1. added a commit that references this issue on Oct 13, 2026
    5c398ca
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions