Skip to content

Comments

Maryland County Income Tax Rates#6439

Open
DTrim99 wants to merge 19 commits intoPolicyEngine:mainfrom
DTrim99:DTrim99/issue6436
Open

Maryland County Income Tax Rates#6439
DTrim99 wants to merge 19 commits intoPolicyEngine:mainfrom
DTrim99:DTrim99/issue6436

Conversation

@DTrim99
Copy link
Collaborator

@DTrim99 DTrim99 commented Aug 25, 2025

Fixes #6436

@DTrim99 DTrim99 marked this pull request as draft August 25, 2025 15:12
@DTrim99 DTrim99 requested a review from PavelMakarchuk August 25, 2025 21:15
@DTrim99 DTrim99 removed the request for review from PavelMakarchuk August 26, 2025 14:33
@DTrim99 DTrim99 requested a review from PavelMakarchuk August 26, 2025 18:31
Copy link
Contributor

@MaxGhenis MaxGhenis left a comment

Choose a reason for hiding this comment

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

does the state or each county set these rates?

@DTrim99
Copy link
Collaborator Author

DTrim99 commented Aug 26, 2025

does the state or each county set these rates?

The state of Maryland mandates that county income tax rates must be within 2.25% and 3.3% (just raised from 3.2% this tax year), but counties control the rate and structure between that range.

Statute: https://mgaleg.maryland.gov/mgawebsite/Laws/StatuteText?article=gtg&section=10-106&enactments=false

@codecov
Copy link

codecov bot commented Nov 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@98d67ac). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff            @@
##             main     #6439   +/-   ##
========================================
  Coverage        ?   100.00%           
========================================
  Files           ?         5           
  Lines           ?       103           
  Branches        ?         0           
========================================
  Hits            ?       103           
  Misses          ?         0           
  Partials        ?         0           
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.

structural suggestions

@DTrim99 DTrim99 marked this pull request as ready for review December 1, 2025 15:34
@DTrim99
Copy link
Collaborator Author

DTrim99 commented Feb 11, 2026

PR Review: Maryland County Income Tax Rates

🔴 Critical (Must Fix)

  1. PDF page anchor format error - All parameter files use #page-23 (hyphen) instead of #page=23 (equals sign). This is an invalid anchor format.

    • Affects: All 11 parameter files in gov/local/md/
    • Fix: Replace #page-23 with #page=23
  2. Missing reference field in variable files - All 4 new variable files lack the required reference attribute:

    • md_anne_arundel_county_tax.py
    • md_frederick_county_tax.py
    • md_flat_rate_county_tax.py
    • md_local_income_tax_before_credits.py
  3. Inconsistent PDF filename - Frederick County and flat_rate files use Resident-Booklet.pdf (hyphen) while Anne Arundel files use Resident_Booklet.pdf (underscore). Use consistent naming.

🟡 Should Address

  1. Consider using adds attribute - md_local_income_tax_before_credits.py performs a pure sum and the where(in_md, ...) check is redundant since each component variable already has defined_for = StateCode.MD. Could be simplified to:

    adds = ["md_anne_arundel_county_tax", "md_frederick_county_tax", "md_flat_rate_county_tax"]
  2. PDF URLs may be broken - The referenced PDFs at marylandcomptroller.gov may return 404 errors. Verify the URLs are accessible or update to working URLs.

  3. Reference titles lack section detail - Consider adding specific section information:

    • Current: 2024 Maryland Income Tax Instructions
    • Better: 2024 Maryland Income Tax Instructions - Local Tax Rates Table, page 25

🟢 Suggestions

  1. Add 2025 references - Since the PR includes 2025 rate values, consider adding the 2025 Resident Booklet reference when available.

  2. Description style - Descriptions like "Maryland sets the following county income tax rates..." could be simplified to "Anne Arundel County income tax rates for single filers."


Validation Summary

