Skip to content

Share North Carolina's $20,000 mortgage and property tax cap between spouses filing separately - #10029

Merged
MaxGhenis merged 4 commits into
mainfrom
nc-mortgage-property-cap-mfs
Oct 14, 2026
Merged

MaxGhenis merged 4 commits into
mainfrom
nc-mortgage-property-cap-mfs

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

North Carolina caps home mortgage interest plus real estate taxes at $20,000. For spouses filing separately, the $20,000 covers both spouses' returns combined. PolicyEngine gave each tax unit its own $20,000, so spouses filing separately could deduct up to $40,000 between them.

  • Example: two spouses file separately and each pays $15,000 of mortgage interest. North Carolina allows $10,000 each; the previous implementation gives $15,000 each.
  • This PR shares the $20,000 between spouses filing separately, as G.S. 105-153.5(a)(2)b and Form D-400 Schedule A lines 3 to 5 require.
  • PE represents such spouses as two tax units linked by a two-person marital unit, explicit or core's default, the same structure ar_medical_expense_deduction_floor already uses.
  • Dataset profiling found no such couples in the default dataset, so this shared-cap change cannot affect its state tax results. This conclusion follows from the dataset's structure; main-versus-branch microsimulation results are unavailable. The fix affects household calculations that link the spouses.
  • Max ruled 1A, 2A, 3A, 4A and 5B under d1351 on 2026-10-12. See "Rulings" below. The first four retain the branch's interpretation; 5B now includes an other-state spouse's payments as well as income.

What North Carolina law says

N.C. Gen. Stat. 105-153.5(a)(2)b, in force since at least S.L. 2016-6 § 3:

For spouses filing as married filing separately or married filing jointly, the total mortgage interest and real estate taxes claimed by both spouses combined may not exceed twenty thousand dollars ($20,000). For spouses filing as married filing separately with a joint obligation for mortgage interest and real estate taxes, the deduction for these items is allowable to the spouse who actually paid them. If the amount of the mortgage interest and real estate taxes paid by both spouses exceeds twenty thousand dollars ($20,000), these deductions must be prorated based on the percentage paid by each spouse. For joint obligations paid from joint accounts, the proration is based on the income reported by each spouse for that taxable year.

Form D-400 Schedule A, from the D-401 instructions. The text is the same, on PDF p. 20, in 2021, 2022, 2023, 2024 and 2025:

  • Line 3: "Add Lines 1 and 2" (home mortgage interest and real estate property taxes).
  • Line 4, the limitation, repeats the statute: "the total mortgage interest and real estate property taxes claimed by both spouses combined may not exceed $20,000", prorated "based on the percentage paid by each spouse" or, for joint accounts, "on the income reported by each spouse".
  • Line 5: "Compare Line 3 with Line 4 and enter the smaller amount."

