Skip to content

Apply New York's pre-TCJA mortgage debt limits to the interest deduction - #10596

Open
MaxGhenis wants to merge 6 commits into
mainfrom
ny-pre-tcja-mortgage-interest
Open

MaxGhenis wants to merge 6 commits into
mainfrom
ny-pre-tcja-mortgage-interest

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

CI: the Pull request run at 53f8806db4 passed, all 35 jobs succeeded (run 41588428535). The hub lands this after an independent review.

Source

The hub checked each quote above against its source on 2026-11-02.

What changed

On main, ny_itemized_deductions_max adds the federal interest_deduction. For loans from 2018 or later its mortgage part uses the federal $750,000 limit ($375,000 separate) and allows no home equity debt. New York's preserved rules allow more.

  • ny_mortgage_interest_deduction (new). From 2018 it applies the pre-TCJA Table 1:
    • grandfathered debt (origination year 1987 or earlier) has no dollar limit and reduces the $1,000,000 limit;
    • acquisition debt above the limit, plus new non-acquisition debt, counts as home equity debt, up to $100,000;
    • on each home, the home's fair market value less the debt on it treated as acquisition or grandfathered debt caps home equity debt; a value of 0 means unknown and sets no such cap;
    • the deductible share is the larger of the worksheet's three-place share and the unrounded share (qualified debt over total debt), times raw interest (federally deductible plus non-deductible interest and points).
    • Before 2018 it returns the federal deductible_mortgage_interest.
  • ny_interest_deduction (new) = federal interest_deduction − federal deductible_mortgage_interest + ny_mortgage_interest_deduction. Starting from interest_deduction keeps an interest total supplied directly as deductible_interest_expense, which ny_itemized_deductions_max.yaml uses.
  • ny_itemized_deductions_max adds ny_interest_deduction instead of interest_deduction. Its existing subtracts of disallowed_interest_components (mortgage insurance premiums) is unchanged.
  • Parameters gov.states.ny.tax.income.deductions.itemized.interest.home_acquisition_debt_limit (1,000,000 / SEPARATE 500,000) and home_equity_debt_limit (100,000 / SEPARATE 50,000), from 2018-01-01.
  • New inputs in ny_mortgage_interest_inputs.py: ny_first_home_fair_market_value, ny_second_home_fair_market_value, ny_first_home_non_acquisition_debt, ny_second_home_non_acquisition_debt.

New inputs and their defaults

All four inputs default to 0.

  • A fair market value of 0 means unknown: the home counts as worth at least its debt, so only the $100,000 ($50,000 separate) home equity debt limit applies, as in the merged Hawaii calculation.
  • With no non-acquisition debt, the calculation adds nothing.

So when a caller supplies neither input, the changes from main are the larger acquisition-debt limit, home equity treatment of acquisition debt above it (up to $100,000), and the larger of the two shares. The property test checks the default limit (property 3).

