Skip to content

Implement Utah Unemployment Insurance (ut_ui)#8281

Draft
daphnehanse11 wants to merge 8 commits into
PolicyEngine:mainfrom
daphnehanse11:ut-ui
Draft

Implement Utah Unemployment Insurance (ut_ui)#8281
daphnehanse11 wants to merge 8 commits into
PolicyEngine:mainfrom
daphnehanse11:ut-ui

Conversation

@daphnehanse11
Copy link
Copy Markdown
Collaborator

@daphnehanse11 daphnehanse11 commented May 13, 2026

Summary

Implements Utah Unemployment Insurance (UI) in PolicyEngine, administered by the Utah Department of Workforce Services (DWS) under Utah Code Title 35A Chapter 4 and Utah Admin Code R994 series.

Closes #8280

Regulatory Authority

Weekly Benefit Amount (WBA) Formula

Monetary Eligibility Tests

  1. Base-period wages ≥ $5,500 (Utah Code § 35A-4-201(16))
  2. Wages in ≥ 2 base-period quarters (Utah Admin Code R994-401-202)
  3. Base-period wages ≥ 1.5 × high-quarter wages (Utah Code § 35A-4-403(1)(f)(i))

Duration of Benefits

  • weeks = clamp(floor(0.27 × base_period_wages / WBA), 10, 26)
  • Maximum Benefit Amount = WBA × weeks
  • Source: Utah Code § 35A-4-401(4)

Partial Benefits

Requirements Coverage