How PE represents spouses filing separately

  • Households: two tax units, one per spouse, each with filing status SEPARATE, and one marital unit holding both spouses. Marital units are not nested in tax units, so this is valid. ar_medical_expense_deduction_floor (Use Arkansas's own medical expense deduction floor #9612) already reaches a separately filing spouse this way. Its tests cover situations without explicit marital units, where policyengine-core puts everyone in one default unit. This PR likewise infers spouses from a two-person marital unit, whether explicit or default, as ruled 4A.
  • Microdata: the profiled default dataset (populace_us_2024.h5@populace-us-2024-spm-20260909) never splits a married couple across tax units.
  • So: the shared-cap guard requires two people in one marital unit who each head a SEPARATE return. A separate filer whose spouse is absent keeps $20,000, which assumes the absent spouse claims nothing (documented below).

Changes

Form D-400 Schedule A lines 3 to 5 become three tax-unit variables. nc_itemized_deductions reads line 5 in place of its inline cap.

Variable Schedule A What it does
nc_mortgage_and_property_tax_before_limitation line 3 mortgage_interest plus real estate taxes up to the federal SALT cap. These are the previous implementation's two terms, moved unchanged. This intermediate calculation now also runs for other-state spouses to supply the shared-cap allocation base.
nc_mortgage_and_property_tax_limitation line 4 $20,000. For two separately filing spouses whose combined line 3 exceeds $20,000, it is $20,000 times their share. Both spouses' payments and income count, including an other-state spouse's. The final limitation remains NC-only.
nc_mortgage_and_property_tax_deduction line 5 The smaller of lines 3 and 4. The final deduction remains NC-only.
mortgage_and_real_estate_taxes_paid_from_joint_account (new input, MaritalUnit, default false) Whether the couple paid their joint mortgage interest and real estate taxes from a joint account.
  • Who shares. The marital unit must hold exactly two people, and each must head a SEPARATE return. A married person meeting IRS Publication 501's "considered unmarried" tests may file head of household while the other spouse files separately. NC's text names spouses "filing as married filing separately or married filing jointly", and federal law treats the head-of-household spouse as unmarried, so that mixed-status pair does not share the cap (ruled 4A).
  • Shares. By default, each spouse's share is their line 3 over the couple's combined line 3. With the joint-account input, it is their federal AGI (not below zero) over the couple's; the shares are equal when neither spouse has positive AGI.
  • Other-state spouse. Removing the NC restriction from the intermediate line-3 calculation lets their payments enter the $20,000 trigger and payment-based proration. Their AGI also enters joint-account proration. Their own state's tax is unchanged; lines 4 and 5 remain NC-only.
  • Unchanged: joint returns (the return holds both spouses, so the $20,000 applies once), single and head-of-household returns, separate returns whose spouse is absent, and any separate couple whose combined line 3 is at most $20,000.

Open readings (d1157)

These were the original interpretation questions. The full write-up was expanded to five choices in ~/reviews/us-hub/nc-mfs-cap/decision-v2.md; Max resolved them under d1351, as recorded in the next section.

  1. "Percentage paid" is a share of each spouse's Schedule A line 3 (1A). The alternative is gross payments, including property taxes above the SALT cap. With no interest above the federal debt limit, $10,000 interest plus $9,000 property tax for one spouse and $10,000 interest for the other produce $12,000 / $8,000 under line-3 allocation, versus $13,103.45 / $6,896.55 under gross-payment allocation. The branch still reads mortgage_interest, including non-deductible interest; the separate federal-interest fix is a prerequisite to excluding that interest from line 3.
  2. Joint-account proration follows the form on the amounts entered (2A). Each spouse's line 4 is $20,000 times their income share, and line 5 is the smaller of their line 3 and line 4.
    • No spouse deducts more than their own entered line 3.
    • Just above $20,000 the couple's total can fall below $20,000. With income shares of 10% and 90%, payments of $5,000 and $15,000 give $20,000; $6,000 and $15,000 give $17,000.
    • One alternative reallocates the full $20,000 by income, even above a spouse's entered line 3. Another attributes joint-account expenses by income before computing line 3, then applies line 5. That attribution can also change below-cap results.
    • PE's per-person expense inputs carry the user's attribution. A separate tax unit receives its own entered mortgage interest; PE does not split joint-account payments across the spouses' separate returns. Entering payments already split by income yields the income-attribution result.
  3. "Income reported by each spouse" is federal AGI (3A) (D-400 line 6), not below zero. The alternatives are gross income or wages. NC taxable income would be circular.

The 2001 NCDOR hearing describes an audit allocation using gross income (finding 5, PDF p. 3), but its final reasoning uses adjusted gross income (PDF p. 5). 17 NCAC 06B .0112(b) also allocates joint-account deductions by AGI. Both concern joint federal returns with separate NC returns, a related but distinct arrangement.

Holding the line-3 amounts and income definition fixed, the form-based joint-account reading and full-cap income reallocation agree above the cap whenever each spouse's line 3 is at least their income share of $20,000. For the mortgage-interest-only examples here, all three choice-2 readings agree when the entered payments are already split by income. These comparisons concern choice 2; choice 1's allocation bases can produce different results.

Rulings (Max, d1351, 2026-10-12)

  1. 1A: use each spouse's Schedule A line 3. Percentage-paid allocation uses mortgage interest plus real estate taxes after the federal SALT cap. Schedule A line 1 still reads mortgage_interest (deductible and non-deductible) until the separate Alabama/North Carolina federal-interest fix lands. Branch al-nc-mortgage-interest-federal-cap is not yet a PR, waits on Apply federal mortgage debt limits by loan vintage, including grandfathered pre-1987 debt #10025, and switches line 1 to deductible_mortgage_interest. That change is outside this PR.
  2. 2A: apply line 5 to the payments entered for each spouse. For joint-account payments over the combined cap, line 4 is $20,000 times the spouse's income share and line 5 is min(their line 3, line 4). PE does not attribute a joint-account payment across separate tax units. A user who enters each spouse's payments already split by income gets the income-attribution result: entering $2,100 / $18,900 with income shares 10% / 90% gives $2,000 / $18,000.
  3. 3A: use federal AGI. Income shares use max(AGI, 0); if both spouses have no positive AGI, divide equally.
  4. 4A: share only for two SEPARATE heads, inferred from a two-person marital unit. The marital unit may be explicit or core's default. A married person meeting IRS Publication 501's "considered unmarried" tests may file head of household while the other spouse files separately. NC names spouses "filing as married filing separately or married filing jointly"; federal law treats the head-of-household spouse as unmarried, so this mixed-status pair does not share the cap.
  5. 5B: include an other-state spouse fully in the allocation base. In a hand-built situation with two households in one marital unit, calculate both spouses' line 3 from the same federal amounts outside the NC restriction. Their payments enter the $20,000 trigger and payment-based allocation, and their AGI enters joint-account allocation. Their own state's tax is unchanged.

Household example

The original branch's household evidence compared against main at ff479aa: two spouses filing separately in 2024, each with $90,000 of wages, $15,000 of mortgage interest and $1,000 of cash charity, in two tax units linked by a marital unit (~/reviews/us-hub/nc-mfs-cap/households.py; evidence/households_{main,branch}.json). These ordinary NC-couple results are unchanged by ruling 5B.

Per spouse main (ff479aa) This PR
Mortgage interest and property taxes after limitation 15,000 10,000
nc_itemized_deductions 16,000 11,000
Deduction taken (standard is 12,750) 16,000 12,750
nc_income_tax 3,330.00 3,476.25

The original household evidence showed $146.25 more per spouse in 2024, $138.12 more in 2025 and $111.72 more in 2026. The YAML integration case gives each spouse $5,000 of charity so both keep itemizing: $20,000 of itemized deductions each under the previous implementation, $15,000 here, and NC income tax rises from $3,150 to $3,375 each.

Other 2024 couples from the same original script:

Couple main (ff479aa) This PR
$18,000 vs $2,000 interest + $4,000 property tax 18,000 / 6,000 15,000 / 5,000
Joint account, $15,000 each, AGI $120,000 vs $40,000 15,000 / 15,000 15,000 / 5,000
$8,000 vs $7,000 (below $20,000) 8,000 / 7,000 8,000 / 7,000

The added cross-state cases use equal incomes and mortgage-interest amounts as their line 3, so no property-tax cap changes the arithmetic:

NC / other-state payments Calculation NC deduction
Joint account, $15,000 / $15,000 $30,000 > $20,000; min($15,000, $20,000 × 1/2) 10,000
Joint account, $25,000 / $15,000 $40,000 > $20,000; min($25,000, $20,000 × 1/2) 10,000
No joint account, $15,000 / $15,000 min($15,000, $20,000 × $15,000 / $30,000) 10,000
No joint account, $25,000 / $15,000 min($25,000, $20,000 × $25,000 / $40,000) 12,500

Invariants

The property test samples finite nonnegative line-3 amounts in valid North Carolina tax units with a head role: $0–$45,000 initially, increases of $0–$20,000, and AGI of −$30,000–$300,000 in 2024 or 2026. Shared-cap assertions require two SEPARATE heads in a two-person marital unit. The generated populations use explicit marital units and NC households; deterministic YAML separately covers implicit units, head-of-household/separate pairs, headless returns, and cross-state spouses.

  1. Shared cap. For two spouses filing separately and linked in one marital unit, their lines 5 add up to at most $20,000.
    • Without a joint account they add up to exactly min(combined line 3, $20,000), so nothing is lost or created.
  2. Bounds. 0 ≤ line 5 ≤ line 3 for each return in this domain.
  3. Only spouses share. Other NC returns get min(line 3, $20,000), as under the previous implementation: joint, single, head of household, a separate filer whose spouse is absent, and separate filers not paired by a two-person marital unit with two SEPARATE heads.
  4. Label invariance. Which spouse is listed first does not change either spouse's result.
  5. Monotone without a joint account.
    • Paying more never lowers the payer's deduction or the couple's total.
    • It never raises the other spouse's deduction.
    • With a joint account the total can fall just above $20,000, as choice 2 describes. That is intended under ruling 2A.

Negative expense inputs are outside the tested domain; the pre-existing minimum can produce a negative deduction for negative line 3. Headless tax units do not satisfy the spouse guard and retain the individual $20,000 limitation. The headless YAML case documents that fallback; the shared-cap invariant does not apply to it.

Tests

  • YAML, with expectations worked from the statute in the case comments: 38 cases across the four PR YAML files.
    • nc_mortgage_and_property_tax_limitation.yaml (19 cases): the original 17 cases plus the head-of-household/separate pair and headless-return fallback. Existing coverage includes single; joint; equal, unequal, below-cap and exactly-$20,000 separate couples; joint-account proration with positive, negative and zero income; joint-account payments below the cap; property taxes above the federal cap; a separate return with a dependent; a lone separate filer; implicit marital units containing two separate filers, a separate and single filer, or three separate filers; and 2026.
    • nc_mortgage_and_property_tax_deduction.yaml (10 cases): the original 6 cases (5 calculating person-level expenses and 1 taking line 3 as an input), plus the 4 cross-state cases above using person-level home_mortgage_interest. The original coverage includes a joint-account case where line 5 takes line 3 because the income share is larger.
    • nc_mortgage_and_property_tax_before_limitation.yaml (3 cases).
    • nc_itemized_deductions.yaml (6 cases): 5 pre-existing cases, including main's self-employed health insurance regression, and the end-to-end separate-spouse case from wages to nc_income_tax.
  • Historical baseline evidence: the original branch's new nc_itemized_deductions.yaml case failed on main at ff479aa ($20,000 each instead of $15,000); its other 4 cases passed on both (evidence/yaml_on_main.log). The three new variables did not exist on that baseline. Current verification results are recorded below.
  • Property test: tests/core/test_nc_mortgage_and_property_tax_limitation_invariants.py contains 2 pytest tests and checks invariants 1 to 5 within the domain stated above. It also checks lines 4 and 5 against a separate arithmetic restatement of the chosen interpretation, with $20,000 taken from the statute rather than parameter files. The oracle is computationally separate from production but shares its legal assumptions.
    • Across 2024 and 2026, it draws 6 Hypothesis populations of 5 to 25 returns and checks two seeded populations of 120 records. Each population is built three ways: as drawn, with spouses swapped, and with payments raised, normally 24 simulations in total. The populations include separate couples with and without a joint account, joint, single, lone separate, and separate with a dependent.
    • Line 3 and AGI are inputs, so no federal calculation runs in these property tests.
    • Why Python as well as YAML: YAML supports mixed populations in a vectorized simulation and supplies deterministic examples. Python adds generated populations, Hypothesis shrinking, and dynamic comparisons under spouse relabeling and increased payments.
    • Prior mutation checks: the original branch's evidence reported that each of these failed the property file: (1) the previous per-return $20,000 rule; (2) ignoring the joint-account input; (3) not flooring negative AGI. Hypothesis shrank each counterexample (4.5 to 6 minutes per mutation). These mutation checks were not rerun for d1351.
    • Cost: the original isolated local macOS arm64 measurement was 18.8 s in pytest, 49.6 s wall with imports, peak RSS 1.10 GB. These are historical local measurements, not CI or incremental-cost measurements. The file remains registered once in the Rest Python core group and adds no runner. CI publishes the affected group's rest-python-core.xml timing and rest-python-core-resources.txt elapsed-time/peak-RSS reports in rest-test-timings. No before/after reports exist in the local evidence, and GitHub API requests for existing runs and artifacts failed with connection errors, so affected-group Linux runtime and peak-memory measurements are unavailable. The Makefile's estimated 142-second note is historical context rather than a measurement of this change.

Run by the hub at 0e1277d23afad2b24e2a4430c25777cd974cf997 on 2026-10-12, one file per process directly in the assigned sandbox, with thread caps (OMP_NUM_THREADS, OPENBLAS_NUM_THREADS, MKL_NUM_THREADS, VECLIB_MAXIMUM_THREADS, and NUMEXPR_NUM_THREADS all 4; OMP_WAIT_POLICY=PASSIVE), without the shared heavy-job lock:

File Result
itemized/nc_claim_of_right_deduction.yaml 17 passed
military_retirement/nc_military_retirement_deduction.yaml 14 passed
military_retirement/nc_military_retirement_deduction_eligible.yaml 4 passed
nc_child_deduction.yaml 7 passed
nc_itemized_deductions.yaml 6 passed
nc_mortgage_and_property_tax_before_limitation.yaml 3 passed
nc_mortgage_and_property_tax_deduction.yaml 10 passed
nc_mortgage_and_property_tax_limitation.yaml 19 passed
nc_standard_deduction.yaml 3 passed
nc_standard_or_itemized_deductions.yaml 1 passed
tests/core/test_nc_mortgage_and_property_tax_limitation_invariants.py 2 passed

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base de3815f4cc against this PR's head 0e1277d23a, for 2024 and 2026. The runs went one at a time under the shared heavy-job lock. Sums are weighted.

Output 2024 change 2026 change 2024 records changed (weighted) 2026 records changed (weighted)
nc_income_tax -0 +0 0 (0) 0 (0)
nc_itemized_deductions +101 -96 0 (0) 0 (0)
nc_taxable_income -0 +0 2 (1) 0 (0)
state_income_tax -0 +0 0 (0) 0 (0)
income_tax +0 +0 0 (0) 0 (0)
household_net_income +0 +0 0 (0) 0 (0)
household_benefits +0 +0 0 (0) 0 (0)

The two 2024 nc_taxable_income records differ by less than one cent each (largest $0.0078); no tax, income or benefit output changes in either year.

Comparison files are in fixes/10029-impact/ on the hub host.

Microsimulation impact

No main-versus-branch microsimulation output is available for this PR. The existing dataset profile shows zero two-person marital units crossing tax units; each separate filer is alone in their marital unit. Consequently no record in that profiled dataset reaches the shared-cap guard, including the new cross-state path, so its state tax results cannot change from this cap fix. The now unrestricted intermediate line-3 variable can return amounts for non-NC tax units when queried, but the final NC limitation and deduction remain restricted to NC and no other state's tax changes.

Known limitations

  • Absent spouse. A separate filer whose spouse is not in the simulation keeps the full $20,000. This is always the case for separate filers in the profiled default dataset. The shared cap needs the other spouse's amounts, so this assumes the absent spouse claims nothing.
  • Two separate filers without marital units. When marital units are omitted, two separate filers alone in a situation are inferred to be spouses, as ruled 4A. That default unit establishes the relationship in the model; it does not prove marriage. Two people married to other absent spouses can therefore share the cap incorrectly if simulated together without explicit marital units.
  • Spouses in different states. These require a hand-built situation with two households linked by a marital unit. Under ruled 5B, both spouses' line-3 payments and AGI count in the NC spouse's allocation; the other spouse's own state tax is unchanged.
  • Mixed payments. The joint-account input covers a couple's payments as a whole. A couple that paid some joint obligations from a joint account and some individually cannot split them.
  • Negative expenses and headless returns. The property invariants do not cover negative line-3 amounts or tax units lacking a head. Negative line 3 can retain the pre-existing negative deduction; a headless return retains the individual cap because the spouse guard requires heads.

Overlaps and merge order

Four pieces of work touch this formula:

  • AL/NC federal limit (branch al-nc-mortgage-interest-federal-cap, not yet a PR; waits on Apply federal mortgage debt limits by loan vintage, including grandfathered pre-1987 debt #10025): changes line 1 from mortgage_interest to deductible_mortgage_interest. After this PR that line lives in nc_mortgage_and_property_tax_before_limitation.py; the edit is the same one line. This will correct both the NC filer's line 3 and the other-state spouse's shared-cap allocation base.
  • NC real estate tax limit (task not started): changes line 2, the min_(real_estate_taxes, cap) term, in the same new file.
  • NC wagering losses and claim-of-right repayments (separate work): adds to other_deductions in nc_itemized_deductions.py. This PR leaves those lines untouched.
  • Whichever lands second resolves a small conflict. The line-3 variable gives the other two changes a separate place to edit.

Follow-ups

  • NC's standard deduction for a separate filer should be $0 when the spouse itemizes (G.S. 105-153.5(a)(1), 26 U.S.C. 63(c)(6)(A)); PE never checks this. A separate task covers it.

axiom: TheAxiomFoundation/rulespec-us#1723 queued

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 9, 2026 09:16
…spouses filing separately

G.S. 105-153.5(a)(2)b caps mortgage interest plus real estate taxes at
$20,000 "claimed by both spouses combined" for spouses filing separately,
prorated by the share each paid or, for joint obligations paid from joint
accounts, by each spouse's income. PE gave each tax unit its own $20,000.

Split Form D-400 Schedule A lines 3, 4 and 5 into
nc_mortgage_and_property_tax_before_limitation, _limitation and _deduction.
Line 4 reaches a separately filing spouse in another tax unit through a
two-person marital unit whose members each head a SEPARATE return (the
Arkansas medical-floor guard). New MaritalUnit input
mortgage_and_real_estate_taxes_paid_from_joint_account switches the shares
from line 3 amounts to federal AGI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis MaxGhenis closed this Oct 13, 2026
@MaxGhenis MaxGhenis reopened this Oct 13, 2026
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 13, 2026 05:05
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (909176a) to head (0e1277d).
⚠️ Report is 469 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10029   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            4         4           
  Lines           76        73    -3     
  Branches         2         0    -2     
=========================================
- Hits            76        73    -3     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MaxGhenis MaxGhenis closed this Oct 13, 2026
@MaxGhenis MaxGhenis reopened this Oct 13, 2026
@MaxGhenis
MaxGhenis merged commit fd12ca6 into main Oct 14, 2026
67 of 68 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10029 at 0e1277d23afad2b24e2a4430c25777cd974cf997 (squash): Share North Carolina's $20,000 mortgage and property tax cap between spouses filing separately.

@MaxGhenis
MaxGhenis deleted the nc-mortgage-property-cap-mfs branch October 14, 2026 00:15
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