Skip to content

Conversation

@rickecon
Copy link
Contributor

@rickecon rickecon commented Dec 3, 2025

This PR:

  • Updates the thresholds and marginal tax rates rates for the Missouri individual income tax in parameters/gov/states/mo/tax/income/rates.yaml
  • Added the variable mo_capital_gains_subtraction.py and the parameter parameters/gov/states/mo/tax/income/deductions/net_capital_gain/rate.yaml for the 2025 inclusion a full deductibility of capital gains in calculating Missouri adjusted gross income. I added this variable to the mo_adjusted_gross_income.py calculation.
  • I updated the parameters/gov/states/mo/tax/income/minimum_taxable_income.yaml parameter with the new 2025 value.

cc: @MaxGhenis

@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (39051b8) to head (d4f5f86).
⚠️ Report is 20 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #6898   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            2         4    +2     
  Lines           39        68   +29     
=========================================
+ Hits            39        68   +29     
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.

@rickecon
Copy link
Contributor Author

rickecon commented Dec 3, 2025

I ran make format and make test locally. The make test tests all passed.

(policyengine-us-dev) richardevans@Richards-MacBook-Pro-5 policyengine-us % make test
pytest policyengine_us/tests/ --maxfail=0
=============================================================== test session starts ===============================================================
platform darwin -- Python 3.11.14, pytest-8.4.2, pluggy-1.6.0
rootdir: /Users/richardevans/Docs/Economics/OSE/microsim/policyengine-us
configfile: pyproject.toml
plugins: anyio-4.12.0
collected 11 items                                                                                                                                

policyengine_us/tests/microsimulation/test_microsim.py ....                                                                                 [ 36%]
policyengine_us/tests/policy/baseline/parameters/test_uprating_extensions.py ...                                                            [ 63%]
policyengine_us/tests/utilities/test_load_county_fips_dataset.py ....                                                                       [100%]
============= 11 passed, 235 warnings in 105.22s (0:01:45) =============
...
============= 9037 passed in 2982.88s (0:49:42) =============

Although all the Missouri tests passed, I need notice the following two issues generating warnings about filing status "MARRIED". Do those need to be changed to "JOINT"?

policyengine_us/tests/policy/baseline/gov/states/mo/dss/tanf/mo_tanf_income_limit.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/adjusted_gross_income/mo_adjusted_gross_income.yaml .....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_property_tax_credit.yaml .....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_gross_income.yaml .
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_income_offset.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_net_income.yaml .
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/property_tax/mo_ptc_taxunit_eligible.yaml .....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/credits/wftc/mo_wftc.yaml ....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_business_income_deduction.yaml ....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_federal_income_tax_deduction.yaml ......WARNING:policyengine_core.enums.enum:Invalid values for enum FilingStatus: ['MARRIED']. These will be encoded as index 0.
.WARNING:policyengine_core.enums.enum:Invalid values for enum FilingStatus: ['MARRIED']. These will be encoded as index 0.
..
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_itemized_deductions.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/integration_tests/mo_pension_and_ss_or_ssd.yaml ............
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction.yaml .....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_a.yaml WARNING:policyengine_core.enums.enum:Invalid values for enum FilingStatus: ['None']. These will be encoded as index 0.
.........WARNING:policyengine_core.enums.enum:Invalid values for enum FilingStatus: ['None']. These will be encoded as index 0.
...........
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_b.yaml .........
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/deductions/mo_pension_and_ss_or_ssd_deduction/mo_pension_and_ss_or_ssd_deduction_section_c.yaml ..............
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/income_tax/integration.yaml ....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/income_tax/mo_income_tax_before_credits.yaml ..........
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/income_tax/mo_income_tax_before_refundable_credits.yaml ..
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/income_tax/mo_income_tax_exempt.yaml ....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/mo_withheld_income_tax.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/subtractions/mo_qualified_health_insurance_premiums.yaml ......
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/taxable_income/mo_net_state_income_taxes.yaml ....
policyengine_us/tests/policy/baseline/gov/states/mo/tax/income/taxable_income/mo_taxable_income.yaml ..
policyengine_us/tests/policy/baseline/gov/states/mo/tax/tax_sim/mo_adjusted_gross_income.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/tax_sim/mo_income_tax.yaml .......
policyengine_us/tests/policy/baseline/gov/states/mo/tax/tax_sim/mo_itemized_deductions.yaml ..
policyengine_us/tests/policy/baseline/gov/states/mo/tax/tax_sim/mo_taxable_income.yaml ...
policyengine_us/tests/policy/baseline/gov/states/mo/tax/tax_sim/scenarios/mo_income_tax_by_family_structure.yaml .....

@MaxGhenis MaxGhenis requested a review from DTrim99 December 3, 2025 19:54
Copy link
Collaborator

@DTrim99 DTrim99 left a comment

Choose a reason for hiding this comment

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

Please add a few tests for the Capital Gains subtraction. I will fix the filing status issues in a separate PR. Thank you!

@DTrim99
Copy link
Collaborator

DTrim99 commented Dec 3, 2025

Filing status fix here: #6901