Req Statute / Rule Parameter Variable Test
REQ-001 UC § 35A-4-201(1)(a) n/a ut_ui_base_period_wages.py (docstring) documented
REQ-002 UC § 35A-4-201(1)(b) n/a ut_ui_base_period_wages.py (docstring) documented
REQ-003 UC § 35A-4-201(16) eligibility/min_base_period_wages.yaml ($5,500) ut_ui_monetarily_eligible.py ut_ui_monetarily_eligible.yaml Case 2; edge Cases 1-3
REQ-004 R994-401-202 eligibility/min_quarters_with_wages.yaml (2) ut_ui_monetarily_eligible.py ut_ui_monetarily_eligible.yaml Case 3; edge Cases 6-7
REQ-005 UC § 35A-4-403(1)(f)(i) eligibility/base_period_to_high_quarter_ratio.yaml (1.5) ut_ui_monetarily_eligible.py ut_ui_monetarily_eligible.yaml Case 4; edge Cases 4-5
REQ-006 composite n/a ut_ui_monetarily_eligible.py ut_ui_monetarily_eligible.yaml Case 1
REQ-007 UC § 35A-4-401(2)(a)(ii) wba/divisor.yaml (26); wba/subtraction.yaml ($5) ut_ui_weekly_benefit_amount.py ut_ui_weekly_benefit_amount.yaml Case 1
REQ-008 UC § 35A-4-401(2)(b)(ii) wba/max_amount.yaml (712/777/806) ut_ui_weekly_benefit_amount.py ut_ui_weekly_benefit_amount.yaml Cases 2-3; edge Cases 10-13
REQ-009 UC § 35A-4-401 (no min) omitted (no min_amount.yaml) ut_ui_weekly_benefit_amount.py (max_(raw, 0)) edge Case 9
REQ-010 composite n/a ut_ui_weekly_benefit_amount.py (× monetarily_eligible) ut_ui_weekly_benefit_amount.yaml Case 4
REQ-011 UC § 35A-4-401(4)(b) duration/multiplier.yaml, min_weeks.yaml, max_weeks.yaml ut_ui_duration_weeks.py ut_ui_duration_weeks.yaml Cases 1-3
REQ-012 UC § 35A-4-401(4)(b) duration/min_weeks.yaml (10) ut_ui_duration_weeks.py ut_ui_duration_weeks.yaml Case 2; integration Case 4
REQ-013 UC § 35A-4-401(4)(b) duration/max_weeks.yaml (26) ut_ui_duration_weeks.py ut_ui_duration_weeks.yaml Case 1; edge Cases 16-17
REQ-014 UC § 35A-4-401(4)(b) duration/multiplier.yaml (0.27) ut_ui_duration_weeks.py ut_ui_duration_weeks.yaml Cases 1-3
REQ-015 UC § 35A-4-401(4)(a) n/a ut_ui_maximum_benefit_amount.py ut_ui_maximum_benefit_amount.yaml Cases 1-2; integration Cases 2-5
REQ-016 UC § 35A-4-401(3)(a); R994-401-301 partial/earnings_disregard_rate.yaml (0.30) ut_ui_weekly_payable_amount.py ut_ui_weekly_payable_amount.yaml Cases 2-3; edge Cases 19-20
REQ-017 UC § 35A-4-401(3)(a)–(b) n/a ut_ui_weekly_payable_amount.py ut_ui_weekly_payable_amount.yaml Case 3; edge Case 20
REQ-018 UC § 35A-4-401(3)(a) n/a ut_ui_weekly_payable_amount.py (earnings ≥ WBA → 0) ut_ui_weekly_payable_amount.yaml Case 4; edge Case 18
REQ-019 Claimant Guide p. 11 partial/hours_threshold.yaml (40) ut_ui_weekly_payable_amount.py ut_ui_weekly_payable_amount.yaml Case 5; edge Cases 21-23
REQ-020 UC § 35A-4-401(3)(c) n/a ut_ui_gross_weekly_earnings.py (docstring) documented
REQ-021 composite n/a ut_ui.py (defined_for=StateCode.UT) integration Cases 2-6; out-of-state Case 7; edge Cases 26-27
REQ-022 UC § 35A-4-403(1)(d) n/a ut_ui.py + ut_ui_duration_weeks.py (26-week cap absorbs waiting week) integration Case 2
REQ-038 input definition n/a ut_ui_base_period_wages.py (Person/YEAR/USD) integration.yaml
REQ-039 input definition n/a ut_ui_high_quarter_wages.py (Person/YEAR/USD) integration.yaml
REQ-040 input definition n/a ut_ui_quarters_with_wages.py (Person/YEAR/int) integration.yaml
REQ-041 input definition n/a ut_ui_gross_weekly_earnings.py (Person/YEAR/USD) integration Case 5
REQ-042 input definition n/a ut_ui_weekly_hours_worked.py (Person/YEAR/hour) integration Case 6; edge Cases 21-23
REQ-043 input definition n/a ut_ui_weeks_unemployed.py (Person/YEAR/week) integration Cases 2-6; edge Case 25

29 in-scope requirements (22 modeled + 7 input definitions) — all covered.

Not Modeled (14 items)

  • Disqualifications (Utah Code § 35A-4-405): voluntary quit (REQ-024), discharge for just cause (REQ-025), refusal of suitable work (REQ-026), strike/labor dispute (REQ-027), fraud (REQ-028), education between-terms (REQ-029), sport between-seasons (REQ-030), alien status (REQ-031)
  • Able-and-available and active work-search requirements (Utah Code § 35A-4-403(1)(b)–(c)) — REQ-032, REQ-033
  • 100% retirement-income offset from base-period employer pensions (Utah Code § 35A-4-401(2)(c); R994-401-203) — REQ-023 (no base-period employer attribution in PolicyEngine)
  • Dependents allowance — Utah has none (REQ-034)
  • Extended Benefits (Utah Code § 35A-4-402) — REQ-035
  • Voluntary tax withholding and child-support garnishment (administrative, handled federally) — REQ-036, REQ-037

Files Added

