Skip to content

Conversation

@DTrim99
Copy link
Collaborator

@DTrim99 DTrim99 commented Dec 9, 2025

Summary

This PR implements Minnesota's 2025 income tax changes and adds several new subtraction and credit programs that were missing from the codebase.

New Programs Added

Program Type Description Available Since
K-12 Education Credit Refundable Credit 75% of qualifying K-12 expenses, up to $1,500/child 1998
K-12 Education Subtraction Subtraction $1,625 (K-6) / $2,500 (7-12) per child max 1997
529 Contribution Subtraction Subtraction $1,500 single / $3,000 joint max 2017
Military Pension Subtraction Subtraction 100% of military retirement pay 2016
Active Duty Military Pay Subtraction Subtraction 100% of active duty pay 2005

Parameter Updates for 2025

  • Tax brackets for all filing statuses (single, joint, separate, head of household, surviving spouse)
  • Standard deduction amounts and high-income reduction thresholds
  • Itemized deduction reduction thresholds
  • Personal exemption amounts and AGI phase-out thresholds
  • Alternative Minimum Tax (AMT) rates and income thresholds
  • Child and Working Families Credit (CWFC) amounts and phase-out thresholds
  • Child and Dependent Care Credit (CDCC) parameters
  • Marriage credit parameters
  • Social Security subtraction income thresholds
  • Public pension subtraction caps and reduction thresholds
  • Charity subtraction thresholds

Key Corrections

  • 529 Contribution Subtraction: Corrected start date from 2023 to 2017 (when program was actually enacted)
  • Updated statute references: Fixed subdivision number for 529 subtraction (Subd. 23, not 31)
  • sources.yaml: Added 529 subtraction to 2021 list and documented program enactment dates

Files Changed

New Variables (6 files):

  • mn_k12_education_credit.py - K-12 education credit calculation
  • mn_k12_qualifying_children.py - Count of qualifying K-12 children
  • mn_k12_education_subtraction.py - K-12 education subtraction calculation
  • mn_529_contribution_subtraction.py - 529 plan contribution subtraction
  • mn_military_pension_subtraction.py - Military pension subtraction
  • mn_active_duty_military_pay_subtraction.py - Active duty pay subtraction

New Parameters (9 files):

  • K-12 education credit: income limits, phaseout rates, expense rate, amount per child
  • K-12 education subtraction: max amounts for K-6 and 7-12
  • 529 subtraction: max amounts by filing status

New Tests (5 files):

  • 23 new test cases covering all new programs

Updated Parameters (78 files):

  • 2025 values for all existing MN income tax parameters

Test Plan

  • All 136 Minnesota tests pass
  • Code formatted with make format
  • New programs have comprehensive test coverage

References

Fixes #6920

🤖 Generated with Claude Code

…rams

New Programs Added:
- K-12 Education Credit: Refundable credit for qualifying K-12 education expenses
  - 75% of qualifying expenses up to $1,500 per child
  - Income-based phaseout starting at $75,820 (2025)
  - Parameters for income limits, phaseout rates, expense rates

- K-12 Education Subtraction: Subtraction for K-12 education expenses
  - $1,625 max per child grades K-6
  - $2,500 max per child grades 7-12
  - No income limit (available since 1997)

- 529 Contribution Subtraction: Subtraction for 529 plan contributions
  - $1,500 single / $3,000 joint maximum
  - Available since 2017 (corrected from 2023)
  - Updated statute reference to Subd. 23

- Military Pension Subtraction: Full subtraction of military retirement pay
  - 100% subtraction with no income limits
  - Available since 2016

- Active Duty Military Pay Subtraction: Full subtraction of active duty pay
  - 100% subtraction with no income limits
  - Available since 2005

Parameter Updates for 2025:
- Tax brackets for all filing statuses
- Standard and itemized deduction amounts and thresholds
- Personal exemption amounts and AGI thresholds
- AMT rates and income thresholds
- Child and Working Families Credit amounts
- CDCC credit parameters
- Marriage credit parameters
- Social Security subtraction thresholds
- Public pension subtraction caps
- Charity subtraction thresholds

