Skip to content

Apply the New York itemized deduction adjustment to IT-196 line 45 - #10658

Open
PavelMakarchuk wants to merge 6 commits into
mainfrom
fix-ny-itemized-reduction-line-45
Open

PavelMakarchuk wants to merge 6 commits into
mainfrom
fix-ny-itemized-reduction-line-45

Conversation

@PavelMakarchuk

@PavelMakarchuk PavelMakarchuk commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

New York's itemized deduction adjustment (Tax Law § 615(f), IT-196 line 46) takes a percentage of "the New York itemized deduction otherwise allowable under subsection (a)". That is the deduction after the line 40 limitation (the pre-TCJA overall limitation), with the § 615(c)–(e) modifications, including the § 615(d)(4) college tuition deduction.

ny_itemized_deductions_lower_incremental_reduction (§ 615(f)(1)) and ny_itemized_deductions_higher_incremental_reduction (§ 615(f)(2)) took the percentage of ny_itemized_deductions_max, which is before the line 40 limitation. Both now use ny_itemized_deductions_max - ny_itemized_deductions_phase_out.

Found from PolicyEngine/policyengine-taxsim#1512, where TAXSIM matches the worksheet and PolicyEngine did not.

Evidence

N.Y. Tax Law § 615(f)(1): "An amount equal to the New York itemized deduction otherwise allowable under subsection (a) of this section, multiplied by a percentage, ..."

2021 IT-196 instructions, PDF p. 20, line 46, Worksheet 3, line 6 and Worksheet 4, line 3: "Enter 25% (.25) of line 45". Line 45 follows line 40.

Source conflict: college tuition

The 2021 IT-196 puts the tuition deduction inside line 45 for Form IT-203 filers (line 43), but after line 46 for Form IT-201 filers (line 48). § 615(a) includes "the modifications specified in this section", among them § 615(d)(4), so the PR follows the statute and keeps tuition in the base for all filers. source_conflicts/candidates.yaml records this with follows: statute, for this tier and for the § 615(g) tier, which already follows the statute in ny_itemized_deductions.

