From ee18abb2e11bad1f6fb0508d9a1be69785050820 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Mon, 1 Jun 2026 01:18:35 -0400 Subject: [PATCH 1/2] Fix NJ filing-threshold phantom tax 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) --- ...x-nj-filing-threshold-phantom-tax.fixed.md | 1 + .../states/nj/tax/income/nj_income_tax.yaml | 33 +++++++++++++++++-- .../gov/states/nj/tax/income/nj_income_tax.py | 7 ++-- 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 changelog.d/fix-nj-filing-threshold-phantom-tax.fixed.md diff --git a/changelog.d/fix-nj-filing-threshold-phantom-tax.fixed.md b/changelog.d/fix-nj-filing-threshold-phantom-tax.fixed.md new file mode 100644 index 00000000000..c641fbb4f8a --- /dev/null +++ b/changelog.d/fix-nj-filing-threshold-phantom-tax.fixed.md @@ -0,0 +1 @@ +Zero the New Jersey pre-credit liability before refundable credits flow through when AGI is at or below the filing threshold. diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml index 06fa60434d9..6998cfe2604 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml @@ -17,16 +17,45 @@ output: nj_income_tax: 0 -- name: NJ income tax below filing threshold but tax is negative because of refundable credits. +- name: Below the filing threshold, tax is zeroed before applying refundable credits period: 2022 input: state_code: NJ nj_agi: 19_999 filing_status: JOINT + # Per N.J.S.A. 54A:2-4, the pre-credit liability is wiped before + # credits flow through; the full $1,001 of credits is refunded + # rather than netted against the $1,000 of bracket-schedule tax. nj_income_tax_before_refundable_credits: 1_000 nj_refundable_credits: 1_001 output: - nj_income_tax: -1 + nj_income_tax: -1_001 + +- name: NJ joint at $20K gross income owes no tax, full refundable credits + period: 2025 + input: + people: + head: {age: 40} + spouse: {age: 40, employment_income: 20_000} + k1: {age: 2} + k2: {age: 11} + tax_units: + tax_unit: + members: [head, spouse, k1, k2] + tax_unit_childcare_expenses: 3_000 + marital_units: + m: + members: [head, spouse] + m1: + members: [k1] + m2: + members: [k2] + households: + household: + members: [head, spouse, k1, k2] + state_name: NJ + output: + nj_income_tax: -3_860.8 - name: NJ income tax above filing threshold so tax is calculated amount minus refundable credits. period: 2022 diff --git a/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py b/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py index f09dfa09ef6..9a5699bca6b 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py @@ -15,10 +15,11 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) income_tax = tax_unit("nj_income_tax_before_refundable_credits", period) refundable_credits = tax_unit("nj_refundable_credits", period) - # if AGI is at or below filing threshold, tax should not be positive, - # but tax could still be negative if filer is due refundable credits + # N.J.S.A. 54A:2-4 zeroes tax for filers at or below the filing + # threshold; refundable credits then flow through against zero tax + # rather than against the bracket-schedule amount. return where( agi <= p.filing_threshold[filing_status], - min_(0, income_tax - refundable_credits), + -refundable_credits, income_tax - refundable_credits, ) From e9d3dfb6164f689146a76d6821ca80ee8b98f67a Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Mon, 1 Jun 2026 01:26:19 -0400 Subject: [PATCH 2/2] Add NJ income tax reference attribute and threshold-boundary tests 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) --- .../states/nj/tax/income/nj_income_tax.yaml | 23 +++++++++++++++++++ .../gov/states/nj/tax/income/nj_income_tax.py | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml index 6998cfe2604..33d6bcdad9d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/tax/income/nj_income_tax.yaml @@ -57,6 +57,29 @@ output: nj_income_tax: -3_860.8 +- name: AGI exactly at the SINGLE filing threshold still zeroes pre-credit tax + period: 2022 + input: + state_code: NJ + nj_agi: 10_000 + filing_status: SINGLE + nj_income_tax_before_refundable_credits: 500 + nj_refundable_credits: 200 + output: + # <= comparison: $10,000 is still inside the no-tax zone. + nj_income_tax: -200 + +- name: AGI one dollar above the SINGLE filing threshold falls through to schedule + period: 2022 + input: + state_code: NJ + nj_agi: 10_001 + filing_status: SINGLE + nj_income_tax_before_refundable_credits: 500 + nj_refundable_credits: 200 + output: + nj_income_tax: 300 + - name: NJ income tax above filing threshold so tax is calculated amount minus refundable credits. period: 2022 input: diff --git a/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py b/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py index 9a5699bca6b..2a9ebe5b167 100644 --- a/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py +++ b/policyengine_us/variables/gov/states/nj/tax/income/nj_income_tax.py @@ -8,6 +8,10 @@ class nj_income_tax(Variable): unit = USD definition_period = YEAR defined_for = StateCode.NJ + reference = ( + "https://law.justia.com/codes/new-jersey/title-54a/section-54a-2-4/", + "https://www.nj.gov/treasury/taxation/pdf/current/1040i.pdf#page=5", + ) def formula(tax_unit, period, parameters): p = parameters(period).gov.states.nj.tax.income