Fix NJ filing-threshold phantom tax#8551
Conversation
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 Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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>
hua7450
left a comment
There was a problem hiding this comment.
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=5citation 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
-
Statute reference URL is fragile / 403s to automated tools (durability nit, not a wrong citation).
nj_income_tax.py:12citeshttps://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.) -
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$20Kcase to land at exactlynj_agi == 20_000. It does today, but that equality is incidental (depends onnj_total_incomeminus NJ exclusions resolving to exactly the gross wage); a future change to NJ income/exclusion logic could nudgenj_agioff20_000and silently move the case to the above-threshold branch without the assertion catching it (before_creditsis only $210, so the value would still look "negative-ish"). Add AGI-direct JOINT cases mirroring the SINGLE triple — at minimumnj_agi = 20_000(at) andnj_agi = 20_001(just above) with explicitbefore_credits/refundableinputs — to pin the JOINT boundary independently of the income pipeline. -
The married-filing-separately (SEPARATE) branch is untested.
nj_income_tax.yaml. NJ recognizes SEPARATE with its own$10,000filing threshold (filing_threshold.yaml:SEPARATE: 10_000), and the formula indexesp.filing_threshold[filing_status], so SEPARATE flows through the samewhere. 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
-
No
absolute_error_marginin the test file.nj_income_taxis a USD output. Per the user's standing rule, addabsolute_error_margin: 0.1(NOT1, which would mask sign errors), especially on the-3_860.8real-household case whose value comes from a real computation. Tests pass today on default tolerance; this is a convention/intent nit. -
Mixed
state_codevsstate_namestyle. Most cases usestate_code: NJ; the JOINT $20K real-household case usesstate_name: NJ. Both valid; standardize for readability. -
No zero-income / empty-AGI case. A
nj_agi = 0case (below threshold, no credits →0; variant with credits → full refund) would document degenerate-input behavior. Low value (9_999already covers below-threshold-no-credit), optional. -
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. -
Document the CDCC refundability vintage in
nj_refundable_credits.py.nj_cdccis 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") innj_refundable_credits.py:11-16would stop a future maintainer from backdatingnj_cdccto 2018–2020 and silently treating a then-non-refundable credit as refundable. No code change in this PR's file. -
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
Summary
Closes #8550.
nj_income_taxreturnedmin_(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_creditsin the below-threshold branch.Test plan
nj_income_tax: -3_860.8.nj_agi: 19_999, $1,000 tax, $1,001 credits) that asserted the buggy-1to the correct-1_001.🤖 Generated with Claude Code