Historical Coverage:
- Updated sources.yaml to include 529 subtraction starting 2021
- Added documentation comments for program enactment dates

Fixes PolicyEngine#6920

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (fce9201) to head (44ad56e).
⚠️ Report is 25 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #6923   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           13         7    -6     
  Lines          195       100   -95     
=========================================
- Hits           195       100   -95     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

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.

Copy link
Collaborator

@PavelMakarchuk PavelMakarchuk left a comment

Choose a reason for hiding this comment

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

Some initial suggestions

@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 10, 2025

@PolicyEngine, please integrate the comments from @PavelMakarchuk. Make sure all parameter descriptions are in active voice and remove "values:" when appropriate. The 2025 values in social_security\income_amount.yaml and social_security\alternative_amount.yaml need to be hardcoded to prevent unwanted uprating

@policyengine
Copy link

policyengine bot commented Dec 10, 2025

Sorry @DTrim99, only members of the PolicyEngine/core-developers team can invoke Claude Code.

Changes based on @PavelMakarchuk review:

1. Military subtraction variables - use 'adds' syntax:
   - mn_military_pension_subtraction: adds = ["military_retirement_pay", "military_retirement_pay_survivors"]
   - mn_active_duty_military_pay_subtraction: adds = ["military_service_income"]

2. K-12 education subtraction parameters - reorganized:
   - Renamed max_k6.yaml -> cap/lower.yaml
   - Renamed max_7_12.yaml -> cap/higher.yaml
   - Updated variable to use p.cap.higher

3. K-12 education credit - split into separate files:
   - Created mn_k12_education_credit_eligible.py for eligibility logic
   - Simplified mn_k12_education_credit.py to use defined_for eligibility

4. Parameter descriptions - converted to active voice:
   - "Minnesota K-12 Education Credit provides..." -> "Minnesota provides this maximum K-12 Education Credit..."
   - "Minnesota K-12 Education Credit phases out..." -> "Minnesota phases out the K-12 Education Credit..."
   - All k12_education credit parameters updated

5. 529 subtraction parameter - removed 'values:' nesting:
   - Changed from "JOINT:\n  values:\n    2017-01-01: 3_000" to "JOINT:\n  2017-01-01: 3_000"
   - Updated description to active voice

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 10, 2025

Addressed Review Comments

Thanks for the feedback @PavelMakarchuk! I've made the following changes:

1. Military subtraction variables - converted to adds syntax

# mn_military_pension_subtraction.py
adds = ["military_retirement_pay", "military_retirement_pay_survivors"]

# mn_active_duty_military_pay_subtraction.py  
adds = ["military_service_income"]

2. K-12 education subtraction parameters - reorganized

  • Renamed max_k6.yamlcap/lower.yaml
  • Renamed max_7_12.yamlcap/higher.yaml
  • Updated variable to reference p.cap.higher

3. K-12 education credit - split into separate files

  • Created mn_k12_education_credit_eligible.py for eligibility logic
  • Simplified mn_k12_education_credit.py to use defined_for = "mn_k12_education_credit_eligible"

4. Parameter descriptions - converted to active voice

Examples:

  • "Minnesota K-12 Education Credit provides..." → "Minnesota provides this maximum K-12 Education Credit..."
  • "Minnesota K-12 Education Credit phases out..." → "Minnesota phases out the K-12 Education Credit..."

5. 529 subtraction parameter - removed values: nesting

# Before
JOINT:
  values:
    2017-01-01: 3_000

# After  
JOINT:
  2017-01-01: 3_000

Note on Social Security parameters

I kept the 2024 and 2025 values in social_security/income_amount.yaml and social_security/alternative_amount.yaml hardcoded as they need to prevent unwanted uprating.

All 136 Minnesota tests pass ✓

- Parametrize the hardcoded `2` as `income_limit/child_threshold.yaml`
- Rename `max_subtraction.yaml` to `cap.yaml` for 529 subtraction
- Move income limit params into `income_limit/` folder (base.yaml, additional.yaml)
- Update description to reference "above a certain number" instead of "beyond 2"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 10, 2025