Check Result
CI Status ✅ All checks passing
Regulatory Accuracy ⚠️ Unable to fully verify (PDF URLs inaccessible)
Reference Quality ❌ 3 critical format issues
Code Patterns ⚠️ Missing variable references, could use adds
Test Coverage ✅ 108 tests covering all counties

Positive Observations

  • ✅ Proper vectorization with where() and select()
  • ✅ No hard-coded numeric values - all rates parameterized
  • ✅ Good test coverage across multiple years and filing statuses
  • ✅ Clean separation of progressive vs flat rate calculations
  • ✅ All rates within statutory 2.25%-3.30% range

Next Steps

To auto-fix issues: /fix-pr 6439

Or address manually:

  1. Fix #page-23#page=23 in all parameter files
  2. Add reference field to all 4 variable files
  3. Standardize PDF filename (underscore vs hyphen)
  4. Verify PDF URLs are accessible

🤖 Generated with Claude Code

- Add 2025 references with page numbers to all 11 MD county tax parameters
- Fix PDF anchor format (#page-23 -> #page=23) for correct linking
- Standardize PDF filename (Resident-Booklet.pdf -> Resident_Booklet.pdf)
- Add reference field to all 4 MD county tax variable files
- Simplify md_local_income_tax_before_credits.py to use adds pattern

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

DTrim99 commented Feb 11, 2026

Fixes Applied ✅

🔴 Critical Issues Fixed

  1. ✅ PDF page anchor format - Changed #page-23 to #page=23 in all 11 parameter files
  2. ✅ Missing reference field - Added reference field to all 4 variable files:
    • md_anne_arundel_county_tax.py
    • md_frederick_county_tax.py
    • md_flat_rate_county_tax.py
    • md_local_income_tax_before_credits.py
  3. ✅ Inconsistent PDF filename - Standardized to Resident_Booklet.pdf (underscore)

🟡 Should-Address Issues Fixed

  1. ✅ Added 2025 references - Added 2025 Resident Booklet reference with page 25 to all parameter files as first reference
  2. ✅ Simplified md_local_income_tax_before_credits.py - Converted from formula to adds pattern:
    adds = [
        "md_anne_arundel_county_tax",
        "md_frederick_county_tax", 
        "md_flat_rate_county_tax",
    ]
  3. ✅ Reference titles updated - Added " - Local Tax Rate Chart" section detail to PDF reference titles

Verification

  • ✅ All 108 county tax tests pass
  • ✅ Code formatted with make format

Files Modified (15 total)

Parameters (11): All files in gov/local/md/anne_arundel_county/, gov/local/md/frederick_county/, and gov/local/md/flat_rate.yaml

Variables (4): All files in gov/states/md/tax/income/local/


🤖 Generated with Claude Code

@DTrim99 DTrim99 requested review from hua7450 and removed request for PavelMakarchuk February 11, 2026 18:35
@hua7450
Copy link
Collaborator

hua7450 commented Feb 18, 2026

Audit Findings

Verified PR parameter values against the NFC withholding bulletins and 2025 Maryland Resident Tax Booklet. 30+ rate/year/county combinations confirmed correct. Three issues found:

1. Frederick County 2023 single/separate threshold

single.yaml:28 and separate.yaml:28 both use 100_000 for the 2023 bracket 2 threshold. Per the NFC 2023 bulletin, single filers use $50,000 and married filers use $100,000. Single and married-filing-separately should be 50_000.

Impact: Single/separate filers earning $50K–$100K would be undertaxed for 2023.

2. Frederick County rate type — flat-rate-by-bracket, not marginal

All 5 Frederick County YAML files use type: marginal_rate. However, the NFC 2024 and 2025 bulletins explicitly label Frederick as a "Fixed Rate Tax Table" — entire income is multiplied by the single rate matching your bracket. Anne Arundel is labeled "Graduated Tax Table" with cumulative base amounts (true marginal rates).

Example (2025, single, $40K income):

  • Fixed rate (correct): $40,000 × 2.75% = $1,100
  • Marginal (current code): $25,000 × 2.25% + $15,000 × 2.75% = $975
  • Difference: $125 undertaxed

3. Unguarded p[county] lookup in md_flat_rate_county_tax.py

Line 18 does flat_rate = p[county] where p is parameters(period).gov.local.md.flat_rate. In a mixed-state microsimulation, defined_for = StateCode.MD does not prevent formula execution — it only masks the result afterward. The vectorized parameter lookup raises ParameterNotFoundError when it encounters non-MD county strings (e.g., LOS_ANGELES_COUNTY_CA) that aren't keys in flat_rate.yaml.

Fix: guard the lookup so only MD counties reach the parameter, e.g.:

in_md = tax_unit.household("state_code_str", period) == "MD"
safe_county = where(in_md, county, "ALLEGANY_COUNTY_MD")
flat_rate = p[safe_county]

Fixes audit findings in PR PolicyEngine#6439:

1. Frederick County 2023 single/separate threshold: Changed from $100,000
   to $50,000 for single and separate filers per NFC bulletins

2. Frederick County rate type: Changed from marginal_rate to single_amount
   (fixed-rate-by-bracket system) - entire income is multiplied by the
   bracket rate, not marginal taxation

3. Unguarded parameter lookup: Added safe_county fallback in
   md_flat_rate_county_tax.py to prevent ParameterNotFoundError in
   mixed-state microsimulations

4. Updated test expectations: Tests now reflect fixed-rate-by-bracket
   calculations (e.g., $35,000 * 2.75% = $962.50, not marginal sum)

Reference: NFC withholding bulletins confirm Frederick County uses
"Fixed Rate Tax Table" while Anne Arundel uses "Graduated Tax Table"

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

DTrim99 commented Feb 18, 2026

Audit Fixes Applied ✅

Fixed all 3 critical issues identified by @hua7450:

1. Frederick County 2023 single/separate threshold ✅

Changed from 100_000 to 50_000 in single.yaml and separate.yaml per NFC 2023 bulletin.

2. Frederick County rate type: fixed-rate-by-bracket ✅

Changed parameter type from marginal_rate to single_amount in all 5 Frederick County YAML files. Updated variable to use .calc(taxable_income, right=True) to get the bracket rate, then multiply entire income by that rate.

Before (marginal, incorrect):

  • $40,000: $25,000 × 2.25% + $15,000 × 2.75% = $975

After (fixed-rate-by-bracket, correct):

  • $40,000: $40,000 × 2.75% = $1,100

3. Unguarded parameter lookup in md_flat_rate_county_tax.py ✅

Added safe county fallback:

in_md = tax_unit.household("state_code_str", period) == "MD"
safe_county = where(in_md, county, "ALLEGANY_COUNTY_MD")
flat_rate = p[safe_county]

Test Updates

Updated all 21 Frederick County tests to reflect fixed-rate-by-bracket calculations. All 111 MD county tax tests pass.


🤖 Generated with Claude Code

Add pre-progressive rate tests (2022) for both progressive counties
to verify flat rate behavior before the progressive structure started
in 2023.

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

DTrim99 commented Feb 19, 2026

Addressed the recent feedback:

Pre-2024 tests for progressive rates:
Added 2022 tests for both Frederick County and Anne Arundel County to verify flat rate behavior before the progressive structure started in 2023. This ensures we have test coverage for:

  • 2021: flat rate (existing tests)
  • 2022: flat rate (new tests added)
  • 2023+: progressive rates (existing tests)

Breakdown tag for flat_rate.yaml:
I attempted to add a breakdown: - county_str tag, but it caused a parameter validation error (UnboundLocalError: cannot access local variable 'possible_values'). This is a known policyengine-core limitation documented in CLAUDE.md. The parameter works correctly without the breakdown tag since it's accessed via p[county] in the code.

@PavelMakarchuk
Copy link
Collaborator

PR Review

✅ No Critical Issues

CI passes and the core implementation is correct. The .inf threshold approach for disabling brackets in pre-progressive years works correctly — verified locally with high-income scenarios ($500K) in flat-rate years (2021) and intermediate years (2023).

🟡 Should Address

  1. Missing filing status tests for progressive counties: Anne Arundel and Frederick tests only cover SINGLE and JOINT. The variables use select() to map all 5 filing statuses to rate schedules, but SEPARATE, HEAD_OF_HOUSEHOLD, and SURVIVING_SPOUSE are untested. If a select() mapping is wrong for these statuses, it wouldn't be caught.

  2. Missing boundary tests at bracket thresholds: Anne Arundel 2024 single has brackets at $50K and $400K, but there's no test at exactly $50K or $400K to verify the marginal rate transitions correctly at the boundary. Same gap for Frederick bracket boundaries.

  3. Cecil County missing 2024 period test: The PR adds 2024 rates but Cecil County only has a 2025 test.

  4. No high-income test in flat-rate years: The 2021/2022 tests only go up to $100K. A $500K test in 2021 would confirm that .inf brackets with undefined rates don't interfere. (Verified locally that this works, but having it in the test suite would prevent regressions.)

🟢 Suggestions

  1. The flat rate parameter (md_county_income_tax_rate.yaml) includes values from 2021 but only references 2024 and 2025 Comptroller booklets — consider adding a 2021 source reference.

  2. Consider adding a comment in md_flat_rate_county_tax.py explaining why the ~progressive guard exists, for maintainability if more counties go progressive later.


Validation Summary

Check Result
Regulatory Accuracy ✅ Correct — Anne Arundel marginal rates and Frederick fixed-rate-by-bracket verified against 2025 MD Comptroller booklet
Reference Quality ✅ Good — official MD Comptroller booklets cited with page numbers
Code Patterns ✅ Clean — proper use of select(), calc(), calc(right=True), microsimulation guard
Test Coverage 🟡 4 gaps — missing filing statuses, boundary tests, Cecil 2024, high-income flat-rate year
CI Status ✅ All passing

Next Steps

To auto-fix issues: /fix-pr 6439

Or address manually and re-request review.

Addresses PR review feedback for missing test coverage:

1. Added filing status tests for SEPARATE, HEAD_OF_HOUSEHOLD, and
   SURVIVING_SPOUSE for both Anne Arundel and Frederick counties

2. Added boundary tests at exact bracket thresholds:
   - Anne Arundel: $50K, $75K, $400K boundaries
   - Frederick: $25K, $50K, $100K, $150K boundaries

3. Added Cecil County 2024 period tests (rate: 2.75%)

4. Added high-income $500K tests in flat-rate years (2021) to verify
   that .inf brackets don't interfere with calculations

Total: 150 MD county tax tests now pass

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

DTrim99 commented Feb 19, 2026

Fixes Applied ✅

🟡 Should-Address Issues Fixed

  1. ✅ Missing filing status tests: Added SEPARATE, HEAD_OF_HOUSEHOLD, and SURVIVING_SPOUSE tests for both Anne Arundel and Frederick counties (2024)

  2. ✅ Missing boundary tests at bracket thresholds:

    • Anne Arundel: Tests at exactly $50K, $75K, and $400K boundaries (plus $1 above each)
    • Frederick: Tests at exactly $25K, $50K, $100K, and $150K boundaries (plus $1 below each)
    • Note: With right=True in .calc(), values at the boundary fall into the lower bracket
  3. ✅ Cecil County missing 2024 period test: Added 2024 tests at $50K, $100K, and $500K income levels (rate: 2.75%)

  4. ✅ No high-income test in flat-rate years: Added $500K income tests for 2021 to verify flat rate works correctly at high incomes

Verification

  • ✅ All 150 MD county tax tests pass
  • ✅ Code formatted

Test Count Summary

File Tests Before Tests After
Anne Arundel 26 41
Frederick 25 41
Cecil 6 10
Other counties 58 58
Total 115 150

Ready for re-review.


🤖 Generated with Claude Code

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.

Maryland County Income Tax Rates

4 participants