policyengine_us/
├── parameters/gov/states/ut/dwf/unemployment_insurance/
│   ├── index.yaml
│   ├── eligibility/
│   │   ├── min_base_period_wages.yaml
│   │   ├── min_quarters_with_wages.yaml
│   │   └── base_period_to_high_quarter_ratio.yaml
│   ├── wba/
│   │   ├── divisor.yaml
│   │   ├── subtraction.yaml
│   │   └── max_amount.yaml
│   ├── duration/
│   │   ├── multiplier.yaml
│   │   ├── min_weeks.yaml
│   │   └── max_weeks.yaml
│   └── partial/
│       ├── earnings_disregard_rate.yaml
│       └── hours_threshold.yaml
├── variables/gov/states/ut/dwf/unemployment_insurance/
│   ├── ut_ui.py
│   ├── ut_ui_base_period_wages.py
│   ├── ut_ui_high_quarter_wages.py
│   ├── ut_ui_quarters_with_wages.py
│   ├── ut_ui_gross_weekly_earnings.py
│   ├── ut_ui_weekly_hours_worked.py
│   ├── ut_ui_weeks_unemployed.py
│   ├── ut_ui_monetarily_eligible.py
│   ├── ut_ui_weekly_benefit_amount.py
│   ├── ut_ui_duration_weeks.py
│   ├── ut_ui_maximum_benefit_amount.py
│   └── ut_ui_weekly_payable_amount.py
├── tests/policy/baseline/gov/states/ut/dwf/unemployment_insurance/
│   ├── ut_ui_monetarily_eligible.yaml
│   ├── ut_ui_weekly_benefit_amount.yaml
│   ├── ut_ui_duration_weeks.yaml
│   ├── ut_ui_maximum_benefit_amount.yaml
│   ├── ut_ui_weekly_payable_amount.yaml
│   ├── integration.yaml
│   └── ut_ui_edge_cases.yaml
└── programs.yaml  (added `ut_ui` State entry)
  • 12 parameters (+ 1 index.yaml) under policyengine_us/parameters/gov/states/ut/dwf/unemployment_insurance/
  • 12 variables under policyengine_us/variables/gov/states/ut/dwf/unemployment_insurance/
  • 7 test files under policyengine_us/tests/policy/baseline/gov/states/ut/dwf/unemployment_insurance/
  • programs.yaml updated with ut_ui State entry (status=complete, verified_years="2024-2026")

Tests

  • 53 tests passing (6 unit-test files + 1 integration file with 7 scenarios + 1 edge-case file with 27 cases)
  • All values cross-verified against the DWS 2026 Benefit Schedule (form 04-13-0126)
  • Formula floor(HQ/26) − 5 clipped to [0, max_wba] reproduces the published schedule exactly (e.g., HQ $15,054 → WBA $574; HQ ≥ $21,086 → WBA $806 cap)

@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (f9a4a0f) to head (e37c7e7).
⚠️ Report is 1329 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #8281   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            7        13    +6     
  Lines          215       157   -58     
  Branches         6         0    -6     
=========================================
- Hits           215       157   -58     
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.

- Split min_base_period_wages into 2024/2025 ($5,300) and 2026 ($5,500) entries
- Fix Claimant Guide page references (printed p.11 = file p.13)
- Update test boundary cases to match new $5,300 threshold
- Replace misleading 2025 form URL with Wayback Machine archive snapshot
- Clarify Claimant Guide page references (printed p.11 = file p.13)
- Add 2024/2026 boundary tests for min_base_period_wages (Cases 28-32)
- Use canonical verb in base_period_to_high_quarter_ratio description
- Pair statute and admin rule citations on min_quarters_with_wages
Derived from 2024 max WBA ($712) per UC § 35A-4-201(16) + § 35A-4-401(2)(b)(ii):
both values depend on the same insured average fiscal year wage. Working
backward, 8% × annual_IAFY ≈ $4,772, rounded up to next $100 = $4,800.

- Update 2024-01-01 entry from $5,300 to $4,800
- Adjust edge case boundaries (Cases 28-29) to test new $4,800 floor
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 Utah UI

1 participant