Addressed all review comments:

  1. Parametrized the 2 - Created income_limit/child_threshold.yaml with value 2 as a policy parameter
  2. Renamed max_subtraction.yaml to cap.yaml - Updated variable to use p.cap[filing_status]
  3. Updated description - Changed to "above a certain number" instead of "beyond 2"
  4. Created income_limit/ folder - Moved income_limit_base.yamlincome_limit/base.yaml
  5. Renamed increment file - income_limit_increment.yamlincome_limit/additional.yaml

All 136 Minnesota income tax tests pass.

DTrim99 and others added 5 commits December 11, 2025 16:39
Updates the Minnesota Statutes reference from general "290.0674" to
specific "290.0674, Subd. 2(a)" with anchor link for income limit
parameters (base, additional, and child_threshold).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@PavelMakarchuk
Copy link
Collaborator

@DTrim99 please fix tests but structure looks good

@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 16, 2025

@DTrim99 please fix tests but structure looks good

They aren't failing, but timing out.

Copy link
Collaborator

@PavelMakarchuk PavelMakarchuk left a comment

Choose a reason for hiding this comment

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

PR Review: Minnesota 2025 Income Tax Updates

I've reviewed this PR focusing on parameter accuracy, legal references, variable logic, and test coverage.

🔴 Critical Issues

1. Incorrect Statute References for Military Subtractions

The military subtraction variables cite incorrect subdivision numbers:

mn_military_pension_subtraction.py (line 11):

mn_active_duty_military_pay_subtraction.py (line 11):


🟡 Medium Issues

2. Missing Unit Tests for mn_k12_qualifying_children

The mn_k12_qualifying_children variable has no direct unit tests. While it's tested indirectly through the credit tests (using the variable as input), adding unit tests would:

  • Verify the is_in_k12_school and is_tax_unit_dependent logic
  • Test edge cases with mixed K-12 and non-K-12 children
  • Ensure proper entity aggregation with tax_unit.sum()

Suggested test location: policyengine_us/tests/policy/baseline/gov/states/mn/tax/income/credits/k12_education/mn_k12_qualifying_children.yaml


🟢 Verified Correct

Parameter Accuracy ✓

Parameter Expected Value PR Value Source
K-12 Credit phase-out rate (1 child) 25% ($1 per $4) 0.25 290.0674
K-12 Credit phase-out rate (2+ children) 50% ($2 per $4) 0.50 290.0674
K-12 Credit max per child $1,500 $1,500 290.0674
529 Subtraction max (single) $1,500 $1,500 290.0132 Subd. 23
529 Subtraction max (joint) $3,000 $3,000 290.0132 Subd. 23
529 Subtraction enacted 2017 2017-01-01 2017 1st Special Session

Entity Relationships ✓

  • Military income variables (military_retirement_pay, military_retirement_pay_survivors, military_service_income) are correctly defined at Person level
  • Subtraction variables use adds attribute to properly aggregate to TaxUnit level
  • K-12 qualifying children uses tax_unit.sum() correctly for person-level aggregation

Variable Logic ✓

  • mn_k12_education_credit_eligible correctly excludes SEPARATE filing status
  • Income limit calculation properly accounts for additional children beyond threshold
  • Phase-out logic uses different rates for single vs multiple children

Test Coverage ✓

All new programs have test files with reasonable coverage:

  • mn_k12_education_credit.yaml - 6 test cases
  • mn_k12_education_subtraction.yaml - 4 test cases
  • mn_529_contribution_subtraction.yaml - 6 test cases
  • mn_military_pension_subtraction.yaml - 4 test cases
  • mn_active_duty_military_pay_subtraction.yaml - 3 test cases

📝 Documentation Note

The mn_k12_education_subtraction.py appropriately documents the limitation that it uses the 7-12 maximum ($2,500) for all children since grade-level data isn't available. This is an acceptable approximation but slightly overestimates benefits for K-6 students.


Summary