Example (taxsim #1512)

2021, joint, interest 360,200, mortgage interest 50,000:

line 40 line 46 NY itemized (line 47) NY taxable income worksheet tax before credits
By hand (IT-196, IT-201) 49,359.50 12,339.88 37,019.62 323,180.38 2 20,457.32
TAXSIM 37,019.62 323,180.38 2 20,457.32
PolicyEngine before 49,359.50 12,500.00 36,859.50 323,340.50 3 21,711.60
PolicyEngine after 49,359.50 12,339.88 37,019.62 323,180.38 2 20,457.54

With a $10,000 tuition deduction, the base is 59,359.50, line 46 is 14,839.875 and the deduction is 44,519.625.

Tests

  • Lower tier (§ 615(f)(1)): the base after the line 40 limitation, with and without tuition.
  • Higher tier (§ 615(f)(2)): the same two cases at NY AGI $500,000.
  • Two integration cases (taxsim Correct MA income tax Part A AGI calculation #1512, and the same household with tuition). The itemized amounts and taxable income use a 0.01 margin. The tax before credits is in separate cases with a 0.5 margin, because the model adds the rounded $1,056 recapture amount.
  • All 882 NY tests pass.

Note: #10596 (open) changes ny_itemized_deductions_max and ny_itemized_deductions_phase_out, but not these two formulas.

axiom: TheAxiomFoundation/rulespec-us#2143 queued

🤖 Generated with Claude Code

The line 46 worksheets take a share of line 45, which is after the
line 40 limitation and before the college tuition deduction (line 48).
Both incremental reductions took the share of the total before the
limitation.

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

DTrim99 commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Apply the New York itemized deduction adjustment to IT-196 line 45

Summary

The PR changes the base of the Tax Law 615(f) itemized deduction adjustment (IT-196 line 46, Worksheets 3 and 4) in two ways:

  1. It moves the base after the line 40 limitation. The formulas now subtract ny_itemized_deductions_phase_out. Both the statute and every IT-196 since 2018 support this, and it fixes the policyengine-taxsim Correct MA income tax Part A AGI calculation #1512 household.
  2. It takes the college tuition itemized deduction out of the base. This follows the IT-201 filer layout of the form, where tuition is line 48, after line 46. It contradicts the statute. Section 615(f) takes its percentage of "the New York itemized deduction otherwise allowable under subsection (a)", and subsection (a) defines that deduction to include "the modifications specified in this section". That includes the 615(d)(4) college tuition deduction. The form itself puts tuition inside line 45 for IT-203 filers (line 43). Before this PR the model kept tuition in the base, which matched the statute. The PR moves it to the form reading and pins that reading with a new test, without recording a source conflict.

The rest of the change is sound:

  • The IT-196 line 40-49 layout is the same in every year checked.
  • The arithmetic in the new tests checks out.
  • CI is green.

Open items:

  • The college tuition part (critical).
  • A missing axiom line.
  • No test covers the change to the higher (over $475,000) formula.

Critical (must fix)

1. Taking the college tuition deduction out of the 615(f) base contradicts the statute in every claimed year.

Files: ny_itemized_deductions_lower_incremental_reduction.py and ny_itemized_deductions_higher_incremental_reduction.py (line_45 = max_(itemized_max - college_tuition - phase_out, 0)), plus the new test ny_itemized_deductions_lower_incremental_reduction.yaml Case 5.

The statute (N.Y. Tax Law 615) reads:

  • (a): "The New York itemized deduction of a resident individual means the total amount of his or her deductions from federal adjusted gross income allowed ... as such deductions existed immediately prior to the enactment of Public Law 115-97 with the modifications specified in this section, except as provided for under subsections (f) and (g) of this section."
  • (d): "Modifications increasing federal itemized deductions. The total amount of deductions from federal adjusted gross income shall be increased by: ... (4) allowable college tuition expenses ... multiplied by the applicable percentage."
  • (f)(1): "An amount equal to the New York itemized deduction otherwise allowable under subsection (a) of this section, multiplied by a percentage ...". (f)(2) uses the same base for the over-$475,000 tier.

So the statutory base is the federal pre-TCJA deduction after section 68, with every 615(c)-(e) modification. That includes (d)(4) tuition and excludes only (f) and (g).

The form's IT-201 layout is the only source for leaving tuition out:

  • 2021 IT-196 (form, page 3) has line 43, "College tuition itemized deduction (Form IT-203 filers only ...)", then line 45, "Add lines 42, 43, and 44", then line 46, the adjustment.
  • Line 48 is "College tuition itemized deduction (Form IT-201 filers only ...)".

So for nonresident and part-year filers the agency itself puts tuition inside line 45, which matches the statute. The line 48 placement is a layout choice for IT-201 filers, not a reading of 615(f). It also matches no consistent reading of the statute:

  • The narrow reading of "under subsection (a)" would mean line 40, before the line 41 subtraction of income taxes. The form rejects that by using line 45.
  • The broad reading includes every modification, tuition among them.

Project rules settle this. CONTRIBUTING.md#source-conflicts says to follow the statute over a form, and lists the canon "the operative words govern over form shortcuts". The model already takes the statute's side at the over-$1,000,000 tier. ny_itemized_deductions returns only 50% or 25% of the charitable deduction under 615(g). The IT-201 form adds line 48 tuition on top of that (2025 IT-196-I line 47, page 21). After this PR, a tuition deduction would be fully lost above $1,000,000 but fully exempt from 615(f) between $100,000 and $1,000,000.

Impact, using the PR's integration household plus a $10,000 tuition deduction (2021, joint, NYAGI $360,200). Line 40 stays at 49,359.50 because tuition is not on the line 40 worksheet.

Step PR Statute
Base 49,359.50 59,359.50
Line 46 reduction (25% of base) 12,339.875 14,839.875
NY itemized deduction 47,019.625 (49,359.50 − 12,339.875 + 10,000) 44,519.625 (59,359.50 − 14,839.875)
  • The PR's deduction is $2,500 too high.
  • Both taxable incomes fall under Tax computation worksheet 2, line 3, at 6.33% (2021 IT-201-I, page 52), so the tax is understated by 0.0633 × 2,500 = $158.25.
  • Above NYAGI $525,000 the overstatement is 50% of the tuition deduction.

Fix:

  • Use line_45 = itemized_max - phase_out in both formulas, so the base is after line 40 and includes tuition.
  • Change Case 5 to expect 0.25 × 60,000 = 15,000.
  • Add a CONTRIBUTING.md#source-conflicts step 1 candidate to source_conflicts/candidates.yaml covering:
    • the statute versus the IT-201 form placement of line 48;
    • both the 615(f) tier and the existing 615(g) tier, which already follows the statute in ny_itemized_deductions;
    • IT-196-I and IT-196 for 2018-2025, with the statute as the deciding source.
  • If a comment describing the conflict is added, add the file's KNOWN_UNMARKED line too.
  • If the author reads the statute as ambiguous and wants the form reading, that needs a recorded project-lead ruling before merge. Without one, the statute-first rule applies.
  • Update the changelog fragment and the code comment, which now say "before the college tuition deduction".

Should address

1. The PR body has no axiom line.

This is a staff policy fix, so CLAUDE.md "Axiom Parity" requires one line in the PR body. There are no PR comments, and rulespec-us has no 615(f) issue: the NY items open there are #2041 (615(a) mortgage limits, for #10596), #2028 (613/615/606(t), for #10100), #1668 and #1525.

Fix: either encode the provision, or file a dispatch-ready pe-parity issue and add axiom: <issue> queued. The issue needs:

  • module path us-ny/statute/TAX/615/f;
  • the verbatim text of 615(a), (d)(4) and (f)(1)-(2);
  • required outputs: the line 46 adjustment for each NYAGI band;
  • companion tests drawn from the IT-196-I Worksheets 3 and 4 hand computations, not from PolicyEngine output.

The issue should state the base the PR ends up with: after line 40, with or without tuition per Critical 1.

2. No test pins the change to ny_itemized_deductions_higher_incremental_reduction.py.

Reverting that formula to ny_itemized_deductions_max fails no test:

  • Every new case has NYAGI $360,200, below $475,000, so the higher amount is 0.
  • The existing higher cases (NYAGI $475,000 and $880,000, max 100) have no phase-out and no tuition.
  • The PR body's "Without the fix, three cases fail" names only the three lower-tier cases.

Fix: add a case to ny_itemized_deductions_higher_incremental_reduction.yaml:

  • Inputs: period 2021, ny_agi: 500_000, SINGLE, ny_itemized_deductions_max: 50_000, ny_itemized_deductions_phase_out: 640.50, charitable_deduction: 0.
  • Arithmetic: Worksheet 4 line 2 = 25,000 / 50,000 = 0.5, and line 3 = 0.25 × 49,359.50 = 12,339.875.
  • Expected higher amount: 0.5 × 12,339.875 = 6,169.9375.
  • Add a tuition input matching the Critical 1 resolution.

Suggestions

  1. The max_(…, 0) floor on line_45 can never bind. ny_itemized_deductions_phase_out is at most 80% of worksheet line 3, which is at most line 1 (max minus tuition). ny_itemized_deductions_max is never negative: every added component is non-negative, and the subtracted mortgage-insurance component is part of interest_deduction. No test can pin it, so consider dropping it or noting why it is there.
  2. References. The added it196i_2021.pdf#page=20 shows "25% of line 45" but not what line 45 contains. Consider citing the form page that lays out lines 40-49 (2021 IT-196, page 3) or instructions pages 18-19. The line numbering is the same 2018-2025, so one source is enough.
  3. Integration margin. ny_income_tax_before_credits expects 20,457.32, the hand value from Worksheet 2 line 3, while the model returns 20,457.54 (per the PR body). The $0.22 gap is not from this PR. The model's supplemental tax appears to add the published, rounded $1,056 recapture instead of applying 6.33% to all taxable income (the exact recapture is 1,055.78). A short comment would stop the 0.5 margin being read as slack in the itemized change.
  4. Rounding (pre-existing, optional follow-up). Worksheets 3 and 4 round the NYAGI fraction to four decimals ("Divide line 4 by $50,000 and round to the fourth decimal place"). The model does not round. This is not changed here.

Verified correct

Line numbering by year.

  • IT-196 lines 40-49 read the same on the 2018, 2019, 2020, 2021 and 2025 forms: 40 total after the worksheet, 41 subtractions, 42, 43 IT-203 tuition, 44 additions, 45 = 42+43+44, 46 adjustment, 47, 48 IT-201 tuition, 49.
  • The IT-196-I worksheets take "25% (.25) of line 45" in 2018, 2019, 2020, 2021, 2022, 2024 and 2025.
  • The 2023 instructions could not be retrieved. The repo already cites them for line 46, and 2022 and 2024 are identical.
  • 2025 changed only line 47 for NYAGI over $1,000,000 (50% or 25% of line 19 directly). Line 46 still uses line 45 for the $100,000-$1,000,000 bands.

Order of the line 40 limitation. Placing the base after line 40 is correct under both the form (line 45 builds on line 42 = line 40 − line 41) and the statute. Subsection (a) counts deductions "allowed" under pre-TCJA law, which means after the IRC 68 limitation.

Hand checks. At least 8 expectations:

# Test case Hand computation Expected Status
1 Integration (2021, joint), ny_itemized_deductions_phase_out Federal AGI 360,200 − 338,850 = 21,350; × 0.03 = 640.50; min(0.80 × 50,000, 640.50) 640.50 ✓
2 Integration, ny_itemized_deductions_reduction Base 50,000 − 640.50 = 49,359.50; NYAGI excess over 200,000 = 160,200, so fraction = min(50,000, 160,200) / 50,000 = 1; 0.25 × 49,359.50; higher tier is 0 (NYAGI under 475,000) 12,339.875 ✓
3 Integration, ny_itemized_deductions 50,000 − 640.50 − 12,339.875 37,019.625 ✓
4 Integration, ny_taxable_income 360,200 − 37,019.625; itemized exceeds the $16,050 joint standard deduction 323,180.375 ✓
5 Integration, ny_income_tax_before_credits TI ≤ 323,200 and NYAGI ≥ 211,550, so Worksheet 2 line 3 applies: 0.0633 × 323,180.375 = 20,457.3177 20,457.32 ✓
6 Pre-fix figure in the PR body Old base 50,000 × 0.25 = 12,500; NY itemized 36,859.50; TI 323,340.50 is over 323,200, so Worksheet 3 applies — Matches the body's "before" row
7 Lower-tier Case 4 0.25 × (50,000 − 640.50), fraction 1 12,339.875 ✓
8 Lower-tier Case 5 Phase-out not triggered (no federal AGI), so 0.25 × (60,000 − 10,000) 12,500 Matches the PR's base; 15,000 under the statute (Critical 1)
9 Existing higher-tier Case 2 (unchanged) (880,000 − 475,000) capped at 50,000, so fraction 1; 0.25 × 100 25 ✓
10 Over $1M: 2024 single, AGI $1.5M Phase-out = min(0.8 × 110,000, 0.03 × (1,500,000 − 330,200) = 35,094) = 35,094; NY itemized = 0.5 × 60,000; reduction 0 35,094 / 30,000 / 0 ✓
11 Over $10M: 2024 single, AGI $12M Phase-out = min(0.8 × 150,000 = 120,000, 350,094) = 120,000; NY itemized = 0.25 × 100,000 120,000 / 25,000 ✓
12 Over $10M, charity only Phase-out = min(80,000, 350,094) = 80,000; NY itemized = 0.25 × 100,000 80,000 / 25,000 ✓

Other checks.

  • Vectorization, entities and periods: all TaxUnit/YEAR, using max_ and arithmetic only. There is no new state-gated int or enum key (the Add New Hampshire state supplementation for 2026 #10156 pattern), and no new dependency cycle (CI is green).
  • Test periods are plain, and the cases sit in variable-named files.
  • The changelog fragment changelog.d/ny-itemized-reduction-line-45.fixed.md is top-level with type fixed. Its text needs updating per Critical 1.
  • The new comments do not match the source-conflict ratchet patterns.
  • Variable references follow the tuple rules: no title, a single #page=20 (file page 20 is the page with Line 46 and Worksheets 3-6).

Axiom line assessment

The PR body has no axiom line and the PR has no comments, so this is missing. It is a staff policy change, so a valid line is required (Should 1). No 615(f) issue exists in rulespec-us.

CI status

gh pr checks 10658: 36/36 passing at head 8af3938. Nothing is pending and nothing failed.

Branch status

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes per the review above: 1 critical (the 615(f) base leaves out the college tuition deduction, which Tax Law 615(a) includes; the IT-201 line 48 placement is a form layout, and IT-203 puts tuition inside line 45), 2 should (no axiom line; the higher-tier formula change is unpinned), 4 suggestions.

PavelMakarchuk and others added 2 commits November 6, 2026 10:48
Tax Law 615(f) takes its percentage of the deduction allowable under
615(a), which includes the 615(d)(4) college tuition deduction. IT-196
line 45 holds it (line 43) for Form IT-203 filers; the IT-201 line 48
placement is a form layout. Both incremental reductions now use
ny_itemized_deductions_max minus the line 40 limitation.

Add tests with tuition for both tiers, a higher-tier case after the
line 40 limitation, and an integration case with tuition expenses.
Record the statute and form readings as a source-conflict candidate.

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

@hua7450 hua7450 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes at 2afa11600c for one blocking item. The formula change itself is now right.

Blocking

  • source_conflicts/candidates.yaml doesn't parse. The new entry's note: is a plain multi-line scalar that contains checked: forms 2018-2021 … (line 6787, column 29). YAML reads the : as a mapping and stops with "mapping values are not allowed here". That fails all four candidate tests in policyengine_us/tests/code_health/test_source_conflicts.py, which is why Rest (Python shard 5) and Check Rest Python shards are red. Quoting the note, or rewording it to drop the : , fixes it.

Fixed

  • David's critical. Both incremental reductions now use ny_itemized_deductions_max - ny_itemized_deductions_phase_out. That is after the line 40 limitation, with the 615(d)(4) tuition deduction kept in the base, as Tax Law 615(a) and (f) require.
  • New cases checked by hand:
    • Lower tier: 0.25 × 49,359.50 = 12,339.875; 0.25 × 60,000 = 15,000; 0.25 × 59,359.50 = 14,839.875.
    • Higher tier: 0.5 × 0.25 × 49,359.50 = 6,169.9375 and 0.5 × 0.25 × 59,359.50 = 7,419.9375.
    • Integration with tuition: NY itemized 44,519.625, taxable income 315,680.375, tax 0.0633 × 315,680.375 = 19,982.57.
  • The higher-tier change (David's should 2) is now tested.
  • The max_(…, 0) floor (David's suggestion 1) is removed.
  • The conflict is recorded as a candidate with follows: statute.

Nits (optional)

  1. The 0.5 absolute_error_margin on the two integration cases applies to every output in the case, not just ny_income_tax_before_credits. Moving the tax assertion into its own case would keep the itemized amounts at 0.01.
  2. The PR body has no axiom: line (David's should 1), and its Summary still says the base excludes college tuition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the tax-before-credits assertions into their own cases, so that
only they use the 0.5 margin for the supplemental tax rounding.

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

DTrim99 commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Apply the New York itemized deduction adjustment to IT-196 line 45

Summary

The fix commits address every round-1 finding and every point in hua7450's review:

  • Both Tax Law 615(f) reductions now take their percentage of ny_itemized_deductions_max - ny_itemized_deductions_phase_out. That is the deduction after the line 40 limitation, and it keeps the 615(d)(4) college tuition deduction in the base, as 615(a) requires.
  • New cases pin the tuition reading in both tiers, and a new case pins the higher (over $475,000) tier change. Reverting either formula to the form reading or to the pre-PR base now fails tests.
  • The statute-versus-form choice is recorded as a source_conflicts/candidates.yaml candidate with follows: statute. hua7450's YAML parse error in that entry is fixed.
  • The PR body has an axiom line. rulespec-us#2143 is open, labelled pe-parity and dispatch-ready.
  • The tax-before-credits assertions now sit in their own cases at a 0.5 margin. The itemized amounts and taxable income are held to 0.01.

One item is still open, and it is about the branch, not the code. Main's #10300 (merged 2026-11-08) changed the same candidates.yaml block where this PR adds its entry. GitHub therefore reports the PR as conflicting, and no CI has run on the head (d7103c5).

What we know about the head without CI:

  • The previous commit (d470591) passed all 36 checks.
  • The only later commit splits test cases.
  • The three changed NY test files pass locally at the head, 16 of 16.

Merge main and get a green run, and this is ready to approve.

Prior findings

Finding Status Evidence
C1: the 615(f) base left out the college tuition deduction Fixed ny_itemized_deductions_lower_incremental_reduction.py:31-34 and ny_itemized_deductions_higher_incremental_reduction.py:30-33 now compute base = itemized_max - phase_out. ny_itemized_deductions_max adds ny_college_tuition_deduction. Lower Case 5 now expects 0.25 × 60,000 = 15,000 (it was 12,500). The model follows the statute, so no project-lead ruling is needed. Candidate us-ny:itemized-deduction-adjustment-college-tuition-base records the conflict as CONTRIBUTING.md#source-conflicts step 1 asks: follows: statute, state: unreviewed, both the 615(f) and 615(g) tiers, and the 2018-2025 forms. The changelog and code comments were updated. Mutation check: subtracting tuition again fails lower Case 5 (12,500), lower Case 6 (12,339.875), higher Case 4 (6,169.9375) and the integration tuition case (reduction 12,339.875).
S1: no axiom line Fixed The PR body ends with axiom: TheAxiomFoundation/rulespec-us#2143 queued. The issue is OPEN, labelled pe-parity, dispatch-ready, and matches the head (see Axiom).
S2: the higher-tier change was unpinned Fixed Higher Case 3 is the suggested case: NY AGI 500,000, max 50,000, phase-out 640.50, expecting 6,169.9375. Reverting the higher formula to ny_itemized_deductions_max gives 0.5 × 0.25 × 50,000 = 6,250 and fails it. Higher Case 4 would fail too (7,500 against 7,419.9375).
Suggestion 1: the max_(…, 0) floor could never bind Fixed Removed. The base stays non-negative, because the phase-out is at most 80% of a subset of ny_itemized_deductions_max.
Suggestion 2: cite the form page with lines 40-49 Fixed Both formulas add it196_2021.pdf#page=3 (lines 40-49). It is a single-page tuple entry with no title, as the reference rules require.
Suggestion 3: explain the 0.5 integration margin Fixed d7103c5 moves ny_income_tax_before_credits into separate cases at 0.5, with a comment on the rounded $1,056 recapture. The itemized amounts and taxable income stay at 0.01 and pass at that margin locally.
Suggestion 4: four-decimal rounding of the NY AGI fraction (pre-existing, optional) Open (optional) Not changed. The axiom issue lists it as a PolicyEngine choice, but the reason it gives is inaccurate (new Suggestion 1).
hua7450, blocking: candidates.yaml did not parse Fixed e219f7a rewrote the note with parentheses instead of checked: …. The next run, at d470591, passed every check, including all nine Rest Python shards and Check Rest Python shards. The entry has not changed since.
hua7450, nit 1: the 0.5 margin covered every output Fixed Same change as Suggestion 3.
hua7450, nit 2: no axiom line, and a stale Summary Fixed The body now carries the axiom line. Its Summary, "Source conflict" and "Tests" sections describe the head: tuition is in the base, and the tax cases are separate at 0.5.

New findings

Critical

None.

Should

1. The PR conflicts with main in source_conflicts/candidates.yaml, so no CI has run on the head.

  • git merge-tree --write-tree PolicyEngine/main d7103c554d reports one conflict, in source_conflicts/candidates.yaml.
  • Main's Follow state statutes where forms differ: NM medical exemption, VT CDCC, NY/NYC household credit, PR EITC #10300 (a7c93e4, 2026-11-08) made two changes at the spot where this PR appends its candidate:
    • it rewrote the note of us-ny:household-credit-claimable-spouse (PE-US now follows Tax Law 606(b)(3)(B) and 1310(d)(3)(B));
    • it added a new candidate, us-ny:household-credit-separate-return-halving, directly after that entry.
  • The PR's side still carries the old note.
  • GitHub reports the PR as CONFLICTING (DIRTY), and gh pr checks 10658 reports no checks for d7103c5.

Fix:

  1. Merge main.
  2. In the conflicted block, keep main's note and main's new us-ny:household-credit-separate-return-halving entry.
  3. Then add this PR's us-ny:itemized-deduction-adjustment-college-tuition-base entry, before us-ny:real-property-credit-claimable-spouse.
  4. Get a green CI run.

The ids stay unique, and none of them is in main's pinned register, so the candidate checks should pass.

Risk is low:

  • d470591 passed 36/36, and d7103c5 only splits integration cases.
  • Main has not touched any NY itemized-deduction or taxable-income variable, parameter or test since the merge base.
  • Main's edit to test_source_conflicts.py only changes KNOWN_UNMARKED lines for other files.
  • Even so, the head cannot merge until this is resolved.

Suggestions

1. Correct one sentence in rulespec-us#2143.

Under "PolicyEngine implementation", the issue says: "with the $50,000 denominator, the fraction is exact to four places for whole-dollar New York AGI." That holds only when the excess is a multiple of $5:

  • The fraction is x / 50,000 = 0.00002x.
  • An excess of $12,346 gives 0.24692, which the worksheet rounds to 0.2469.
  • The gap is at most 0.00004 × 25% of the base, which is $1 on a $100,000 base.

So the claim is small, but it is wrong, and an encoder could rely on it in a parity comparison.

Fix:

  • State the bound instead of claiming exactness.
  • Refresh the mirrored head SHA after the merge of main.
  • Optionally, say what blocks the encoder run. CLAUDE.md asks a queued issue to record its blocker.

Verified

Hand checks of every new or changed expectation. All of these pass locally at the head:

File, case Hand computation Expected
Lower Case 4 Fraction = min(360,200 − 200,000, 50,000) / 50,000 = 1; 0.25 × (50,000 − 640.50) 12,339.875
Lower Case 5 (changed) No federal AGI is given, so the line 40 limitation is 0; 0.25 × 60,000 (tuition stays in the base) 15,000
Lower Case 6 (new) 0.25 × (60,000 − 640.50) = 0.25 × 59,359.50 14,839.875
Higher Case 3 (new) Fraction = min(500,000 − 475,000, 50,000) / 50,000 = 0.5; 0.5 × 0.25 × 49,359.50 6,169.9375
Higher Case 4 (new) 0.5 × 0.25 × 59,359.50 7,419.9375
Integration, taxsim #1512 (margin now 0.01) Phase-out = min(0.8 × 50,000, 0.03 × (360,200 − 338,850)) = 640.50; reduction = 0.25 × 49,359.50 = 12,339.875; itemized = 50,000 − 640.50 − 12,339.875 = 37,019.625; taxable income = 360,200 − 37,019.625 640.50 / 12,339.875 / 37,019.625 / 323,180.375
Integration, taxsim #1512, tax (own case, 0.5) Taxable income ≤ 323,200 and NY AGI ≥ 211,550, so Worksheet 2 line 3 applies: 0.0633 × 323,180.375 = 20,457.3177. The model is about 0.22 higher (rounded $1,056 recapture). 20,457.32
Integration with tuition (new, 0.01) 615(d)(4): 100% × 10,000 = 10,000. Line 40 worksheet L1 = 60,000 − 10,000 = 50,000, so the phase-out is 640.50. Reduction = 0.25 × 59,359.50 = 14,839.875; itemized = 59,359.50 − 14,839.875 = 44,519.625; taxable income = 360,200 − 44,519.625 10,000 / 640.50 / 14,839.875 / 44,519.625 / 315,680.375
Integration with tuition, tax (new, 0.5) Worksheet 2 line 3: 0.0633 × 315,680.375 = 19,982.5677 19,982.57

Other checks.

  • Local run at the head: ny_itemized_deductions_lower_incremental_reduction.yaml (6), ny_itemized_deductions_higher_incremental_reduction.yaml (4) and ny_itemized_deductions_phase_out_integration.yaml (6) give 16 passed, 0 failed.
  • Callers: the two formulas feed only ny_itemized_deductions_incremental_reduction, then ny_itemized_deductions_reduction, then ny_itemized_deductions. The untouched tests that read them (ny_itemized_deductions_reduction.yaml, ny_itemized_deductions_incremental_reduction.yaml, ny_medical_expense_deduction.yaml) passed in the State shards at d470591.
  • 615(g) tier: ny_itemized_deductions still returns only the charitable share above NY AGI $1,000,000. The candidate's note on this tier is accurate.
  • Source-conflict ratchet: the new Python comments use no conflict-sounding wording. The ratchet scans only variables, parameters and reforms, so the YAML test comments do not count.
  • Candidate entry: it has every step 1 field (id, program, question, years, three sources, readings, pe_us with follows/code/prs, and state). Its code paths exist at the head, and its id is not in main's pinned register.
  • Changelog: the fragment is top-level, typed fixed, and its text matches the head.
  • Merge of main: d470591 merged cleanly, with no hand-resolved blocks.
  • Code patterns: vectorized arithmetic only, TaxUnit/YEAR. There is no new state-gated key.
  • Companion cases in rulespec-us#2143:
    • Case 3: 12,339.875 + 6,169.9375 = 18,509.8125.
    • Case 4 (head of household, NY AGI 160,000): 0.25 × (10,000 / 50,000) × 40,000 = 2,000.
    • Case 5 (single, NY AGI 100,000): 0.

Axiom

axiom: TheAxiomFoundation/rulespec-us#2143 queued. Issue #2143 is OPEN, labelled pe-parity, and was created 2026-11-09.

It is dispatch-ready:

  • target module paths us-ny/statutes/TAX/615/f.yaml and us-ny/policies/dtf/it-196/2021.yaml;
  • registered corpus citations;
  • verbatim 615(a), (d)(4) and (f)(1)-(2);
  • the text of IT-196 lines 43, 45, 46 and 48 and of the Worksheet 3 and 4 lines;
  • the source conflict and PolicyEngine's statute choice;
  • required outputs for each filing status and NY AGI band;
  • five companion cases computed by hand from the statute and the 2021 worksheets, not from PolicyEngine output.

It mirrors d470591. The only later commit splits test cases and changes no code, interpretation or expected value, so the issue matches the head. Suggestion 1 covers its one inaccurate sentence and the SHA refresh after the merge.

CI

  • Head d7103c5: gh pr checks 10658, re-checked at the end of the review, reports no checks. No workflow can run while the PR conflicts with main (Should 1).
  • Previous commit d470591: 36/36 passed on 2026-11-07, covering all Baseline, State, Contrib and Python shards, partner tests and PR validation.
  • e219f7a: Rest (Python shard 1) and Check Rest Python shards failed in test_social_security_input_state_machine with "'DelegatingSimulation' object has no attribute '_bind_to_tax_benefit_system'". That failure came from main under Core 3.34.8 and was fixed by Give the Social Security state backend the method Core 3.34.8's apply_reform calls #10684, which the merge picked up. It is not a finding.
  • Locally at the head: the three changed NY test files pass, 16 of 16.

Branch

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2: every round-1 item and hua7450's review are fixed. One should remains: the PR conflicts with main in source_conflicts/candidates.yaml (#10300), so no CI has run on this head. Merge main (keep main's block, add this PR's entry) and get a green run; 2 optional suggestions.

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

DTrim99 commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 3): Apply the New York itemized deduction adjustment to IT-196 line 45

Summary

Since round 2 (head d7103c5) there is one new commit: 410ea26, a merge of main with one hand-resolved block in source_conflicts/candidates.yaml. The resolution is correct:

The branch is now 6 ahead and 0 behind main, and it merges cleanly. CI ran on the head, and all 33 recorded jobs passed, including the PR's three NY test files and the source-conflict guard.

The workflow run itself is marked "failure" because two Contrib matrix jobs never started (see CI). That is an infrastructure problem, not a finding. Static checks show those two groups cannot be affected by this change.

The PR's own code and tests are byte-for-byte what we reviewed in round 2. The round-2 Should is fixed. The two optional suggestions are still open.

Prior findings

Finding Status Evidence
Should 1: the branch conflicted with main in candidates.yaml (#10300), so no CI ran on the head Fixed Merge 410ea26 keeps main's block and adds this PR's entry (details under Verified). git merge-tree --write-tree PolicyEngine/main 410ea26a32 is clean, and the merged tree equals the head tree (13b0630546). Run 42665231927 at the head passed all 33 recorded jobs. Rest (Python shard 4) ran test_source_conflicts.py, 103 passed and 0 failed. Baseline (states-shard-4) ran the three changed NY files: lower 6, higher 4 and integration 6, all passing. Two Contrib jobs did not start (CI note).
Suggestion 1: four-decimal rounding of the NY AGI fraction (pre-existing, optional) Open (optional) Not changed.
Suggestion 2: rulespec-us#2143 says the fraction is "exact to four places for whole-dollar New York AGI" Open The issue body still has the sentence, and it still mirrors head d470591. It was last updated 2026-11-09 14:50 UTC, before our round-2 review. The PR's code and expected values have not changed since d470591, so the issue still matches the head in substance (see Axiom).
Round-1 C1, S1, S2 and suggestions 1-3, and hua7450's points Fixed (round 2), still fixed The PR-side diffs of both formulas, the three test files and the changelog are identical, line for line, to the diffs at d7103c5 against its merge base.

New findings

Critical

None.

Should

None.

Suggestions

None new. The two open ones are in the table above.

Verified

The hand-resolved candidates.yaml block. We parsed the file at the head, at d7103c5 and on main:

  • The head has 406 candidates. Main has 405, and the one extra id is this PR's entry. There are no duplicate ids.
  • Every one of main's 405 entries is present at the head and equal to main's version. That includes us-ny:household-credit-claimable-spouse (the Follow state statutes where forms differ: NM medical exemption, VT CDCC, NY/NYC household credit, PR EITC #10300 note on Tax Law 606(b)(3)(B) and 1310(d)(3)(B)) and us-ny:household-credit-separate-return-halving (state ruled, follows: statute, 606(b)(3)(C) and 1310(d)(3)(C)).
  • This PR's entry appears exactly once and equals its d7103c5 version. It keeps follows: statute and state: unreviewed, with its three code paths and its sources.
  • Order:
    • The New York ids run in sorted order: ... household-credit-claimable-spouse, household-credit-separate-return-halving, itemized-deduction-adjustment-college-tuition-base, real-property-credit-claimable-spouse, ...
    • The file as a whole is not sorted on main either: three pairs are out of order, none of them New York and none from this PR.
  • The top-level keys (version, candidates) match main.

Source-conflict guard at the head.

  • Locally, test_source_conflicts.py gave 102 passed and 1 failed. The failure was test_unscannable_files_fail_closed, a known Windows-only failure: it feeds synthetic text with a vertical-tab character and does not read the repo.
  • On Linux CI at the head, Rest (Python shard 4) shows all 103 tests PASSED, including that one.
  • The PR adds no conflict-sounding Python comments, so the KNOWN_UNMARKED counts are unaffected.

No other changes from the merge. git diff PolicyEngine/main 410ea26a32 touches only the PR's seven files: two formulas, three test files, the changelog and candidates.yaml.

Expectations. No expected value is new or changed since round 2. The round-2 hand checks still hold, and CI passed each of them at the head:

Case Computation Expected
Lower Case 4 0.25 × (50,000 − 640.50) 12,339.875
Lower Case 5 0.25 × 60,000 (tuition in the base, no line 40 limitation) 15,000
Lower Case 6 0.25 × (60,000 − 640.50) 14,839.875
Higher Case 3 (25,000 / 50,000) × 0.25 × 49,359.50 6,169.9375
Higher Case 4 0.5 × 0.25 × 59,359.50 7,419.9375
Integration, taxsim #1512 50,000 − 640.50 − 12,339.875 = 37,019.625; 360,200 − 37,019.625 37,019.625 / 323,180.375
Integration, tax (0.5 margin) 0.0633 × 323,180.375 = 20,457.318 20,457.32
Integration with tuition 59,359.50 − 14,839.875 = 44,519.625; 360,200 − 44,519.625 44,519.625 / 315,680.375
Integration with tuition, tax (0.5 margin) 0.0633 × 315,680.375 = 19,982.568 19,982.57

Main-side guards.

Axiom

axiom: TheAxiomFoundation/rulespec-us#2143 queued is still in the PR body. Issue #2143 is OPEN, labelled pe-parity and dispatch-ready, as round 2 found. It has no comments.

It mirrors d470591. Since then the branch has only split test cases (d7103c5) and merged main (410ea26). No code, interpretation or expected value has changed, so the issue is not stale in substance.

Suggestion 2 still applies:

  • Replace the "exact to four places" sentence with the bound. The worksheet's four-place rounding can differ when the excess over the threshold is not a multiple of $5, by at most about $1 on a $100,000 base.
  • Refresh the mirrored SHA to 410ea26.

CI

Head 410ea26, run 42665231927 (re-checked at the end of the review): gh pr checks 10658 shows 33 pass and 0 fail. GitHub's check rollup reads SUCCESS, and no check is marked required. The run includes:

  • all 11 Baseline shards;
  • 7 Contrib groups;
  • Microsimulation and 8 Rest Python shards, plus Plan and Check;
  • Household API Partners, Package and compatibility, PR validation and Quick Feedback.

The workflow run's own conclusion is "failure", with every recorded job a success. Compared with the 36-job run at d470591, three jobs are absent:

  • Rest (Python shard 9) is not a gap. The shard plan chose 8 shards this time ("Rest Python plan: 8 shards, times from release run 42649396429"), and Check Rest Python shards reports plan=success, shards=success.
  • Contrib (congress) and Contrib (other-shard-1) never started. They are in the head's contrib-tests.yaml matrix, but the run has no job record or log for them. Other PR runs the same afternoon (for example 42664894758) ran all nine Contrib groups.

We classify these two as infrastructure, not a finding:

  • The PR changes no workflow.
  • Both groups passed at d470591, whose PR-side code is identical.
  • The PR's change only moves the 615(f) base when the line 40 limitation is positive, which needs itemizable deductions. The New York-touching files in those groups have no itemized-deduction inputs. Those files are aca/ptc_*, local/ny/nyc_mamdani_income_tax.yaml, the three local/nyc/stc files, taxsim/outputs/taxsim_outputs.yaml and congress/mcdonald_rivet/working_parents_tax_relief_act.yaml.

"Re-run failed jobs" on run 42665231927 would let the workflow run read green before merge.

Branch

  • The branch is 6 ahead and 0 behind main (main is 61d9e85, the merge's second parent).
  • git merge-tree --write-tree PolicyEngine/main 410ea26a32 is clean, and its tree equals the head tree.
  • Sibling Apply New York's pre-TCJA mortgage debt limits to the interest deduction #10596 edits ny_itemized_deductions_max and ny_itemized_deductions_phase_out, not these two formulas, and does not overlap textually.
  • hua7450's CHANGES_REQUESTED review (at 2afa116) is still on record. Round 2 confirmed that each of his points is fixed, but he will need to re-review or dismiss it before merge.

Verdict: APPROVE

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 3: the merge of main resolves the candidates.yaml conflict correctly (main's entries intact, this PR's entry once), and CI ran on this head with all completed jobs passing; please re-run the two Contrib jobs that never started before merging. Two optional suggestions remain.

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.

3 participants