Skip to content

Add NJ budget housing reforms#7763

Open
daphnehanse11 wants to merge 3 commits intoPolicyEngine:mainfrom
daphnehanse11:nj-budget-housing
Open

Add NJ budget housing reforms#7763
daphnehanse11 wants to merge 3 commits intoPolicyEngine:mainfrom
daphnehanse11:nj-budget-housing

Conversation

@daphnehanse11
Copy link
Collaborator

Implements StayNJ and ANCHOR changes from Gov. Sherrill's budget proposal.

Closes #7762

🤖 Generated with Claude Code

daphnehanse11 and others added 3 commits March 12, 2026 10:08
Implements StayNJ and ANCHOR changes from Gov. Sherrill's budget proposal.

Closes PolicyEngine#7762

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement separately toggleable contributed reforms for NJ's FY2026
budget proposals:
- ANCHOR: expanded property tax relief with higher benefits for seniors
- StayNJ: new senior property tax credit with $250K income limit

Includes parameters, reform modules, and YAML test cases (12 tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tions

The previous commit accidentally included WA SB6346 import from another
branch, causing ModuleNotFoundError in CI. Removed it and properly
registered the NJ StayNJ and ANCHOR reforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@daphnehanse11 daphnehanse11 requested a review from DTrim99 March 13, 2026 14:42
@daphnehanse11 daphnehanse11 marked this pull request as ready for review March 13, 2026 14:42
@DTrim99
Copy link
Collaborator

DTrim99 commented Mar 13, 2026

PR Review

✅ Validation Summary

Check Result
CI Status ✅ All Passing
Regulatory Accuracy ✅ Correct - StayNJ $250K/$4K confirmed, ANCHOR senior homeowner bonus expiry confirmed
Reference Quality ✅ Good - Multiple authoritative sources cited
Code Patterns ✅ Follows standard reform patterns
Test Coverage ✅ Comprehensive (6 test cases per reform)

🟢 Minor Suggestion

The P.L. 2023, c.75 PDF references could include page anchors for direct navigation:

  • stay_nj/income_limit.yaml: Add #page=1 to the href
  • stay_nj/max_benefit.yaml: Add #page=1 to the href

Example:

href: https://pub.njleg.state.nj.us/Bills/2022/PL23/75_.PDF#page=1

Conclusion

APPROVE - Well-implemented reform that correctly models:

  • Senior homeowner ANCHOR bonus expiry ($1,750→$1,500, $1,250→$1,000)
  • Senior renter ANCHOR bonus extension (unchanged at $700)
  • StayNJ income limit reduction to $250K
  • StayNJ max benefit cap at $4,000

The minor suggestion is optional and does not block merge.

@PavelMakarchuk
Copy link
Collaborator

Program Review: PR #7763 — Add NJ budget housing reforms

Source Documents

Critical (Must Fix)

  1. where() used on scalar parameter boolean — will crash for pre-2027 periods.
    where(reform_active, reform_value, baseline_value) evaluates both branches. Reform parameters (income_limit, max_benefit, lower_income, upper_income) only have values from 2027-01-01. For any period before 2027, accessing these parameters raises ParameterNotFoundError even when in_effect is false. Must switch to Python if p_reform.in_effect: / else: pattern which short-circuits.

    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/reforms/states/nj/stay_nj/nj_stay_nj_reform.py (eligibility lines 29-33, benefit lines 61-65)
    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/reforms/states/nj/anchor/nj_anchor_reform.py (lines 41-49)
  2. Reform parameters have no value before 2027-01-01 — no sentinel base value.
    Even after fixing issue Basic prototype #1 (switching to if), adding a sentinel value (e.g., 0000-01-01: 0) to each reform parameter would provide defense-in-depth against future regressions. This is a secondary fix; resolving Basic prototype #1 alone is sufficient.

    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/parameters/gov/contrib/states/nj/stay_nj/income_limit.yaml
    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/parameters/gov/contrib/states/nj/stay_nj/max_benefit.yaml
    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/parameters/gov/contrib/states/nj/anchor/homeowner/senior/amount/lower_income.yaml
    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/parameters/gov/contrib/states/nj/anchor/homeowner/senior/amount/upper_income.yaml

Should Address

  1. absolute_error_margin: 1 in all 12 reform test cases. A margin of 1 makes true (1) and false (0) indistinguishable for boolean outputs like nj_staynj_eligible. Should be 0.01.

    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/tests/policy/contrib/states/nj/anchor/nj_anchor_budget_reform.yaml
    • /Users/pavelmakarchuk/policyengine-us/policyengine_us/tests/policy/contrib/states/nj/stay_nj/nj_stay_nj_budget_reform.yaml
  2. Missing test: both StayNJ + ANCHOR reforms active simultaneously. StayNJ subtracts ANCHOR benefit; when both reforms are active, ANCHOR amounts change, which affects StayNJ output. No combined test exists.

  3. Missing test: property tax cap boundary at $8,000. The reform changes the cap breakpoint from $13,000 (50% = $6,500) to $8,000 (50% = $4,000). This new boundary is untested.

  4. Missing test: income at $249,999 under StayNJ reform. Exact boundary ($250K) is tested but just-below is not.

  5. Missing test: $150K income boundary for ANCHOR senior homeowner tiers under reform.

  6. Missing test: couple scenario under either reform to verify greater_age_head_spouse logic.

  7. StayNJ reference title/href mismatch for P.L. 2024, c.88. Title says "P.L. 2024, c.88 - Stay NJ Amendment" but href points to the NJ Division of Taxation webpage, not the session law.

    • All 4 files in policyengine_us/parameters/gov/states/nj/tax/income/credits/staynj/
  8. Duplicate href in StayNJ parameter references. Two references share the same https://www.nj.gov/treasury/taxation/staynj/index.shtml URL with different titles.

  9. StayNJ references missing section numbers. "P.L. 2023, c.75 - Stay NJ Act" is too vague for a 14-page, 19-section law. Each parameter should cite the specific section (e.g., Section 2 for income limit, Section 3 for max benefit).

  10. ANCHOR references cite "P.L. 2022" without chapter number. "P.L. 2022 - ANCHOR Program" is an incomplete session law citation (missing chapter number).

    • All 7 ANCHOR baseline parameter files
  11. ANCHOR parameters lack any primary legal source. Both references in every ANCHOR file point to NJ Treasury websites. No statute, regulation, or appropriations act is cited.

  12. Parameter descriptions use non-standard verbs. Per policyengine-parameter-patterns, descriptions should use limits, provides, sets, excludes, deducts, or uses. Current descriptions use "applies", "reduces", "caps".

    • All 6 contrib parameter files
  13. Parameter labels use "NJ" instead of "New Jersey". Per policyengine-parameter-patterns, labels should spell out the state name.

    • All 6 contrib parameter files
  14. 0000-01-01 as base date for in_effect parameters. Other reforms typically use a reasonable inception date (e.g., 2023-01-01: false for StayNJ). While functional, it looks like a placeholder.

Suggestions

  1. Pre-existing: Baseline ANCHOR parameters do not model the FY2026 sunset of Section 15 bonuses. The $250 bonus is baked into baseline parameters ($1,750/$1,250) from 2022 onward with no end date. Per P.L. 2023, c.75, Section 15(b), the bonus only applies for FY2024-2026. Recommend filing a separate issue.

  2. Senior renter $250 bonus FY2027 extension not modeled. The Budget in Brief (p.18) proposes maintaining the $250 ANCHOR bonus for senior renters beyond FY2026. Not in scope for this PR but could be a follow-up.

  3. StayNJ references should cite permanent statutes (N.J.S.A. 54:4-8.75a-p) instead of or in addition to session laws. Permanent citations incorporate amendments.

  4. Verbose comments in formula code. Comments like "Senior homeowner amounts: reform overrides when active" describe what the code does rather than why. Minor; matches pre-existing baseline style.

  5. add() with single-element list (e.g., add(tax_unit, period, ["nj_gross_income"])) is equivalent to direct variable access when at the same entity level. Follows pre-existing pattern; not a bug.

  6. 12% surplus trigger from P.L. 2023, c.75 not modeled. The repo uses a simple boolean toggle instead. Acceptable for a contributed reform.

  7. Baseline $6,500 max benefit cap could not be confirmed from the two provided PDFs alone. Likely comes from P.L. 2024, c.88 (cited in parameter metadata but not among audit PDFs). Should be verified separately.

PDF Audit Summary

Category Count
Confirmed correct 7
Mismatches 0
Unmodeled items 6
Pre-existing issues 1

Note: The StayNJ audit flagged the baseline $6,500 cap as "needs verification" because it was not found in the two reviewed PDFs. This is a verification gap (the value is cited to P.L. 2024, c.88, which was not among the audit documents), not an actual mismatch. No value mismatches were found between code and source documents.

Validation Summary

Check Result
Regulatory Accuracy 0 issues (all reform values match law/proposal)
Reference Quality 5 issues (title/href mismatches, missing sections, incomplete citations)
Code Patterns 2 issues (where() on scalar boolean, parameter date gap)
Test Coverage 6 gaps (error margin, combined reform, boundary tests, couple scenario)
PDF Value Audit 0 mismatches / 7 confirmed
CI Status All passing

Review Severity: REQUEST_CHANGES

The where() pattern on scalar parameter booleans is a runtime crash risk for pre-2027 periods (Critical #1). Both branches are always evaluated by where(), which means reform parameters that only exist from 2027-01-01 will be accessed for earlier periods even when in_effect is false, causing ParameterNotFoundError. This must be fixed before merge.

Next Steps

To auto-fix issues: /fix-pr 7763

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.

Add NJ budget housing reforms

3 participants