Repository navigation
Conversation
Grandfathered debt incurred on or before October 13, 1987 has no dollar limit and reduces the other limits (26 U.S.C. 163(h)(3)(D)). Debt incurred on or before December 15, 2017 keeps the $1,000,000 limit and reduces the $750,000 limit for later debt ((h)(3)(F)). Compute the qualified loan limit as in IRS Publication 936 Table 1, so it no longer depends on which loan is larger or which slot it is in (fixes #10011). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review: the origination-year inputs now say a refinancing that still counts as grandfathered debt takes the original year (163(h)(3)(D) (iii)-(iv)); the Pub. 936 balloon-note case distinguishes 1986 from 1996; the YAML header and property test note the exact ratio (Table 1 line 14 rounds to three decimals); the Simulation grid becomes two vectorized YAML cases covering every filing status in 2017 and 2024; cap.yaml cites (B)(ii) and (F)(i)(II), (IV) and says the reduction stops at zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hand-off from the original session to the US + core hub, which adopted this PR. I'm not pushing to this branch. My last head was Expected microsim impact: zero on the certified default dataset. On 2026-10-09 I read the default h5 directly (
So neither the grandfathered rule nor the two-loan vintage rule binds on any record. A non-zero diff in the hub's impact run would point to something other than these two rules. My own main-vs-branch microsims never got the heavy lock before that session ended; their driver is Reviews: GPT-6.1 Sol round 1 requested changes, and all five findings were fixed: Axiom: the held d1071 draft at Follow-ups owed:
🤖 Generated with Claude Code |
Fixes #10011.
What and why
The federal limit on deductible mortgage interest depends on when each debt was incurred. The old helper picked each loan's cap by origination year (everything through 2017 got $1,000,000) and then combined the two loans by balance size. That got two cases wrong:
This PR computes the qualified loan limit by vintage, line by line as IRS Publication 936 Table 1 does, so it no longer depends on loan order or relative size.
Montana already counts this federal amount (#10004), and Alabama and North Carolina will (branch
al-nc-mortgage-interest-federal-cap, which waits on this PR), so they inherit the fix. Puerto Rico'spr_mortgage_deductionalso reads it.Law
26 U.S.C. 163(h)(3)(D):
26 U.S.C. 163(h)(3)(F)(i), for taxable years beginning after December 31, 2017:
IRS Publication 936 (2025), PDF p. 13: "Grandfathered debt isn't limited. All of the interest you paid on grandfathered debt is fully deductible home mortgage interest. However, the amount of your grandfathered debt reduces the limit for home acquisition debt." Table 1, PDF p. 14 computes the qualified loan limit:
Line 6 counts grandfathered debt once against the $750,000 limit, matching p. 12: "The limits above are reduced (but not below zero) by the amount of your grandfathered debt." For 2017 and earlier, both limit parameters are $1,000,000 ($500,000 separate), so all non-grandfathered debt shares one $1,000,000 limit reduced by grandfathered debt; the 2017 edition says the same ("Grandfathered debt isn't limited", PDF p. 10; Table 1, PDF p. 11).
Inputs and the 1987 convention
The model stores a balance and an origination year for up to two loans, so the October 13, 1987 cutoff falls inside a year. A new parameter
gov.irs.deductions.itemized.interest.mortgage.grandfathered_origination_year= 1987 treats a 1987 loan as grandfathered. It mirrors the existingpre_tcja_origination_year= 2017 convention, under which a 2017 loan counts as incurred on or before December 15, 2017. January 1 to October 13 is 286 of 365 days of 1987. A year of 0 means unknown and counts as post-2017 debt, as before. Both origination-year inputs now document this. No new input is added; exact cutoff dates would require an origination-date input.The three cap parameter descriptions now say which debt each applies to; the old ones called all pre-2018 debt "grandfathered", which Pub. 936 reserves for pre-October 14, 1987 debt.
Households (main vs this branch)
Measured with
evidence/households.pyongh/mainfdd70629054a24a0a53077c358d13b9a17d96b4dand this branch9e5342c844145868e917f9a92d0c06e5185fb3d2. Each of the 14 households uses one tax unit; both JSON outputs record the imported module. Dollar pairs show main → branch.Deductible interest is unchanged for 1988 boundary, Equal balances; older first, Post-2017 only.
The following cases use the standard deduction on both versions: 1986 debt; zero wages. A changed mortgage-interest allowance does not change federal income tax in these nonitemizing cases.
Federal income tax is unchanged for 1986 debt; zero wages, 1988 boundary, Equal balances; older first, Post-2017 only.
Tests
deductible_mortgage_interest_tax_unit.yaml(new, named for the variable it checks): 18 cases. Each shows its Table 1 arithmetic and covers:The model prorates by the exact ratio; Pub. 936 line 14 rounds it to three decimals, which would move some expected amounts by up to $20.
mt_itemized_deductions_joint.yaml: one case where 1986 debt above $1,000,000 is fully deductible in Montana.tests/core/test_mortgage_debt_limit_invariants.py(new). It tests the pure helpers only and builds no Simulation; it's Python because YAML can't state for-all properties. Hypothesis (400 derandomized examples) draws two loans around every cutoff year and limit, and checks the helpers against two independent restatements: Table 1 line by line, with its "stop here" branch, and the statute in its own order. Local passing run: 2 tests passed in 177.502 seconds elapsed, with 812.73 MiB peak RSS (852,213,760 bytes), measured locally on macOS usingtime.monotonic()for elapsed time andos.wait4child resource usage for RSS (Darwinru_maxrssis in bytes). These are local measurements, not Linux CI measurements.Branch verification
Measured verification at
9e5342c844145868e917f9a92d0c06e5185fb3d2: 84 YAML cases across 10 files and 8 Python tests across 2 files passed. The YAML files ran in one bounded foreground batch of 10 files; each Python file ran in its own foreground process.core/test_mortgage_debt_limit_invariants.pycore/test_mt_mortgage_interest_federal_limit_properties.pypolicy/baseline/gov/irs/income/taxable_income/deductions/deductible_mortgage_interest.yamlpolicy/baseline/gov/irs/income/taxable_income/deductions/deductible_mortgage_interest_tax_unit.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_federal_income_tax_deduction_for_federal_itemization.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_federal_income_tax_deduction_for_federal_itemization_indiv.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_indiv.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/federal_itemization/mt_itemized_deductions_for_federal_itemization_joint.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_itemized_deductions_indiv.yamlpolicy/baseline/gov/states/mt/tax/income/deductions/itemized/mt_itemized_deductions_joint.yamlpolicy/baseline/gov/states/mt/tax/income/integration.yamlpolicy/baseline/gov/territories/pr/tax/income/taxable_income/deductions/pr_mortgage_deduction.yamlPer-file passing counts in a batch come from the YAML case counts only after its real pytest summary and collected count confirm every case in the complete batch passed.
No partner test files were edited. A read-only search found no partner cases supplying mortgage balances, origination years, mortgage interest, or deductible mortgage interest.
Regression evidence on main
The branch's new cases were also run against
gh/mainfdd70629054a24a0a53077c358d13b9a17d96b4d:deductible_mortgage_interest_tax_unit.yaml;mt_grandfathered_case.yaml(2-file batch)deductible_mortgage_interest_tax_unit.yamlmt_grandfathered_case.yamlPer-file rows count the recorded verbose result lines before the failure report. Every file's case count, the total number of result lines, and their combined outcomes match the batch's collected count and pytest summary; no per-file results are inferred.
These are preserved assertion failures demonstrating the pre-fix behavior; the same federal and Montana cases pass on the branch.
Invariants (asserted in the property test)
For every pair of loans, filing status, and pre- or post-2018 year:
Hub integration notes
_limited_mortgage_balanceand_mortgage_balance_caphelpers and must migrate those imports.Hub microsimulation impact
Pending: the hub runs it.
Not changed (known limitations)
axiom: TheAxiomFoundation/rulespec-us#1673 queued
The rest of 26 U.S.C. 163(h)(3) and Montana's mortgage interest line are already queued as TheAxiomFoundation/rulespec-us#1413 and #1512; #1673 covers the pre-October 13, 1987 grandfathered debt this PR adds (filed under d1142).
Independent review: round 2 APPROVE at
248d68cdb8; its two nits were corrected in8929b0d709(the 2017 YAML arithmetic comment and pre-2018 cap explanation). Confirmation at the merged head9e5342c844145868e917f9a92d0c06e5185fb3d2is pending with the hub. The PR remains a draft.🤖 Generated with Claude Code