Skip to content

Fix NJ filing-threshold phantom tax#8551

Open
PavelMakarchuk wants to merge 2 commits into
mainfrom
fix-nj-filing-threshold-phantom-tax
Open

Fix NJ filing-threshold phantom tax#8551
PavelMakarchuk wants to merge 2 commits into
mainfrom
fix-nj-filing-threshold-phantom-tax

Conversation

@PavelMakarchuk
Copy link
Copy Markdown
Collaborator

Summary

Closes #8550.

nj_income_tax returned min_(0, income_tax − refundable_credits) when AGI was at or below the filing threshold, which netted the bracket-schedule tax (statutorily zeroed under N.J.S.A. 54A:2-4) against refundable credits and understated refunds by exactly the phantom tax amount. Replace with -refundable_credits in the below-threshold branch.

Test plan

  • Add the integration test from the issue: joint filer, 2 deps, $20K wages → nj_income_tax: -3_860.8.
  • Update the existing test (nj_agi: 19_999, $1,000 tax, $1,001 credits) that asserted the buggy -1 to the correct -1_001.
  • Full NJ regression passes (521 tests).
  • CI green.

🤖 Generated with Claude Code

Closes #8550

When AGI is at or below the filing threshold, N.J.S.A. 54A:2-4 zeroes
the pre-credit liability before refundable credits flow through. The
previous formula returned min_(0, income_tax - refundable_credits),
which netted the (statutorily zeroed) bracket-schedule amount against
refundable credits, understating refunds by exactly the phantom tax.

Replace with -refundable_credits in the below-threshold branch. Update
the existing test that codified the buggy behavior and add the
integration test from the issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (16a0dce) to head (e9d3dfb).
⚠️ Report is 31 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main     #8551      +/-   ##
===========================================
+ Coverage   94.69%   100.00%   +5.30%     
===========================================
  Files           7         1       -6     
  Lines         113        16      -97     
  Branches        2         0       -2     
===========================================
- Hits          107        16      -91     
+ Misses          6         0       -6     
Flag Coverage Δ
unittests 100.00% <100.00%> (+5.30%) ⬆️

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

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

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

Add link to N.J.S.A. 54A:2-4 and the 2025 NJ-1040 instructions on
nj_income_tax. Add boundary tests at exactly the SINGLE filing
threshold and one dollar above to pin the inclusive <= comparison
and the cliff behavior introduced by the formula change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@PavelMakarchuk PavelMakarchuk marked this pull request as ready for review June 1, 2026 13:50
@PavelMakarchuk PavelMakarchuk requested a review from hua7450 June 1, 2026 13:50
Copy link
Copy Markdown
Collaborator

@hua7450 hua7450 left a comment

Choose a reason for hiding this comment

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

Program Review — PR #8551: Fix NJ filing-threshold phantom tax

Source Documents

  • Statute: N.J.S.A. 54A:2-4 (Minimum taxable income) — confirmed correct citation. The reference-checker raised a precision question (is 54A:2-4 the right section, or should it be 54A:2-1?); the regulatory-reviewer RESOLVED it: 54A:2-4 ("A taxpayer shall not be subject to tax ... if gross income is $10,000 or less") is exactly the "no tax at/below the filing threshold" authority the fix relies on. Not an open finding.
  • Instructions: 2025 NJ-1040 Instructions printed p.3 / file p.5 (70 pages) — filing-threshold table ("Do You Have to File a New Jersey Income Tax Return?": Single/Married-separate = $10,000; Married-joint/HoH/Qualifying widow(er) = $20,000). #page=5 citation verified correct (physical PDF page, confirmed by pdf-collector, reference-checker, and regulatory-reviewer).
  • Scope: PR changes only (formula logic; no parameter values changed).

Branch Status

⚠ PR branch is 41 commit(s) behind main (2 ahead). Consider rebasing before merging. Review was scoped to the PR's actual changes (merge-base diff) — staleness did not affect findings. Per the Delaware-FS4 dependency-staleness check, both consumers of nj_income_tax (state_income_tax.py:27, household_state_income_tax.py:26) only pass the value through a select() branch and neither assumes the old min_(0, …) non-negative clamp — no upstream variable goes stale from this change.

Summary of the fix

For tax units at or below the NJ filing threshold, nj_income_tax changed from min_(0, income_tax - refundable_credits) to -refundable_credits, so the statutorily-zeroed bracket-schedule tax (the "phantom tax," still computed upstream in nj_income_tax_before_refundable_credits) no longer nets against refundable credits. The regulatory-reviewer confirmed this is correct: N.J.S.A. 54A:2-4 imposes zero tax at-or-below the threshold, all four credits folded into nj_refundable_credits (nj_property_tax_credit, nj_eitc, nj_cdcc, nj_ctc) are refundable for every year the model covers, and the <= (at-or-below) boundary matches the NJ-1040 instructions ("not required to file if your income is at or below the filing threshold"). The above-threshold branch (income_tax - refundable_credits) is unchanged.

Critical (Must Fix)

None.