Required changes before merge:

  1. ❌ Fix the military pension subtraction statute reference (Subd. 27 → Subd. 21)
  2. ❌ Fix the active duty military pay subtraction statute reference (Subd. 20 → Subd. 12)

Recommended improvements:
3. ⚠️ Add unit tests for mn_k12_qualifying_children variable

@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 16, 2025

@PolicyEngine fix the statute misalignment and add unit tests for mn_k12_qualifying_children variable

@policyengine
Copy link

policyengine bot commented Dec 16, 2025

I ran into an issue:

Failed to clone repository: Cloning into '/tmp/policyengine-bot-zm45uomt/policyengine-us'...
warning: Could not find remote branch DTrim99/issue6920 to clone.
fatal: Remote branch DTrim99/issue6920 not found in upstream origin

- Correct mn_active_duty_military_pay_subtraction reference to Subd. 12
- Correct mn_military_pension_subtraction reference to Subd. 21
- Add mn_k12_qualifying_children tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@DTrim99
Copy link
Collaborator Author

DTrim99 commented Dec 16, 2025

All 141 Minnesota tests pass ✓

Committed fixes:

  • Corrected statute references for military subtractions (Subd. 12 and Subd. 21)
  • Added mn_k12_qualifying_children tests

@PavelMakarchuk
Copy link
Collaborator

PR Review - Minnesota 2025 Income Tax Updates

✅ CI Status: All Checks Passing


🟢 Approval Recommended

This is a well-structured PR that adds Minnesota 2025 tax updates and 5 new programs. The implementation follows PolicyEngine standards and all tests pass.


Validation Summary

Check Result Notes
CI Status ✅ Passing All 10 checks pass
Implementation Patterns ✅ Good No hard-coded values, proper parameter access, correct vectorization
Reference Quality ✅ Good 100% of parameters have proper title+href references
Test Coverage ✅ Good All 5 new programs have test files (23 new tests)
Naming Conventions ✅ Good All variables use mn_ prefix correctly

🟡 Suggestions (Optional Improvements)

1. Add Direct Unit Tests for Helper Variables

The following variables are only tested indirectly:

  • mn_k12_qualifying_children - tested via credit/subtraction tests
  • mn_k12_education_credit_eligible - tested via credit tests

Recommendation: Consider adding dedicated test files for these helper variables for more explicit coverage.

2. Minor Comment Clarification

File: policyengine_us/variables/gov/states/mn/tax/income/subtractions/mn_k12_education_subtraction.py

The comment mentions using an "average limit as approximation" but the code actually uses p.cap.higher ($2,500). Consider updating the comment to:

# Using 7-12 cap as upper bound since we cannot distinguish grades
# (K-6 cap is $1,625, 7-12 cap is $2,500)

3. Edge Case Tests to Consider

For future enhancement, consider adding boundary tests at:

  • Exact income thresholds for K-12 credit phaseout
  • Zero qualifying children scenarios
  • Maximum expense limits

New Programs Validated

Program Implementation Parameters Tests Status
K-12 Education Credit Ready
K-12 Education Subtraction Ready
529 Contribution Subtraction Ready
Military Pension Subtraction Ready
Active Duty Military Pay Subtraction Ready

Key Implementation Highlights

Good Patterns Used:

  • adds attribute for simple sum variables (military subtractions)
  • where, max_, min_ for vectorized calculations
  • ✅ Proper p = parameters(period).gov.states.mn... pattern
  • ✅ All numeric values come from parameters
  • ✅ Minnesota statute and form references included

References Quality:

  • All new parameters reference Minnesota Statutes (revisor.mn.gov)
  • 2025 form references added (Schedule M1ED, M1M, etc.)
  • Page anchors included in PDF references (e.g., #page=3)

Verdict

APPROVE - This PR is ready for merge. All critical requirements are met, CI passes, and the implementation follows PolicyEngine standards. The suggestions above are optional enhancements.


🤖 Automated review by Claude Code

@PavelMakarchuk
Copy link
Collaborator

@DTrim99 please add last couple unit tests for intermediate variabels and merge

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.

2025 Minnesota 2025 Income Tax Changes

2 participants