PE choices (judgment calls)

  1. Federal pass-through. When federal law disallows nothing and the household reports no non-acquisition debt, the federal amount passes through, which keeps a deductible_mortgage_interest supplied directly (taxsim and many integration tests do this). There is no federal floor now. The pass-through stays because without it New York would limit such a deduction a second time when balances are present: the case "A directly supplied deduction passes through even with balances" gives 12,000, where the worksheet path would give 12,000 × .7333 = 8,800.
  2. Allocation method: the larger share. Until regulations issue, the House report on OBRA 1987 allows any reasonable allocation method, and CCA 201201017 accepts both the Publication 936 worksheet (three-place rounding) and the regulation's simplified method (Treas. Reg. 1.163-10T(d), unrounded). Under 1.163-10T(g) the taxpayer chooses, so New York allows interest times the larger of the two shares. New York's limits never fall below the federal ones for the same loans, so with no non-acquisition debt the result never falls below PE's unrounded federal amount.
  3. Sharing the acquisition limit between two homes: PE counts grandfathered debt in full on its own home and shares the rest of the limit in proportion to each home's other acquisition debt. The statute's limit is an aggregate and doesn't say how to place it on each home. This only matters for the per-home fair market value room.
  4. Unknown fair market value: 0 means unknown, as for the origination-year inputs, and the home counts as worth at least its debt (the merged Hawaii calculation's rule). A positive value applies (C)(i) home by home.
  5. Grandfathering cutoff: this PR keeps the existing origination-year convention (1987 or earlier = on or before October 13, 1987), shared with the federal and Hawaii calculations. The follow-up is one exact-date input that the federal, Hawaii and New York calculations all read.
  6. Before 2018 New York uses the federal amount, per the 2018 effective date.

Federal follow-up

PE's federal calculation reads only the two acquisition balances, so after 2017 it treats home equity interest included in home_mortgage_interest like interest on those balances: fully deductible when they fit the federal limit, and deductible at the same share when they don't. The input documentation now says so, and a follow-up will move that balance to a shared input that the federal calculation disallows.

Extra files beyond the spec's list

  • ny_itemized_deductions_phase_out.py: a docstring now names ny_interest_deduction, the variable the worksheet's line 1 now includes.
  • policyengine_us/tests/core/test_ny_mortgage_debt_limit_invariants.py: the Hypothesis property test.
  • changelog.d/ny-pre-tcja-mortgage-interest.fixed.md.
  • hi_mortgage_interest_deduction.py: its docstring now says PolicyEngine has no Hawaii home value input, since this PR adds New York ones.
  • source_conflicts/candidates.yaml: the us:mortgage-interest-limit-ratio-rounding candidate now lists the New York file and this PR, and says PE-US follows three conventions, New York's being the larger of the three-place share and the exact ratio.

Witness and years

Bug confirmed on origin/main (0ea8894) with a failing YAML case: 2025, single, $36,000 interest, $900,000 loan from 2020. ny_itemized_deductions_max was 30,000; expected 36,000.

Case (2025 unless noted) Federal NY
$900k loan, FMV $1.2M 30,000 36,000
$1.5M loan, FMV $2M 20,000 29,333.33 (1.1M/1.5M = .7333; worksheet .733)
SEPARATE, $600k loan, FMV $800k 12,500 18,340 (550k/600k = .9167; worksheet .917)
$1.5M loan, no FMV (default) 20,000 29,333.33 (no room limit; .7333)
$1.5M loan, FMV $1.04M 20,000 27,733.33 (.6933; worksheet .693)
$500k loan + $200k non-acq debt, FMV $1M 35,000 30,000 (6/7; worksheet .857)
$400k loan + $200k non-acq debt on the second home 30,000 25,000 (500k/600k = .8333; worksheet .833)
grandfathered $400k + 2020 $800k 31,250 45,850 (.9167; worksheet .917)
two homes, second without room — 44,000 (.7333; worksheet .733)
2017, $1.5M loan from 2010 26,666.67 26,666.67 (federal)
$1.2M loan from 2015, FMV $1M 50,000 50,000 (unrounded .8333; worksheet 49,980)
$2M loan from 2020, FMV $1.001M 30,000 40,080 (1.001M/2M = .5005, an exact tie; worksheet .501)
$300k loan from 2020 + $50k non-acq debt, no FMV 20,000 20,000 (share 1; 17,140 at the old head)
deductible_mortgage_interest 12,000 supplied, $1.5M loan from 2020 — 12,000 (pass-through; 8,800 without it)

Years: 2018 onward.

Invariants

  • grandfathered debt ≤ NY qualified debt ≤ total debt;
  • NY qualified debt ≥ PE's federal qualified debt for the same acquisition loans;
  • with the defaults (no FMV, no non-acquisition debt), NY qualified debt = line 6 + min(acquisition debt − line 6, home equity limit), where line 6 = min(max(G, limit), acquisition debt), which the Hawaii helper also gives;
  • swapping the two homes never changes the result;
  • a higher positive fair market value never lowers the result;
  • setting every fair market value to 0 (unknown) never lowers the result;
  • for interest on two homes with no non-acquisition debt, split by PE's federal share: federal deduction ≤ NY deduction ≤ interest paid, and NY = federal when federal disallows nothing; the federal bound follows from the limits, not from a floor;
  • the share is at least the unrounded ratio and the three-place rounding, at most 1, and exceeds the ratio by no more than .0005.

policyengine_us/tests/core/test_ny_mortgage_debt_limit_invariants.py checks all of these with Hypothesis (400 derandomized examples, every filing status, 2018/2025/2026 parameters), plus an explicit exact tie (1,001 / 2,000) for the share. It also checks the helper against a separately written statute-order restatement, which places the limit home by home. The restatement shares the limit between homes the same way (judgment call 3), so it can't catch a wrong sharing rule.

Tests (local, one file per process)

  • At 53f8806db4: ny_mortgage_interest_deduction.yaml 16/16; Hypothesis test_ny_mortgage_debt_limit_invariants.py 4/4; policyengine_us/tests/code_health/test_source_conflicts.py 103 passed.
  • Readers, at 7538a504f8: ny_itemized_deductions_max 5, ny_itemized_deductions_phase_out 7, ny_pre_tcja_deductions 6, ny_itemizes 3, ny_deductions 1, ny_taxable_income 1, ny_income_tax 3, NY integration 11, HI hi_mortgage_interest_deduction 22, federal deductible_mortgage_interest 16 and deductible_mortgage_interest_tax_unit 18. All pass.
  • No microsimulation or full suite was run locally (the hub measures impact). CI at 53f8806db4 passed.

Mutants (each in its own process, code restored after)

(a)-(f) ran at 7538a504f8, with 14 YAML cases.

Mutant YAML (14) Hypothesis (4)
(a) share is the worksheet share only 6 failed (the five .733/.693/.857 cases and the round-down case) 2 failed (allowed interest, share)
(b) share is the unrounded ratio only 2 failed (separate returns, grandfathered debt) 1 failed (share)
(c) a value of 0 gives no room again 2 failed (no FMV dollar limit; no FMV keeps home equity interest) 1 failed (loan-limit properties)
(d) no pass-through 1 failed (directly supplied deduction with balances) passes (equivalent there: the federal share is 1 when nothing is disallowed)
(e) acquisition limit 750,000 / 375,000 8 failed 3 failed
(f) ny_interest_deduction keeps the federal mortgage part too 1 failed (first case's ny_interest_deduction) passes (helpers untouched)

(g)-(i) ran at 53f8806db4, with 16 YAML cases:

  • (g) the share's floor without np.round: YAML 1 failed (the exact tie, 40,040 instead of 40,080); Hypothesis 1 failed (the share test, at its @example(limit=1_001.0, debt=2_000.0)).
  • (h) the formula ignores ny_second_home_non_acquisition_debt: YAML 1 failed (the second-home case, 30,000 instead of 25,000). Before that case no test read the input.
  • (i) Hawaii's line 6 ignores grandfathered debt above the limit: Hypothesis 1 failed (the loan-limit properties, at the Hawaii assertion; one $1,100,001 grandfathered loan gives 1,100,001 against 1,100,000).

Readers

ny_mortgage_interest_deduction → ny_interest_deduction → ny_itemized_deductions_max, which is read by ny_itemized_deductions, ny_itemized_deductions_phase_out, and ny_itemized_deductions_{higher,lower}_incremental_reduction.

impact: pending (hub)
axiom: (hub files pe-parity before landing)

🤖 Generated with Claude Code

N.Y. Tax Law 615(a) computes New York itemized deductions as they existed
before P.L. 115-97, but ny_itemized_deductions_max added the federal
interest_deduction, so post-2017 loans got the federal $750,000 limit and
no home equity debt. A new ny_mortgage_interest_deduction applies the
preserved $1,000,000 / $100,000 limits (halved for separate returns) with
per-home fair market value room, and ny_interest_deduction substitutes it
for the federal mortgage component. New inputs default to values that add
no home equity debt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6J5LH2sLC5oX6pUHnC
Building ny_interest_deduction from its components dropped an interest
total supplied as deductible_interest_expense, which
ny_itemized_deductions_max.yaml uses. Start from interest_deduction and
swap only the federal mortgage component for New York's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6J5LH2sLC5oX6pUHnC
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 4b893cfcb9df1d8c150f0aa33f9f4c29d4df38db (branch ny-pre-tcja-mortgage-interest, from origin/main 0ea8894). Backlog item us-ny-mortgage-interest-pre-tcja.

Ownership check

Reproduced on main

2025, single, $36,000 interest, $900,000 loan from 2020: ny_itemized_deductions_max was 30000 on main; expected 36000 (1 failed).

What the item asked, and where

  • NY mortgage calculation from 2018 that replaces the federal mortgage component: ny_mortgage_interest_deduction.py (formula_2018; formula returns federal before 2018).
  • Interest aggregate that keeps non-mortgage interest and the downstream limits: ny_interest_deduction.py (interest_deduction − federal mortgage + NY mortgage). ny_itemized_deductions_max.py:32 now adds it. The subtracts of mortgage insurance premiums, the phase-out and the reductions are unchanged.
  • Limits 1,000,000 / 500,000 SEPARATE and 100,000 / 50,000 SEPARATE: parameters/.../itemized/interest/home_acquisition_debt_limit.yaml and home_equity_debt_limit.yaml. Values are from the spec.
  • Grandfathered debt and its effect on the remaining limit: _ny_qualified_loan_limit, line 6.
  • Per-home FMV limit, and home equity treatment of excess acquisition debt: the per-home loop in _ny_qualified_loan_limit.
  • New inputs (ny_mortgage_interest_inputs.py): ny_{first,second}_home_fair_market_value and ny_{first,second}_home_non_acquisition_debt, all defaulting to 0.
    • FMV 0 means no home equity room, and non-acquisition debt 0 adds nothing. So without them the only change from main is the $1,000,000 acquisition limit.
  • Raw interest, not the recapped federal amount: the worksheet uses deductible + non-deductible interest and points.
  • Three-place rounding: _ny_deductible_share.
  • The spec's three witnesses (36000, 29320, 18340) pass exactly.

Hand computations

All are in the YAML comments.

  • 36,000 (900k ≤ 1M).
  • 29,320 = 40,000 × .733 (1.1M/1.5M).
  • 18,340 = 20,000 × .917 (550k/600k).
  • 26,680 = 40,000 × .667 (no FMV).
  • 27,720 = 40,000 × .693 (FMV room 40k).
  • 29,995 = 35,000 × .857 (500k + 100k of 200k non-acquisition debt, over 700k).
  • 45,850 = 50,000 × .917 (grandfathered 400k + 600k, plus 100k home equity, over 1.2M). Federal here is 31,250 = 50,000 × 750k/1.2M.
  • 43,980 = 60,000 × .733 (two homes, second has no room).
  • 2017 = federal 26,666.67.
  • Rounding floor: 50,000, where the worksheet gives 49,980.

Tests (local, one file per process)

  • New YAML 12/12.
  • Hypothesis 3/3 (400 derandomized examples per property).
  • Readers, all passing: ny_itemized_deductions_max 5, phase_out 7, phase_out_integration 2, reduction 10, higher_incremental 2, lower_incremental 3, incremental 3, ny_pre_tcja_deductions 6, ny_itemizes 3, ny_deductions 1, ny_taxable_income 1, ny_income_tax 3, NY integration 11, hi_mortgage_interest_deduction 22, deductible_mortgage_interest 16.
  • Partner files that mention mortgage inputs: analytics_coverage/.../housing_assistance/federal.yaml 11 and analytics_coverage/signatures/federal.yaml 18, both passing. No partner file changed or moved. No partner file uses NY.
  • The first commit broke ny_itemized_deductions_max.yaml (5 failed), because those cases supply deductible_interest_expense directly. Fixed in 4b893cf, and the file was rerun green.
  • No microsimulation or full suite was run.

Mutants

Each ran in its own process in a separate worktree, with the code restored after. All 7 are killed:

  • M1 federal interest back in max: YAML 1 failed.
  • M2 limit 750k: YAML 4, Hypothesis 1.
  • M3 no home equity: YAML 6, Hypothesis 1.
  • M4 no FMV room: YAML 3, Hypothesis 1.
  • M5 no federal floor: YAML 1, Hypothesis 1.
  • M6 NY calc before 2018: YAML 1.
  • M7 federal mortgage double-counted: YAML 1.

CI

The PR is a draft, so the Pull request run at 4b893cf shows completed / skipped. CI has not run.

Judgment calls

  1. Grandfathering date. I did not add an exact October 13, 1987 input. The spec asks for one. I kept the shared origination-year convention so that one loan can't have two vintages between the federal and NY calculations. Hub to decide; follow-up proposed below.
  2. Federal pass-through and floor, copied from the merged Hawaii calculation. When nothing is disallowed federally and no non-acquisition debt is reported, the federal amount passes through, which keeps direct deductible_mortgage_interest inputs. Otherwise the worksheet result is floored at federal, except when non-acquisition debt is reported. This floor is PE's choice, not the worksheet.
  3. Sharing the limit between two homes. Grandfathered debt stays on its own home, and the rest of the limit is shared in proportion to each home's other acquisition debt. The statute's limit is an aggregate.
  4. FMV default 0 means no home equity room. This differs from Hawaii, which assumes FMV ≥ debt. I chose it so the new input, when absent, keeps main's (zero) home equity result, as the brief asks.
  5. Before 2018 NY uses the federal amount, per the spec's "effective 2018".
  6. Axiom line. The brief says both to add axiom: pending: hub files parity and never to write it. I wrote axiom: needed instead. The hub should replace it with the parity line it files.
  7. Sources. None of the sources were reachable from this VM (nysenate, tax.ny.gov, govinfo and irs.gov all returned 403 at the proxy). The quotes come from the spec's excerpt fields. Unverified: the Publication 936 (2017) home equity FMV rule (PDF page 10) and the Table 1 line wording. They are paraphrased, and the reviewer should read them.

Extra files beyond the spec's list

  • phase_out/ny_itemized_deductions_phase_out.py (docstring only).
  • tests/core/test_ny_mortgage_debt_limit_invariants.py.
  • changelog.d/ny-pre-tcja-mortgage-interest.fixed.md.

Proposed follow-ups

  • Exact grandfathered-debt flag for mortgage limits. Add a per-loan input marking debt secured on or before October 13, 1987, read by the federal, HI and NY calculations in place of the 1987 origination-year proxy.
  • Federal treatment of non-acquisition home debt. The federal calculation can't see home equity debt, so after 2017 it allows interest on such debt if a user includes it in home_mortgage_interest. Federal could read the new non-acquisition debt (moved to a shared input) and disallow it through 2025 and beyond under 163(h)(3)(F).

Generated by Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review November 2, 2026 16:07
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
4b893cf
METHODOLOGY_CHOICE: yes

Independent review, round 1, of head 4b893cfcb9df1d8c150f0aa33f9f4c29d4df38db. The head has not moved since the hand-off. I found no earlier "Hub review in progress" comment, so this review starts from scratch. Main is now 4bfc548de3. No NY or mortgage-structure file has changed on main since the PR's base 0ea88946.

Sources. No source was reachable from this VM. govinfo.gov, uscode.house.gov, ecfr.gov, irs.gov, nysenate.gov, legislation.nysenate.gov and tax.ny.gov all returned CONNECT tunnel failed, response 403 (curl) or did not resolve (fetch tool). So I confirmed none of § 615(a), pre-TCJA 26 U.S.C. 163(h)(3), Publication 936 (2017) or the NY instructions. The limits, the worksheet line structure and the per-home fair market value rule below are checked only against the backlog spec's excerpts and against the merged Hawaii code (#10274). They still need a reviewer who can read the sources (see "What I couldn't check").

Findings

P2-1. A fair market value of 0 (the default) disallows interest the law allows, and can take New York below both main and federal

ny_mortgage_interest_deduction.py:49 (equity_room = max_(fair_market_value - treated, 0)) with ny_mortgage_interest_inputs.py:22,38 (default_value = 0).

An unknown home value is read as a home worth $0. Any debt counted as home equity debt then gets no room, so its interest is disallowed. On top of that, because a non-acquisition balance is added to line 9's total debt, the share can fall below federal, and the floor is turned off when non-acquisition debt is reported (_ny_allowed_interest, line 92).

Witnesses, run at this head (policyengine_core ... test pr_witness.yaml, 7 passed, meaning these outputs are what the code produces):

2025, single federal NY at this head
W1: $20,000 interest, no acquisition balance, ny_first_home_non_acquisition_debt 50,000, no FMV 20,000 0
W2: $20,000 interest, $300,000 loan from 2020, non-acquisition debt 50,000, no FMV 20,000 17,140 (300k/350k = .857)
W3: same as W2 with FMV $500,000 20,000 20,000

On main both W1 and W2 give New York the federal 20,000. The input's own documentation tells users to "Include the interest on this debt in the home mortgage interest inputs". A user who does that and leaves FMV blank, which the documentation also invites ("Leave at 0 if unknown"), loses interest that New York allows on any home with positive equity.

On survey data the same default denies home equity treatment to every acquisition balance above $1,000,000. The data carries no home value, and these new inputs are 0 for every record. Mortgaged homes with positive equity are the norm, so Hawaii's merged rule (treat every home as worth at least its mortgages) fits survey data better. The hand-off's reason for 0, that it "keeps main's (zero) home equity result", keeps a part of main's bug rather than main's behaviour. Main's result is the federal amount, which the 1M limit already changes.

Suggested replacement for line 49, so that 0 means unknown, as first_home_mortgage_origination_year already does:

        # 0 means unknown: treat the home as worth at least the debt on it,
        # as the Hawaii calculation does.
        equity_room = where(
            fair_market_value > 0, max_(fair_market_value - treated, 0), np.inf
        )

Then update the input documentation (_FAIR_MARKET_VALUE_DEFAULT), the "Without a fair market value" case (26,680 becomes 29,320) and the rounding case (1.1M/1.2M = .917, so 55,020), and change Hypothesis property 3 to min(max(G, L), acquisition) + min(excess, home_equity_limit). If the hub keeps 0 = no room, the floor should at least stay on for the acquisition part, and the W1 case should be a test that documents it.

P2-2. The new input tells users to make the federal deduction wrong

ny_mortgage_interest_inputs.py:9-13, and the PR's own test at ny_mortgage_interest_deduction.yaml:101-116.

The hand-off's follow-up claim is confirmed. The federal calculation sees only the two acquisition balances, so interest on home equity debt that is folded into home_mortgage_interest is allowed in full after 2017. Main witness (2025, joint, $35,000 interest, $500,000 loan from 2015, run at main and at this head): deductible_mortgage_interest_tax_unit = 35,000 and non_deductible_mortgage_interest_tax_unit = 0 (2 passed in each tree). The PR's own case asserts federal 35,000 next to $200,000 of non-acquisition debt.

That gap exists on main. But this PR adds an input whose documentation directs every NY user with home equity debt to put its interest in home_mortgage_interest, which overstates federal Schedule A (and federal tax) for those users. The PR should either say so in the input documentation, for example:

"Include the interest on this debt in the home mortgage interest inputs. "
"PolicyEngine's federal calculation cannot see this debt and so does not "
"disallow that interest federally after 2017. Defaults to 0, ..."

or the hub should file the federal follow-up the hand-off proposes (move the non-acquisition balance to a shared input that the federal calculation reads and disallows under 163(h)(3)(F)) before this lands. I couldn't read 163(h)(3)(F) to quote it.

P3-1. Floor at federal versus the worksheet: a source conflict for the hub to rule on (METHODOLOGY_CHOICE)

ny_mortgage_interest_deduction.py:90-94; case "Rounding cannot take New York below the federal amount" (ny_mortgage_interest_deduction.yaml:185): 50,000 where the worksheet gives 60,000 × .833 = 49,980.

§ 615(a), as quoted in the spec, adopts the pre-TCJA deduction. That deduction allows interest on qualified indebtedness and prescribes no three-place rounding. The merged Hawaii code cites Treas. Reg. 1.163-10T for that point; I couldn't read it. Under the project's rule, the 2017 worksheet fills that gap, and the worksheet gives 49,980. The floor follows neither source: it is a PE safeguard copied from the merged Hawaii calculation. The exact statutory ratio gives 50,000 here, so the floor and the unrounded statute agree on this witness.

The current mix is asymmetric. Rounding up above the exact amount is kept (the no-FMV case allows 26,680 where the exact amount is 26,666.67). Rounding down below the exact NY amount is kept whenever NY exceeds federal (29,320 against the exact 29,333.33). Rounding down is undone only when it would cross the federal amount. Both the worksheet and the exact ratio are defensible, and the floor is consistent with Hawaii, so this is for the hub to rule on. If the hub keeps it, record it in the register. If not, either drop the rounding (use the exact ratio; the floor is then redundant when no non-acquisition debt is reported) or drop the floor (the worksheet governs, 49,980).

P3-2. Body and hand-off claims that are now false or loose

  • Body line 1, "Draft. CI skips drafts, so it has not run", and the hand-off's "CI has not run". The PR is no longer a draft. The Pull request run at this head (actions run 41478449345, 2026-11-02T16:07Z) completed success on all 35 jobs, with none skipped or cancelled. Replace with: "CI: the Pull request run at 4b893cf passed (all jobs)."
  • "So when they aren't supplied, the only change from main is the larger acquisition-debt limit." The three-place rounding is also new whenever federal disallows anything (for example 26,680 against the exact 26,666.67). Replace with "...is the larger acquisition-debt limit, with the worksheet's three-place rounding of the deductible share."
  • "It also checks the helper against a separately written statute-order restatement." The restatement (statute(), test_ny_mortgage_debt_limit_invariants.py:134) uses the same proportional sharing of the limit between homes (judgment call 3), so it cannot catch a wrong sharing rule. Say so.
  • The body and the hand-off number the judgment calls differently, and the body omits the FMV default (hand-off call 4) from its list.
  • axiom: needed is the external-contributor line in CLAUDE.md. The hub needs to replace it with its parity line.

P3-3. The grandfathering date is still a year proxy (spec deviation)

The spec says the implementation "must obtain that fact rather than infer it from the year". The PR keeps the 1987-or-earlier convention (judgment call 1). Only one witness depends on it, "Grandfathered debt reduces the acquisition-debt limit" (origination 1986), and it is unaffected by the October 13 boundary. No test sits at 1987. The proxy is wrong only for a loan taken out between October 14 and December 31, 1987 that is still outstanding, which is negligible today. I agree with keeping one shared convention rather than a NY-only date. The hand-off's follow-up (one exact-date flag read by the federal, HI and NY calculations) is the right fix.

Checked and sound

  • Defect reproduces on main (4bfc548de3). 2025, single, $36,000 interest, $900,000 loan from 2020: ny_itemized_deductions_max = 30000 against the expected 36000 ("differs ... [6000.]", 1 failed). At this head the same case passes.

  • Hand values, recomputed before reading the code (the line numbers follow the PR's own worksheet labels):

    • 900k ≤ 1M → 36,000; federal 36,000 × 750/900 = 30,000.
    • 1M + min(500k excess, room 1M, 100k) = 1.1M; 1.1/1.5 = .7333 → .733 × 40,000 = 29,320.
    • SEPARATE: 500k + min(100k, 300k, 50k) = 550k; .9167 → .917 × 20,000 = 18,340; federal 20,000 × 375/600 = 12,500.
    • No FMV: 1M/1.5M = .6667 → .667 × 40,000 = 26,680.
    • FMV 1.04M: room 40k; 1.04/1.5 = .6933 → .693 → 27,720.
    • Joint, 500k + 200k non-acquisition, FMV 1M: 500k + min(200k, 500k room, 100k) = 600k; 600/700 = .857 → 29,995.
    • Grandfathered 400k (1986) + 800k (2020), joint: line 6 = min(max(400k, 1M), 1.2M) = 1M; second home 600k treated, 200k excess, room 300k; home equity 100k; 1.1/1.2 = .917 → 45,850. Federal: line 6 = 400k, then min(max(400k, 750k), 1.2M) = 750k; 50,000 × 750/1,200 = 31,250.
    • Two homes, 900k and 600k: 1M shared .6/.4; first home 300k excess fits; second home has no room; home equity 100k → .733 × 60,000 = 43,980.
    • 2017: 40,000/1.5 = 26,666.67. Rounding case: federal 50,000, worksheet 49,980.

    All match the YAML.

  • Sharing between homes (judgment call 3). For each home, room − excess = FMV − (that home's total acquisition debt) whatever the sharing. So the sharing rule changes the result only when a home's treated debt exceeds its FMV (an underwater home, where the room clips at 0). The proportional rule is defensible, and the worksheet I couldn't read reportedly works with aggregates. The rule is consistent with property 4 (order) and does not depend on loan slot.

  • Code applies the rule in every year, filing status and entity it reaches. The parameters exist only from 2018-01-01, and formula (pre-2018) returns the federal amount without reading them. formula_2018 reads the filing status breakdown, so all five statuses are handled (the Hypothesis test reads all five in 2018, 2025 and 2026, and test_new_york_limits_are_the_statutory_amounts pins 1M/100k and 500k/50k). The person-level parts add back to the tax-unit totals because home_mortgage_interest_share sums to one across head and spouse. The inputs and both new variables are defined_for = StateCode.NY; the NJ case gives 0.

  • Pass-through (my witnesses W4–W7, all pass at this head). Interest with no balances (the survey-like case) passes through unchanged: 2025 joint, $40,000 → 40,000. A 2018 head-of-household $1.1M 2018 loan with FMV $1.5M gives federal 30,000 and NY 44,000. A 2017-vintage $1.1M loan in 2025 (surviving spouse) gives federal 40,000 and NY 44,000, so home equity is the only difference there. The direct deductible_mortgage_interest input passes through (PR case).

  • ny_interest_deduction. interest_deduction = deductible_interest_expense + deductible_mortgage_insurance_premiums, and deductible_interest_expense adds deductible_mortgage_interest + non_mortgage_interest. Subtracting deductible_mortgage_interest and adding the NY amount therefore replaces exactly the mortgage part, and keeps non-mortgage interest, the premiums (still removed by subtracts in ny_itemized_deductions_max) and a directly supplied deductible_interest_expense.

  • Tests at this head, one file per process:

    • ny_mortgage_interest_deduction.yaml: 12 passed.

    • test_ny_mortgage_debt_limit_invariants.py: 3 passed. This needs uv sync --extra dev, because Hypothesis is a dev extra.

    • Readers, all passing:

      Reader file Passed
      ny_itemized_deductions_max 5
      ..._phase_out 7
      ..._phase_out_integration 2
      ..._reduction 10
      ..._reduction_applies 2
      ..._reduction_based_on_charitable_deduction_applies 2
      ..._higher_incremental_reduction 2
      ..._lower_incremental_reduction 3
      ..._incremental_reduction 3
      ny_itemizes 3
      ny_medical_expense_deduction 12
      ny_pre_tcja_deductions 6
      ny_deductions 1
      ny_taxable_income 1
      ny_income_tax 3
      NY integration 11
      hi_mortgage_interest_deduction 22
      deductible_mortgage_interest 16
      deductible_mortgage_interest_tax_unit 18
    • My own witnesses: main_repro.yaml (main: 1 failed as expected; head: 2 passed) and pr_witness.yaml W1–W7 (head: 7 passed).

  • Hypothesis. The ranges include every limit (50k, 100k, 375k, 500k, 550k, 750k, 1M, 1.1M) and the vintage boundaries 1987/1988 and 2017/2018. The properties are not trivially true: M2, M3, M4, M5 and my M9 below each fail at least one of them. They miss M1, M6, M7 and M8, which the YAML catches, because the properties test only the helpers and not the formula's wiring or the rounding. Two of them are weak. test_new_york_allowed_interest_without_non_acquisition_debt draws the share independently of the interest amounts, and its allowed >= deductible bound holds by construction (max_(worksheet, floor)). The differential shares the sharing rule (P3-2).

  • Mutants, each in its own process on a scratch worktree, with git status clean after each (columns are the PR's YAML, ny_itemized_deductions_max.yaml and Hypothesis):

Mutant PR YAML (12) ..._max.yaml (5) Hypothesis (3)
M1 ny_itemized_deductions_max adds interest_deduction again 1 failed 5 passed 3 passed
M2 acquisition limit 750k / 375k SEPARATE 7 failed 5 passed 2 failed
M3 line 7 forced to 0 (no home equity) 6 failed 5 passed 1 failed
M4 FMV ignored (unlimited room, Hawaii's rule) 3 failed 5 passed 1 failed
M5 floor removed 1 failed 5 passed 1 failed
M6 NY calculation from 2017 (formula_2017, parameters from 2017) 1 failed 5 passed 3 passed
M7 ny_interest_deduction keeps the federal mortgage part too 1 failed 5 passed 3 passed
M8 (mine) no three-place rounding, exact ratio 7 failed 5 passed 3 passed
M9 (mine) FMV room less the home's whole balance, not the treated part 1 failed 5 passed 1 failed
M10 (mine) pass-through removed (uses_worksheet always true) 12 passed 5 passed 3 passed

I ran YAML summaries only, so I name the killing case by which case asserts the changed output rather than from a failure listing. M1 is killed by "New York keeps the $1,000,000 acquisition-debt limit" (ny_itemized_deductions_max), M5 by "Rounding cannot take New York below the federal amount", M6 by "Before 2018 New York uses the federal amount", M7 by the first case's ny_interest_deduction, and M9 by "Fair market value limits home equity debt". M2, M3, M4 and M8 fail several cases. The existing ny_itemized_deductions_max.yaml kills none of them, as expected, because it supplies deductible_interest_expense directly.

M10 survives because it is equivalent, not because a test is missing. When non_deductible == 0 and no non-acquisition debt is reported, worksheet = deductible × share ≤ deductible and floor = deductible, so max_(worksheet, floor) is already deductible. The uses_worksheet branch in _ny_allowed_interest (lines 93-94) is therefore dead. The direct deductible_mortgage_interest input passes through either way. This is P3 cleanup: return max_(worksheet, floor) with the same docstring minus the pass-through sentence. The same holds in the merged Hawaii helper.

  • Readers up to household_net_income. ny_mortgage_interest_deduction → ny_interest_deduction → ny_itemized_deductions_max, which feeds ny_itemized_deductions, ny_itemized_deductions_phase_out (whose docstring the PR updates) and the higher and lower incremental reductions. ny_itemized_deductions feeds ny_deductions and ny_itemizes (and through it ny_college_tuition_credit_eligible). ny_deductions → ny_taxable_income, which feeds ny_main_income_tax, ny_supplemental_tax and nyc_taxable_income. From there it reaches ny_income_tax and NYC income tax, then state and local income tax and household_net_income. state_itemized_deductions lists ny_itemized_deductions, but no variable reads it. No other variable reads interest_deduction for New York.
    • Which move at baseline: only records with an acquisition balance and an origination year. With no balance, the share is 1 and the federal amount passes through (W4). I couldn't check whether the enhanced CPS fills first_home_mortgage_balance and the origination years. If it doesn't, nothing moves at baseline.
  • Overlap. Require federal itemizing for the New York itemized deduction only before 2018 #10100 (8fc6faaa) merges cleanly with this head (git merge-tree, no conflicts). On the merged tree, ny_mortgage_interest_deduction passed 12, Require federal itemizing for the New York itemized deduction only before 2018 #10100's ny_itemizes passed 5, ny_deductions passed 1 and ny_itemized_deductions_max passed 5. The two PRs don't interact semantically: Require federal itemizing for the New York itemized deduction only before 2018 #10100 changes the election flag and the pre-2018 gate, and this PR changes the amount only from 2018. Open Limit investment interest deductions under §163(d) and Form 4952 #9977 (investment interest, touches interest_deduction and the NY phase-out tests) and Apply Hawaii's pre-TCJA mortgage debt limits to the interest deduction #9950 (Hawaii, touches mortgage_interest_structure.py) conflict with main already. Their conflict lists are identical against main and against this head (merge-tree --name-only), so this PR adds no new conflict. If Limit investment interest deductions under §163(d) and Form 4952 #9977 lands, ny_interest_deduction inherits its §163(d) limit through interest_deduction, which is consistent with the pre-TCJA rule. Apply California's pre-TCJA mortgage debt limits to the interest deduction #10220 (California) touches only CA files.
  • Scope. The 10 changed files include nothing under .github/ or tests/policy/baseline/partners/. The Household API Partners job passed at this head. The changelog fragment is top-level changelog.d/ny-pre-tcja-mortgage-interest.fixed.md. The reference formatting follows CLAUDE.md (a quoted #page 42-43 title with the href on the first page, and a # PDF pages comment above the variable href).
  • Body sentences checked against the code: the "What changed" bullets, the witness table, the invariants list and the readers line are accurate, apart from the P3-2 items. impact: pending (hub) and an axiom: line both exist.
  • Population check the hub needs. These variables, NY households only, 2025 and 2026: ny_mortgage_interest_deduction against main's deductible_mortgage_interest sum, ny_interest_deduction, ny_itemized_deductions_max, ny_itemized_deductions, ny_deductions, ny_taxable_income, ny_income_tax, nyc_income_tax and household_net_income, plus the counts of records with first_home_mortgage_balance > 0 and a post-2017 origination year. Expected direction: with the inputs absent, NY itemized deductions are weakly higher for every household (the floor), so ny_income_tax (and NYC tax) should fall weakly and household_net_income rise weakly. Any increase in NY tax would be a defect. The change is non-zero only if the data carries balances above $750,000 on loans from 2018 or later, or federal-disallowed interest where rounding moves the share.

What I couldn't check

  • Every primary source. Because of the 403s above, I couldn't confirm: § 615(a) and its L.2018 ch. 59 Part JJ amendment; pre-TCJA 163(h)(3)(B)(ii), (C)(i)-(ii) and (D) (the $1,000,000/$500,000 and $100,000/$50,000 amounts, the per-residence FMV limit, and whether "acquisition indebtedness with respect to such residence" means the limited or the full amount); 163(h)(3)(F); Treas. Reg. 1.163-10T; Publication 936 (2017) Table 1 line numbers, its three-place rounding and Figure A note 3; the NY DTF guidance; and the IT-196 instructions. The PR's citations of these are unverified, here as in the hand-off.
  • The population impact and whether the survey data carries mortgage balances and origination years. That needs data I can't reach.
  • The spec's tax example (330 less tax for the joint example), which rests on the 2025 NY rate schedule I couldn't fetch.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Bring the branch up to date before the round 1 fixes.

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

Round 1 review of the New York pre-TCJA mortgage interest calculation:

- A fair market value of 0 now means unknown, as the Hawaii calculation
  and the origination-year inputs treat it, so home equity debt on a home
  without a value is capped only by the $100,000 ($50,000 separate) limit
  instead of losing its interest.
- The deductible share is the larger of the 2017 Publication 936
  worksheet's three-place share and the unrounded ratio of Treas. Reg.
  1.163-10T(d); CCA 201201017 allows any reasonable method. The federal
  floor, which followed no source, goes. The pass-through stays so a
  directly supplied deduction is not limited twice.
- The non-acquisition debt inputs now say that PE's federal calculation
  does not disallow that interest after 2017.
- YAML expectations, two new cases and the Hypothesis properties follow.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 7538a504f8a6f6ec021150e4891b8b75c37cd9b2 (branch ny-pre-tcja-mortgage-interest). Round 1 fix round on top of 4b893cfcb9.

Ownership and merge

  • At pick-up the PR was open, not a draft, at the named head 4b893cfcb9. Its last commit (15:15Z) was less than 6 hours old, but it is the named head: the previous build round pushed it and round 1 reviewed it. No one pushed after it, so I went ahead. That is a judgment call.
  • Fast-forward checkout of origin/ny-pre-tcja-mortgage-interest (already at 4b893cfcb9). Then git merge --no-ff origin/main (f8142b0223) gave merge commit a2df179a66 with no conflicts.
  • One fix commit, 7538a504f8. One push.

Findings

  1. FMV 0 = unknown. ny_mortgage_interest_deduction.py:50-54: equity_room = where(fair_market_value > 0, max_(fair_market_value - treated, 0), np.inf) with the hub's comment. The docstring clause is at lines 22-23. _FAIR_MARKET_VALUE_DEFAULT is replaced (ny_mortgage_interest_inputs.py:3-7). I read hi_mortgage_interest_deduction.py first. Its docstring says "PolicyEngine has no home value input, so every home is treated as worth at least its mortgages", so it has no FMV input at all, and the comment's "as the Hawaii calculation does" describes that rule.
  2. Larger share, no floor. The two helpers are replaced verbatim (ny_mortgage_interest_deduction.py:64-100). No other comment in the formula mentioned the floor (grep). The Treas. Reg. 1.163-10T and CCA 201201017 references were added with PDF-page comments (lines 130-135).
  3. Hypothesis. statute() room is inf at 0. Property 3 is now line_6 + min(acquisition - line_6, home_equity_limit). Property 5 raises only positive values. Property 6 is new: all values set to 0 never lower the limit. test_new_york_allowed_interest_without_non_acquisition_debt was rewritten: it draws interest and two homes with no non-acquisition debt, sets the federal share to min(1, federal_limit/acquisition) (1 with no balance), and takes the NY share from model_limit. It asserts deductible − tol ≤ allowed ≤ interest + tol, and allowed == deductible when non_deductible == 0. The new test_new_york_deductible_share_is_the_larger_method checks that the share is ≥ the ratio, ≥ the three-place half-up rounding (computed exactly with Fraction), ≤ 1, and ≤ ratio + .0005 (1e-12 slack). The module docstring lists the new properties and says the restatement shares the limit the same way.
  4. Non-acquisition input documentation. _NON_ACQUISITION_DEBT_DEFAULT is replaced verbatim (ny_mortgage_interest_inputs.py:9-15).
  5. Body. Rewritten from a file with PATCH. The readback is identical except for the closing attribution line, which the server replaced with its own footer. It has every item the brief lists: CI line, sources with the hub's checked quotes plus 1.163-10T and the CCA, the fourth sub-bullet, "New inputs and their defaults", judgment calls 1-6, witness table, invariants, mutants, "Federal follow-up", impact: pending (hub), and axiom: (hub files pe-parity before landing).
  6. Grandfathering cutoff. Left as is (year proxy).

YAML cases (ny_mortgage_interest_deduction.yaml): old head → new

Case Old NY New NY Federal (unchanged)
New York keeps the $1,000,000 acquisition-debt limit 36,000 36,000 30,000
Acquisition debt above the limit counts as home equity debt 29,320 29,333.33 20,000
Separate returns halve both limits 18,340 18,340 12,500
Without a fair market value, only the dollar limit caps home equity debt (renamed) 26,680 29,333.33 20,000
Fair market value limits home equity debt 27,720 27,733.33 —
Non-acquisition debt counts as home equity debt up to the limit 29,995 30,000 35,000
Grandfathered debt reduces the acquisition-debt limit 45,850 45,850 31,250
Each home's equity room limits its own excess debt 43,980 44,000 —
A directly supplied deduction passes through when nothing is disallowed 12,000 12,000 —
New D: A directly supplied deduction passes through even with balances — 12,000 —
New W2: Without a fair market value, home equity debt keeps its interest (17,140 at old head) 20,000 20,000
Before 2018 New York uses the federal amount 26,666.67 26,666.67 26,666.67
When the worksheet rounds down, New York uses the unrounded share (reworked: FMV $1M added) 50,000 50,000 50,000
Mortgage interest outside New York is zero 0 0 —

The file's header comment now describes line 7's per-home room (0 = no limit) and line 11 as the larger of the two shares. Hand checks match the hub's table. Case D: I read mortgage_interest_structure.py. non_deductible_mortgage_interest_tax_unit = max(0, home_mortgage_interest_tax_unit + points − deductible_mortgage_interest_tax_unit), and with no home_mortgage_interest both are 0. The person-level non_deductible_mortgage_interest is that times the share. So the supplied 12,000 passes through, and mutant (d) confirms 8,800 without the pass-through.

Tests at 7538a504f8 (one file per process)

  • ny_mortgage_interest_deduction.yaml: 14 passed.
  • test_ny_mortgage_debt_limit_invariants.py: 4 passed (after uv sync --extra dev).
  • Readers: ny_itemized_deductions_max 5, ny_itemized_deductions_phase_out 7, ny_pre_tcja_deductions 6, ny_itemizes 3, ny_deductions 1, ny_taxable_income 1, ny_income_tax 3, NY tax/income/integration.yaml 11, hi_mortgage_interest_deduction 22, deductible_mortgage_interest 16, deductible_mortgage_interest_tax_unit 18. All passed.
  • test_source_conflicts.py: 103 passed.
  • make format: no changes. ruff check: clean.
  • No full suite and no microsimulation.

Mutants (each in its own process, git status clean after each)

Mutant YAML (14) Hypothesis (4)
(a) worksheet share only 6 failed: acquisition above the limit (29,320), no-FMV dollar limit (29,320), FMV limits (27,720), non-acquisition (29,995), each home's room (43,980), worksheet rounds down (49,980) 2 failed: ..._allowed_interest_without_non_acquisition_debt, ..._deductible_share_is_the_larger_method
(b) unrounded ratio only 2 failed: separate returns (18,333.33), grandfathered (45,833.33) 1 failed: ..._deductible_share_is_the_larger_method
(c) FMV 0 treated as $0 2 failed: no-FMV dollar limit (26,680), W2 (17,142.86) 1 failed: ..._qualified_loan_limit_properties
(d) no pass-through 1 failed: D (8,800) 4 passed. This is equivalent in the helper property, because with no non-acquisition debt and nothing disallowed the federal and NY shares are both 1
(e) acquisition limit 750,000 / 375,000 8 failed 3 failed: ..._statutory_amounts, ..._qualified_loan_limit_properties, ..._allowed_interest_without_non_acquisition_debt
(f) ny_interest_deduction keeps the federal mortgage part 1 failed: first case, ny_interest_deduction 66,000 vs 36,000 4 passed (helpers untouched)

CI

The Pull request run at 7538a504f8 (run 41526937728, https://github.com/PolicyEngine/policyengine-us/actions/runs/41526937728) completed with conclusion success: 34 jobs, all success, none cancelled or skipped.

Judgment calls

  • Went ahead despite the under-6-hours commit, because it is the named head (see above).
  • Share test: draws debt ≥ 1 and clamps limit ≤ debt. Its rounding reference is exact (Fraction). That is safe because integer inputs below 3M sit at least about 1.6e-7 away from a tie after ×1000, far above double-precision error.
  • W2 comment: it records the old head's 17,140 as history. Mutant (c) gives 17,142.86, because the old head rounded .857 and the mutant, with the larger share, uses the unrounded 6/7.
  • I didn't re-run the extra phase-out sub-files (_reduction, _incremental_reduction, ...) that round 1 ran. The item didn't name them, and CI covers them.

Proposed follow-ups

  • Federal: disallow home equity interest after 2017 via a shared non-acquisition balance input. The federal calculation reads only acquisition balances, so home equity interest in home_mortgage_interest stays deductible. Move the non-acquisition balance to a shared input that federal disallows under 163(h)(3)(F) and NY/HI treat as home equity debt.
  • Exact grandfathering date for mortgage debt limits. Replace the 1987-year proxy with one exact-date (on or before October 13, 1987) input that the federal, Hawaii and New York calculations all read, so that one loan cannot have two vintages.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

…text

An exact tie such as 1,001,000 / 2,000,000 = .5005 comes to
500.49999999999994 after x 1,000 in binary, so the floor rounded it down to
.500 and New York used the unrounded .5005 instead of the worksheet's .501.
Twelve ratios from .5005 to .5115 did this. Rounding the scaled ratio to nine
places before the floor makes the tie round up, as the docstring says. A
YAML case and a Hypothesis example now pin the tie.

Also from the round 2 review:

- A YAML case for non-acquisition debt on the second home, which no test
  read before.
- Property 3 now asserts that the Hawaii helper gives the same default
  limit, which the input documentation cites.
- The input documentation credits the unknown-value rule to PolicyEngine,
  and says the federal calculation applies its share to home equity
  interest when the balances exceed the federal limit.
- Active voice, a clearer subject and corrected Publication 936 pages in
  the docstrings and comments.
- The Hawaii docstring and the rounding candidate in the source-conflict
  backlog now account for New York's home value input and its larger-share
  rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The property 3 docstring's "which is also what the Hawaii helper gives"
could attach to the home equity debt limit rather than the whole default
limit, so it is now a sentence of its own. The rounding candidate's note
said "using it for both" after listing three conventions; it now says
Hawaii, whose floor the sentence is about.

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub, continuing an earlier session's work on this fix round (169fe4fd39, 53f8806db4). I'll post a hand-off comment here when it's ready.


Generated by Claude Code

1 similar comment
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub, continuing an earlier session's work on this fix round (169fe4fd39, 53f8806db4). I'll post a hand-off comment here when it's ready.


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 53f8806db4aa11d7bc1ce0400301607051297ba4 (branch ny-pre-tcja-mortgage-interest). Round 2 fix round on 7538a504f8.

Ownership and the earlier session's commits

  • At pick-up the PR was open, not a draft, at 53f8806db4 rather than the named 7538a504f8. The two newer commits, 169fe4fd39 (05:35Z) and 53f8806db4 (06:12Z), come from an earlier session on this same fix round, as does the 12:23Z pick-up comment. No one else committed or commented after 2026-11-03T13:00Z. I checked at pick-up, before the body edit and before this comment.
  • I read both commits against every finding and kept both. Each change matches the review's text, and I found nothing to fix. Two small edits go beyond the review's wording. I kept them and list them under judgment calls.
  • No merge. A trial merge (git merge-tree) of origin/main (89a15b72f5) into the head is clean. Of the PR's 12 files, main changed only source_conflicts/candidates.yaml, and it left the mortgage rounding entry alone. The item says to merge main only on a conflict.
  • No push. The head already carries every code, test and documentation change, and its CI passed. This round changed only the PR body.

Findings

Finding At the head Commit My check
P2-1 code line ny_mortgage_interest_deduction.py:80-82, verbatim with the review's comment 169fe4fd39 YAML case gives 40,080; mutant (g)
P2-1 @example test file :59 (import), :278 (decorator) 169fe4fd39 fails at the example under (g)
P2-1 YAML case YAML :264-279, verbatim 169fe4fd39 run: 40,080, federal 30,000; hand check below
P3-1 YAML :206-210, verbatim 169fe4fd39 read mortgage_interest_structure.py:198-206
P3-2 ny_mortgage_interest_inputs.py:3-7, verbatim 169fe4fd39 read ny_mortgage_interest_deduction.py:50-54
P3-3 ny_mortgage_interest_inputs.py:9-16, verbatim; body "Federal follow-up" 169fe4fd39; body this round read mortgage_interest_structure.py:145-184
P3-4 ny_mortgage_interest_deduction.py:26-28; test file :34-36 and :45-46; YAML :16; body: the per-home cap bullet and judgment calls 1 and 3 169fe4fd39; body this round
P3-5 YAML :126-141, verbatim 169fe4fd39 run: 25,000, federal 30,000; mutant (h)
P3-6 ny_mortgage_interest_deduction.py:23-24 169fe4fd39
P3-7 ny_mortgage_interest_deduction.py:128-131; body Source bullet 169fe4fd39; body this round read the 2017 Publication 936 PDF: Figure A note 3 on p.3, home acquisition debt on p.9, home equity debt, its limit and grandfathered debt on p.10, Table 1 on p.11, line 7 instructions on p.13
P3-8 Hawaii hi_mortgage_interest_deduction.py:22-23, verbatim 169fe4fd39
P3-8 register source_conflicts/candidates.yaml:10438-10478: New York file :10467, this PR :10470, "three conventions" :10472, New York sentence :10477-10478 169fe4fd39, 53f8806db4 entry still starts at :10438; test_source_conflicts.py 103 passed
P3-9 test file :63-65 (import), :222-227 (assertion), verbatim 169fe4fd39 passes, so it stays; mutant (i) shows it can fail
Body edited by content; impact: and axiom: lines untouched this round (PATCH) readback matches

The register entry keeps the file's order. Its edits stay inside the entry. On main the same entry, unchanged, now starts at line 11173 after newer entries. The trial merge keeps it between us:idr-10-year-cap-before-spousal-proration and us:niit-excess-business-loss-addback.

Tests (one file per process, at 53f8806db4)

  • ny_mortgage_interest_deduction.yaml: 16 passed. Its log shows the tie case at 40,080 and the second-home case at 25,000, with federal 30,000 in both.
  • test_ny_mortgage_debt_limit_invariants.py: 4 passed.
  • test_source_conflicts.py: 103 passed, which also confirms the earlier count the review relied on CI for.
  • ruff format --check and ruff check on the PR's 7 Python files: clean.
  • No full suite, microsimulation or reader re-run. CI covers them.

Hand computations

  • Tie case (YAML :264): line 6 = 1,000,000; room = 1,001,000 − 1,000,000 = 1,000; line 7 = 1,000; line 8 = 1,001,000; line 9 = 2,000,000; ratio .5005. In Python, 1_001_000 / 2_000_000 * 1000 gives 500.49999999999994, so the bare floor gives .500, the larger share is .5005 and the result is 40,040. Rounding to 9 places first gives 500.5, then .501 and 40,080. Federal: 80,000 × 750,000 / 2,000,000 = 30,000, which leaves 50,000 non-deductible, so the worksheet path applies.
  • Second home (YAML :126): line 6 = 400,000. The second home's 200,000 of non-acquisition debt has no room limit (value 0), so line 7 = min(200,000, 100,000) = 100,000. Line 8 = 500,000 and line 9 = 600,000. The unrounded .8333 beats .833, so 30,000 × 5/6 = 25,000. Federal: 2015 debt within the limit, share 1, so 30,000. The non-acquisition debt sends New York to the worksheet path.
  • The review's second witness (200,200 / 400,000 on $20,000): 10,020 with the fix, 10,010 without it.

Mutants (each in its own process, git status clean after each)

Mutant YAML (16) Hypothesis (4)
(g) np.round removed from line 82, the brief's mutant 1 failed: the exact tie, 40,040 vs 40,080 1 failed: the share test at @example(limit=1_001.0, debt=2_000.0), assert 0.5005 >= (0.501 - 1e-12)
(h) the formula ignores ny_second_home_non_acquisition_debt (0 * on line 157) 1 failed: the second-home case, 30,000 vs 25,000. No test named this input at 7538a504f8 not run: it changes only formula_2018, which the file never calls
(i) Hawaii's line 6 = min(limit, total debt), ignoring grandfathered debt above the limit not run 1 failed: loan-limit properties at the Hawaii assertion (:227); a $1,100,001 grandfathered loan gives 1,100,001 vs 1,100,000

CI

The Pull request run at 53f8806db4 is 41588428535: completed, conclusion success, 35 jobs, all success, first attempt. GitHub reports the PR mergeable, with merge state blocked (review required).

Judgment calls

  1. I kept two edits from the earlier session that go beyond the review's wording:
    • Property 3's docstring adds "The Hawaii helper gives the same limit." (test file :27-28), to match the P3-9 assertion.
    • The register note says "using it for Hawaii too" where it said "using it for both" (candidates.yaml:10476-10477). "Both" lost its referent once the note listed three conventions.
  2. I made body edits beyond the review's list:
    • the CI line now names 53f8806db4, its 35 jobs and run 41588428535;
    • the witness table has rows for the two new YAML cases;
    • the tests and mutants sections name the head each ran at, and add (g)-(i);
    • the extra-files list names the Hawaii file and the register backlog;
    • the invariants note the Hawaii check and the explicit tie example.
  3. I neither merged nor pushed, for the reasons above. The re-filed note asked me to merge origin/main again before pushing, but that assumed the earlier session had merged main. It hadn't, and I pushed nothing.
  4. The 9-place window. np.round(ratio * 1_000, 9) treats any scaled ratio within 5e-10 of a half as a tie.
    • With whole-dollar lines 8 and 9 and line 9 under $1 billion, a non-tie lies at least 1/(2 × line 9) > 5e-10 away, so only exact ties move.
    • Line 8 carries a fraction only when a home's positive value falls short of the debt treated as acquisition debt on it and home equity stays under the cap. That home's room then clamps at 0, so its pro-rata share of the limit stays in line 8. Such a case could round up when line 8 sits within about $0.000002 of a tie, at $4 million of debt.
    • A random search of that configuration found no such case. It compared helpers lifted from the file with exact fractions, over every valid first-home value for each draw of balances.
    • I left the line as the review wrote it.
  5. I didn't re-run the readers locally. This round's commits change the share only at exact ties; the rest is documentation and tests. CI at the head passed.

For the follow-up list (not fixed here)

  1. Hawaii's tie arithmetic (hi_mortgage_interest_deduction.py:48, np.floor(ratio * 1_000 + 0.5) / 1_000). I ran a witness through the model in a scratch YAML, not committed:
    • Inputs: 2025, Hawaii, single; $100,000 interest; $1,902,000 from 1986 and $2,098,000 from 2020.
    • Line 8 = 1,902,000 + 100,000 = 2,002,000 and line 9 = 4,000,000, so .5005 is an exact tie.
    • PE gives 50,000 (share .500); the worksheet's .501 gives 50,100. Federal is 47,550, so the floor doesn't bind.
    • As the review said, a failing tie there needs grandfathered debt above the limit.
  2. New York college tuition credit.
    • How the model decides:
      • ny_college_tuition_credit_eligible returns ~ny_itemizes.
      • ny_itemizes is tax_unit_itemizes & (ny_itemized_deductions >= ny_standard_deduction).
      • ny_itemized_deductions_max adds ny_college_tuition_deduction.
    • This PR raises ny_interest_deduction for some filers. A federal itemizer whose New York itemized total now reaches the standard deduction starts itemizing for New York and loses the credit.
    • New York tax rises when the credit exceeds the tax value of the larger deductions. So "any increase in NY tax would be a defect" needs that exception in the impact check.
  3. Mixed inputs. I ran this through the model in a scratch YAML:
    • Inputs: 2025, New York, single; deductible_mortgage_interest 12,000 supplied; home_mortgage_interest 5,000; a $1.5M loan from 2020.
    • The federal tax-unit amounts come to 2,500 deductible and 2,500 non-deductible, so the worksheet path applies: (12,000 + 2,500) × 11/15 = 10,633.33, under the supplied 12,000.
    • The inputs contradict each other, so I report no defect. The impact run should confirm that no dataset stores both.

Proposed follow-up issues

  • Round exact ties up in Hawaii's mortgage interest share. _hi_deductible_share floors ratio * 1000 + 0.5, so in binary an exact tie such as 2,002,000 / 4,000,000 = .5005 rounds to .500. Apply New York's 9-place rounding, and add the witness above as a YAML case.
  • Decide New York itemizing by tax, counting the college tuition credit. ny_itemizes compares deductions, though its documentation says the lower tax decides, and ny_college_tuition_credit_eligible is ~ny_itemizes. Check Tax Law 606(t) and 615 on the choice, and test a filer with tuition expenses near the standard deduction.
  • The previous round's two follow-ups still stand: a shared non-acquisition balance input that the federal calculation disallows, and an exact grandfathering date.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Head under review: 53f8806db4aa11d7bc1ce0400301607051297ba4 (unchanged since the round 2 brief).

Firm so far:

  • At the head, one file per process: ny_mortgage_interest_deduction.yaml 16 passed; test_ny_mortgage_debt_limit_invariants.py 4 passed (needs uv sync --extra dev for Hypothesis); test_source_conflicts.py 103 passed. CI run 41588428535 at the head: 35 of 35 jobs succeeded.
  • P2-1 holds through the model: the tie case gives 40,080, and the delta's second witness (200,200 / 400,000 on $20,000) gives 10,020.
  • Mutants (g), (h), (i) reproduce the hand-off's results. Two of mine on ny_second_home_non_acquisition_debt: (j) leaving it out of line 9 fails the second-home YAML case (37,500 vs 25,000). (k) moving it onto the first home's slot survives all 16 YAML cases and 4 Hypothesis tests. A case with a positive second-home value kills it.
  • Judgment call 4's bound is slightly loose. For whole-dollar lines, the 9-place rounding first misrounds a non-tie at line 9 = 999,824,101, not at $1 billion. A fractional line 8 can round the wrong way at ordinary sizes: one witness gives 49,600 where the exact larger-share rule gives 49,550, though a whole-dollar worksheet would also give 49,600.
  • Sources: this session's network allows only package registries. tax.ny.gov, irs.gov, govinfo.gov and nysenate.gov all refuse the connection, so I can't confirm any quote.

Still to do: main-merged test runs, reproducing the defect at main, the college tuition household, mixed inputs, the Hawaii witness, readers, and the body.


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
53f8806
METHODOLOGY_CHOICE: yes

The head hasn't moved since the round 2 brief. I reviewed the whole diff from 7538a504f8 to 53f8806db4 (6 files) and the body. The PR picks readings where the law allows more than one: the larger of the worksheet share and the exact ratio; sharing the acquisition limit between homes in proportion to each home's other acquisition debt; and a home value of 0 meaning unknown. Hence METHODOLOGY_CHOICE: yes.

Can it land? Yes, as far as I can check. Every delta finding is fixed as asked. The tie now rounds up, the defect reproduces at current main, the changed tests pass at the head and on a tree with main merged in, and CI at the head is 35/35. The four findings below are all P3. None changes a result for a household with consistent inputs, except the 9-place rounding case, which needs a line 8 within a millionth of a dollar of a tie. I couldn't reach any legal source from this session (see the end).

Findings

P3-1. A wiring slip on the second home's non-acquisition debt survives every test

policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/ny_mortgage_interest_deduction.py:155-158

The only second-home case (YAML :126-141) leaves both home values at 0, so it can't tell which home the debt sits on. Mutant (k) moves ny_second_home_non_acquisition_debt onto the first home's slot:

        non_acquisition_debts = [
            tax_unit("ny_first_home_non_acquisition_debt", period).astype(np.float64)
            + tax_unit("ny_second_home_non_acquisition_debt", period).astype(np.float64),
            0 * tax_unit("ny_second_home_non_acquisition_debt", period).astype(np.float64),
        ]

Result: YAML 16 passed, Hypothesis 4 passed. The property tests only call the helpers, so they never see the formula's wiring.

The code is right today. Case to add (hand value: the second home has no acquisition debt, so its room is its $50,000 value; line 7 = 50,000; line 8 = 450,000; line 9 = 600,000; share .75; $30,000 × .75):

- name: The second home's value limits its own non-acquisition debt
  period: 2025
  absolute_error_margin: 0.01
  input:
    state_code: NY
    filing_status: JOINT
    home_mortgage_interest: 30_000
    first_home_mortgage_balance: 400_000
    first_home_mortgage_origination_year: 2015
    ny_first_home_fair_market_value: 2_000_000
    ny_second_home_fair_market_value: 50_000
    ny_second_home_non_acquisition_debt: 200_000
  output:
    # Room on the second home: $50,000 - $0 = $50,000, so line 7 = $50,000.
    # ($400,000 + $50,000) / $600,000 = .75; $30,000 x .75.
    ny_mortgage_interest_deduction: 22_500

I ran it at the head: 1 passed. With the second home's value at $1 instead, the head gives 20,010 and mutant (k) gives 25,000. That shows the case kills the mutant: under (k) the debt lands on the first home, whose room is $1.6M, so line 7 = $100,000.

P3-2. The 9-place rounding can move a non-tie, and judgment call 4's bound is loose

ny_mortgage_interest_deduction.py:82 (worksheet = np.floor(np.round(ratio * 1_000, 9) + 0.5) / 1_000); the hand-off's judgment call 4; body line 90.

Whole-dollar lines. For whole-dollar lines 8 and 9, a non-tie sits at least 1/(2 × line 9) from a tie in the scaled ratio. The hand-off says that keeps it outside the 5e-10 window "under $1 billion". Float error in ratio * 1_000 eats a little of that margin.

  • I checked every minimum-distance near-tie (2000·L8 − (2k+1)·L9 = ±1) for line 9 in 1.0-1.2M, 500.0-500.2M and 999.0-999.82M. There were no misroundings.
  • The first failure is at line 9 = 999,824,101 and line 8 = 950,332,808. The share comes out .951 where the exact answer is .9504999999995.
  • So the bound is about $999.8 million, not $1 billion. In practice that doesn't matter.

Fractional line 8. Line 8 carries a fraction when one home's room binds at its value less its prorated share of the limit. In that case the rounding does move a non-tie at ordinary sizes. Witness, 2025, single:

  • inputs: home_mortgage_interest 100,000; a $1,333,334 loan from 2020; a $666,669 loan from 2021; ny_first_home_non_acquisition_debt 21,997; first home value 668,567; second home value 300,000;
  • exact lines: the first home's share of the limit is 1,000,000 × 1,333,334 / 2,000,003, so line 8 = 1,001,900.999999…; line 9 = 2,022,000; ratio = .49549999999951;
  • the exact larger-share rule gives the unrounded ratio, 49,550.00. The model at the head gives 49,600:
    ny_mortgage_interest_deduction@2025: [49600.] differs from 49550.0.
  • At 7538a504f8 (floor only) this case gave the ratio. The head moved it.

This is defensible: a worksheet filled in whole dollars has line 8 = 1,001,901, which is an exact tie (.4955), and that rounds to .496 = 49,600. But two texts overstate the guarantee:

  • the hand-off's "only exact ties move";
  • body line 90, "exceeds the ratio by no more than .0005". Here the excess is .0005 + 5e-13. The property test's 1e-12 slack hides it.

No code change needed. If the hub wants the whole-dollar reading explicit, round line 8 and line 9 to whole dollars before the share; that makes this case an exact tie. Otherwise replace the end of body line 90 with:

"…at most 1, and exceeds the ratio by no more than .0005 apart from float error. The 9-place rounding treats a scaled ratio within 5e-10 of a half as a tie: with whole-dollar lines 8 and 9 below about $999.8 million only exact ties move, and a fractional line 8 that close to a tie rounds as a whole-dollar worksheet would."

P3-3. With mixed inputs this PR lowers New York's deduction, and judgment call 2's bound is false there

Body line 40, "so with no non-acquisition debt the result never falls below PE's unrounded federal amount", and _ny_allowed_interest's docstring (ny_mortgage_interest_deduction.py:96-98).

Witness, 2025, New York, single: deductible_mortgage_interest 12,000 supplied, home_mortgage_interest 5,000, a $1.5M loan from 2020.

main abc67b98de head
deductible_mortgage_interest_tax_unit 2,500 2,500
non_deductible_mortgage_interest_tax_unit 2,500 2,500
ny_mortgage_interest_deduction (not on main) 10,633.33
ny_itemized_deductions_max 12,000 10,633.33

The worksheet path takes (12,000 + 2,500) × 11/15. The federal interest deduction, which reads the supplied 12,000, stays above that. The inputs contradict each other, so I don't count this as a wrong result. But the PR makes it reachable and lower than main, and the body's bound doesn't hold for it. Replace the end of judgment call 2 with:

"…so with no non-acquisition debt, and with the federal amount computed from home_mortgage_interest rather than supplied as deductible_mortgage_interest alongside it, the result never falls below PE's unrounded federal amount."

The hub's population check should confirm that no dataset stores both inputs.

P3-4. The phase-out docstring line conflicts with open PR #9977

policyengine_us/variables/gov/states/ny/tax/income/taxable_income/deductions/itemized/phase_out/ny_itemized_deductions_phase_out.py:48. This PR changes "inside interest_deduction" to "inside ny_interest_deduction". #9977 rewrites the same sentence ("interest after the federal Form 4952 limit appears in both L1 (inside interest_deduction)…"). Whichever lands second gets a textual conflict, and its resolution should say ny_interest_deduction. I found no other file overlap among the open PRs that search returns for the changed file names (#9912 and #10038 match only on text). #9950's Hawaii file conflicts with main already.

Hand-off follow-ups: main or this PR?

Follow-up main abc67b98de head Verdict
Hawaii tie (hi_mortgage_interest_deduction.py:48), 2025, single: $100,000 interest, $1,902,000 from 1986, $2,098,000 from 2020 hi_mortgage_interest_deduction 50,000; federal 47,550 50,000; federal 47,550 Same on main: follow-up. Hand: line 6 = 1,902,000, line 7 = 100,000, 2,002,000 / 4,000,000 = .5005, a tie; .501 gives 50,100.
College tuition credit (ny_college_tuition_credit_eligible = ~ny_itemizes; ny_itemizes compares deductions; ny_deductions = max of the two) witness below: NY tax 19,801.85 20,001.16 (+199.31) Same mechanism on main: follow-up. This PR is a new trigger for it.
Mixed inputs 12,000 10,633.33 Made reachable and lower by this PR: P3-3 above.

Tuition witness. 2025, New York, single, age 30:

  • inputs: $300,000 wages, $1,000 qualified_tuition_expenses, $13,200 home_mortgage_interest, a $1.5M loan from 2020. The filer itemizes federally because of state income tax.
  • At main: NY itemized 5,700 (after the 25% reduction at this income), below the $8,000 standard, so the filer gets the $200 credit.
  • At the head: NY itemized 8,010, so ny_itemizes is true, the credit is 0, and deductions rise by only $10.
  • Main has the same defect with no help from this PR: adding a $3,300 cash gift to the same household at main moves NY itemized to 8,175 and NY tax from 19,801.85 to 19,989.86.
  • So the hub's impact run should expect some New York tax increases from this PR.
  • I couldn't read Tax Law 606(t), 613 or 615 to say whether the law lets an itemizer take the credit, so the follow-up should settle that from the statute.

Checked and sound

  • Defect at main. At abc67b98de: 2025, single, $36,000 interest, a $900,000 loan from 2020. ny_itemized_deductions_max is 30,000, where the head's YAML case gives 36,000.

  • P2-1 by hand.

    • Tie case (YAML :264-279):
      • line 6 = min(max(0, 1,000,000), 2,000,000) = 1,000,000; the share of the limit is 1, so treated = 1,000,000 and the excess is 1,000,000;
      • room = 1,001,000 − 1,000,000 = 1,000, so line 7 = 1,000 and line 8 = 1,001,000;
      • 1,001,000 / 2,000,000 = .5005, a tie, so the share is .501 and the result 40,080;
      • federal: 80,000 × 750,000 / 2,000,000 = 30,000; the 50,000 non-deductible sends New York to the worksheet path.
    • The delta's second witness: line 6 = 100,200; line 7 = min(299,800, 100,000) = 100,000; 200,200 / 400,000 = .5005, so .501 × 20,000 = 10,020. The model gives 10,020 (run: 1 passed). Federal is 20,000 with nothing disallowed, and the non-acquisition debt sends it to the worksheet path.
  • Delta findings, each against the head.

    • P2-1: code line :80-82, @example :278 (import :59), YAML :264-279. All verbatim.
    • P3-1: YAML :206-210, verbatim. non_deductible_mortgage_interest_tax_unit reads deductible_mortgage_interest_tax_unit at mortgage_interest_structure.py:205.
    • P3-2: inputs :3-7. P3-3: inputs :9-16, and body line 48. Both verbatim.
    • P3-4: deduction :26-28; test file :34-36 and :45-46; YAML :16; body lines 21, 39 and 41.
    • P3-5: YAML :126-141. P3-6: :23-24. P3-7: :128-131 and body line 8.
    • P3-8: Hawaii :22-23; candidates.yaml adds the New York file, PR Apply New York's pre-TCJA mortgage debt limits to the interest deduction #10596, "three conventions" and the New York sentence, and the entry keeps its place.
    • P3-9: test file :63-65 and :222-227.
    • Judgment call 1's two extra edits both hold. Property 3's docstring sentence "The Hawaii helper gives the same limit." matches the :227 assertion. "using it for Hawaii too" fixes the dangling "both" now that the note lists three conventions.
  • Tests at the head, one file per process.

    • ny_mortgage_interest_deduction.yaml: 16 passed.
    • test_ny_mortgage_debt_limit_invariants.py: 4 passed. Hypothesis needs uv sync --extra dev; plain uv sync fails collection with ModuleNotFoundError: hypothesis, and CI installs it.
    • test_source_conflicts.py: 103 passed.
    • ruff format --check and ruff check on the 7 changed Python files: clean.
  • Main merged in. I merged origin/main (abc67b98de) into the head in a scratch tree. The merge is clean, and its diff against main is still the PR's 12 files. There, the YAML gave 16 passed, Hypothesis 4, source conflicts 103. Readers: ny_itemized_deductions_max.yaml 5, ny_itemized_deductions_phase_out.yaml 7, ny_itemizes.yaml 3, hi_mortgage_interest_deduction.yaml 22.

  • Mutants. Each ran in its own process; git status was clean after each.

    Mutant YAML (16) Hypothesis (4) Killed by
    (g) no np.round 1 failed: 40,040 vs 40,080 1 failed: assert 0.5005 >= (0.501 - 1e-12) the tie case; @example(limit=1_001.0, debt=2_000.0)
    (h) 0 * the second home's non-acquisition debt 1 failed: 30,000 vs 25,000 4 passed the second-home case
    (i) Hawaii line 6 = min(limit, total) no case failed 1 failed: loan-limit properties at the Hawaii assertion :227
    (j) mine: line 9 omits the second home's non-acquisition debt 1 failed: 37,500 vs 25,000 4 passed the second-home case
    (k) mine: the second home's debt placed on the first home 16 passed 4 passed none; see P3-1
  • Rule coverage. formula_2018 applies to every year from 2018. Both parameters cover all five filing statuses from 2018-01-01, with SEPARATE halved. The entity is TaxUnit, and the person-level interest goes in through add. Before 2018 the formula returns the federal amount (YAML 2017 case: 26,666.67).

  • Readers.

    • ny_mortgage_interest_deduction is read only by ny_interest_deduction.
    • ny_interest_deduction is read only by ny_itemized_deductions_max; the phase-out file mentions it in a docstring only.
    • ny_itemized_deductions_max is read by ny_itemized_deductions, ny_itemized_deductions_phase_out, and the higher and lower incremental reductions. That matches body line 120.
    • The four inputs and the two New York parameters are read only by ny_mortgage_interest_deduction.
    • No other New York variable reads interest_deduction or deductible_mortgage_interest.
    • The phase-out's L2 investment interest still cancels, since ny_interest_deduction keeps the non-mortgage parts of interest_deduction.
  • CI at the head. Pull request run 41588428535: completed, success, attempt 1, 35 jobs, all success. No cancelled or skipped jobs.

  • Body.

    • The CI line, test counts, witness table, mutants (g)-(i), readers line, extra-files list and invariants 1-7 match the code and my runs, apart from P3-2 and P3-3.
    • Every witness table row is a passing YAML case.
    • impact: and axiom: lines exist. The axiom: line reads "(hub files pe-parity before landing)", which isn't one of CONTRIBUTING's forms yet. The hub sets it.
  • Scope. No file under .github/ or policyengine_us/tests/policy/baseline/partners/.

  • Baseline. The diff moves outputs at baseline: ny_itemized_deductions_max and everything downstream, from 2018, for New York filers with post-2017 debt above $750,000, debt above $1,000,000, or non-acquisition debt. The population check should cover:

    • the New York itemized total and tax change;
    • counts of filers who newly itemize and lose the tuition credit;
    • whether any dataset stores both deductible_mortgage_interest and home_mortgage_interest (P3-3).

What I couldn't check

  • Sources. This session's network policy allows only package registries. tax.ny.gov, irs.gov, govinfo.gov, nysenate.gov and legislation.nysenate.gov all refused the connection. So:
    • I couldn't fetch the IT-196 instructions and can't quote a New York rounding rule for the share;
    • I couldn't confirm Pub 936 (2017) Table 1 line 11's "rounded to three places", IRC 163(h)(3) (2017), Treas. Reg. 1.163-10T, CCA 201201017, or Tax Law 606(t), 613 and 615;
    • every quote in the body is unconfirmed by me, and so is the reading of 606(t) behind the tuition-credit follow-up;
    • I didn't try uscode.house.gov or ecfr.gov once the proxy refused the others, since its policy denies all non-registry hosts.
  • Impact. No microsimulation or population data, per the brief. The hub's run has to measure the change and the tuition-credit and mixed-input counts.
  • Full suites. CI at the head is the evidence. I ran only the files named above.

Generated by Claude Code

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