Comment on lines 19 to 20
capped_stcg = min_(0, stcg)
net_cg = max_(0, ltcg + capped_stcg)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
capped_stcg = min_(0, stcg)
net_cg = max_(0, ltcg + capped_stcg)
net_cg = max_(0, ltcg + stcg)

Copy link
Collaborator

Choose a reason for hiding this comment

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

This canceled out any positive short-term capital gains, thus making the test fail.

Copy link
Contributor Author

@rickecon rickecon Dec 9, 2025

Choose a reason for hiding this comment

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

@DTrim99. I am trying to verify what is the correct policy in the law. The original code in the PolicyEngine-US MO variables was the ltcg + capped_stcg. I will verify what the previous years' law says (e.g., 2024). I have currently updated the test so that it calculates the capital gains as ltcg + capped_stcg, which currently passes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DTrim99. I have verified that the Missouri policy is to take the federal definition of capital gains. The original tax logic shown above is correct. But the cleaner, more direct approach is what I have now done in the mo_capital_gains_subtraction.py file using net_capital_gain = max_(0, tax_unit("net_capital_gain", period)) instead of the logic above. But the answer is the same.

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.

Thank you for the PR, some suggestions here

Comment on lines 3 to 4
0000-01-01: 0.00
2025-01-01: 1.00
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
0000-01-01: 0.00
2025-01-01: 1.00
2025-01-01: 1

Won't need this if we have the list parameter with an empty list entry pre 2025 ([])

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PavelMakarchuk. I am happy to change this from "1.00" to "1". Can you tell me what is the material reason that you prefer "1" over "1.00"? My intuition is that two decimal points is a nice baseline convention for rates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PavelMakarchuk. Done. And I moved this net_capital_gain parameter folder to the subtractions folder rather than the deductions folder.

@rickecon
Copy link
Contributor Author

rickecon commented Dec 9, 2025

@PavelMakarchuk @DTrim99. I have responded to all of your comments. This is now passing all tests. Ready for review. Some of the major updates are:

  • Added a subtractions list parameter structure to capture the two subtractions to Missouri AGI.
  • Changed the mo_capital_gains_subtraction variable to TaxUnit entity rather than Person
  • Moved the net_capital_gain parameter folder to be under the subtractions folder rather than the deductions folder
  • Updated the tests
  • Added better legislative references

rickecon and others added 3 commits December 10, 2025 16:33
- Create mo_capital_gains_subtraction_person to allocate the tax unit
  capital gains subtraction proportionally by each person's share of
  long-term capital gains
- Change mo_agi_subtractions from TaxUnit to Person entity level
- Update agi_subtractions.yaml to reference person-level variable
- Update mo_adjusted_gross_income.py to use person-level subtractions
- Add multi-person tax unit tests to verify correct allocation

This prevents overcounting where each person in a tax unit would
previously receive the full tax unit subtraction (2x for married couples).

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

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

DTrim99 commented Dec 11, 2025

Fix: Entity-level mismatch in MO capital gains subtraction

I reviewed the PR and found an entity-level mismatch that would cause overcounting of the capital gains subtraction in multi-person tax units.

The Problem

  • mo_adjusted_gross_income is a Person-level variable
  • mo_agi_subtractions was a TaxUnit-level variable
  • mo_capital_gains_subtraction was a TaxUnit-level variable

This meant in mo_adjusted_gross_income.py:

subtractions = tax_unit("mo_agi_subtractions", period)  # TaxUnit-level
return max_(0, fed_agi - subtractions)  # Applied to each Person!

For a married couple with $10,000 in long-term capital gains, each spouse would subtract $10,000 from their personal MO AGI, resulting in a $20,000 total subtraction instead of $10,000.

The Fix

  1. Created mo_capital_gains_subtraction_person.py - allocates the tax unit capital gains subtraction proportionally based on each person's share of long-term capital gains
  2. Changed mo_agi_subtractions.py from TaxUnit to Person entity
  3. Updated agi_subtractions.yaml to reference mo_capital_gains_subtraction_person instead of mo_capital_gains_subtraction
  4. Updated mo_adjusted_gross_income.py to use person("mo_agi_subtractions", period)
  5. Added multi-person tax unit tests to verify correct allocation

Test Results

✅ All 167 Missouri tests pass

DTrim99 and others added 2 commits December 11, 2025 14:06
🤖 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>
@DTrim99
Copy link
Collaborator

DTrim99 commented Dec 11, 2025

Sorry @rickecon for asking you to switch the entity from person to tax_unit. @PavelMakarchuk and I decided that we should keep everything at the person level after a discussion.

@rickecon
Copy link
Contributor Author

@DTrim99. I can update the uv.lock as I have done in the past. But are there any substantial changes that you still want me to make in this? You already added the commits you wanted.

DTrim99 and others added 3 commits December 15, 2025 10:25
🤖 Generated with [Claude Code](https://claude.com/claude-code)

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

DTrim99 commented Dec 15, 2025

@DTrim99. I can update the uv.lock as I have done in the past. But are there any substantial changes that you still want me to make in this? You already added the commits you wanted.

I think we should be good now. I've requested @PavelMakarchuk's review. Thank you for your help with these updates!

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.

3 participants