Should Address

  1. Statute reference URL is fragile / 403s to automated tools (durability nit, not a wrong citation).
    nj_income_tax.py:12 cites https://law.justia.com/codes/new-jersey/title-54a/section-54a-2-4/. Justia returned HTTP 403 to the fetcher (and FindLaw / public.law block automated access too). The URL works fine in a browser and the citation itself is CORRECT — this is purely an anti-bot / link-durability concern. Other NJ files in this repo cite the more durable NJ Legislature portal (lis.njleg.state.nj.us / pub.njleg.gov). Consider adding (or switching to) an official NJ Legislature statute URL for N.J.S.A. 54A:2-4 alongside the NJ-1040 instructions link, so the statutory basis stays verifiable. (Raised by both the regulatory-reviewer (S1) and the reference-checker; combined here.)

  2. JOINT at-threshold is only tested via a real household, not an AGI-direct case — and the coupling is fragile.
    nj_income_tax.yaml. The SINGLE threshold has a clean AGI-direct triple (9_999 / 10_000 / 10_001). The JOINT threshold relies on the real-household $20K case to land at exactly nj_agi == 20_000. It does today, but that equality is incidental (depends on nj_total_income minus NJ exclusions resolving to exactly the gross wage); a future change to NJ income/exclusion logic could nudge nj_agi off 20_000 and silently move the case to the above-threshold branch without the assertion catching it (before_credits is only $210, so the value would still look "negative-ish"). Add AGI-direct JOINT cases mirroring the SINGLE triple — at minimum nj_agi = 20_000 (at) and nj_agi = 20_001 (just above) with explicit before_credits / refundable inputs — to pin the JOINT boundary independently of the income pipeline.

  3. The married-filing-separately (SEPARATE) branch is untested.
    nj_income_tax.yaml. NJ recognizes SEPARATE with its own $10,000 filing threshold (filing_threshold.yaml: SEPARATE: 10_000), and the formula indexes p.filing_threshold[filing_status], so SEPARATE flows through the same where. It has zero coverage. Per the testing-patterns skill, every value of a dimension the formula keys on needs at least one case. Add one below-threshold-with-credits SEPARATE case (e.g. nj_agi = 9_999, before_credits = 500, refundable = 200-200).

Suggestions

  1. No absolute_error_margin in the test file. nj_income_tax is a USD output. Per the user's standing rule, add absolute_error_margin: 0.1 (NOT 1, which would mask sign errors), especially on the -3_860.8 real-household case whose value comes from a real computation. Tests pass today on default tolerance; this is a convention/intent nit.

  2. Mixed state_code vs state_name style. Most cases use state_code: NJ; the JOINT $20K real-household case uses state_name: NJ. Both valid; standardize for readability.

  3. No zero-income / empty-AGI case. A nj_agi = 0 case (below threshold, no credits → 0; variant with credits → full refund) would document degenerate-input behavior. Low value (9_999 already covers below-threshold-no-credit), optional.

  4. No "credits far exceed any plausible tax" above-threshold case. An above-threshold case where refundable > before_credits (e.g. before_credits = 100, refundable = 5_000-4_900) would confirm the above-threshold branch still nets into negative territory and isn't accidentally floored. Optional.

  5. Document the CDCC refundability vintage in nj_refundable_credits.py. nj_cdcc is correctly refundable for all years the model covers (its rate param starts 2021-01-01, the year it became refundable per P.L. 2021 c.128). But the NJ CDCC was non-refundable in TY2020 (P.L. 2018 c.45). A one-line comment ("CDCC is refundable from TY2021; not modeled before 2021") in nj_refundable_credits.py:11-16 would stop a future maintainer from backdating nj_cdcc to 2018–2020 and silently treating a then-non-refundable credit as refundable. No code change in this PR's file.

  6. Comment precision. The formula comment (nj_income_tax.py:22-24) says credits "flow through against zero tax." Strictly, the bracket tax is still computed upstream and simply ignored in this branch by returning -refundable_credits. Substantively right; if tightening, say "the computed bracket tax is discarded below the threshold; only refundable credits remain." Optional.

PDF / Value Audit

No parameter values changed; no value audit applicable. The reference page citation (#page=5 on the NJ-1040 instructions) was independently verified correct (physical PDF page 5 = the filing-threshold table; the document has a +2 offset, printed page 3 = physical page 5). No off-by-one.

Validation Summary

Check Result
Regulatory Accuracy Correct — fix matches N.J.S.A. 54A:2-4 (zero tax at/below threshold + fully refundable credits); <= boundary matches NJ-1040; 0 critical
Reference Quality OK — #page=5 verified; statute citation 54A:2-4 confirmed correct; statute-URL durability nit only
Code Patterns 0 critical, 0 should-address — correctly vectorized where, parameter-driven, valid fixed changelog, correct reference format
Test Coverage 0 critical; 2 should-address (JOINT AGI-direct at/above cases; untested SEPARATE branch)
CI Status All 26 checks passing (changelog, full baseline suites, partners, lint, codecov)

Review Severity: APPROVE

0 critical findings and all 26 CI checks green. The should-address items are low-risk follow-ups — a reference-durability swap and two test-coverage additions (JOINT AGI-direct boundary + SEPARATE branch) — not merge blockers. The fix removes a genuine phantom-tax bug with no upstream breakage. Recommend APPROVE with the should-address items as optional follow-ups.

Next Steps

To auto-fix the optional items: /fix-pr 8551

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.

NJ minimum-filing-threshold formula leaks phantom tax when refundable credits exceed tax-before-credits (§ 54A:2-4)

2 participants