From d22b5c6874207bb6a2e4c499b350bfb05a80d0af Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Wed, 13 May 2026 18:04:51 -0400 Subject: [PATCH 01/13] Implement Alabama Unemployment Insurance (al_ui) (ref #8282) Adds the Alabama Unemployment Compensation program (Code of Alabama Title 25, Chapter 4) with monetary eligibility, weekly benefit amount, sliding-scale duration tied to state unemployment rate, dual-cap maximum benefit, partial-week earnings disregard, and the 1-week waiting period. All parameters effective 2020-01-01 per Act 2019-204. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eligibility/bpw_to_hqw_multiplier.yaml | 16 + .../eligibility/quarters_with_wages.yaml | 16 + .../al/dol/unemployment_insurance/index.yaml | 9 + .../mba/bpw_fraction.yaml | 14 + .../mba/duration_weeks.yaml | 45 +++ .../partial/disregard_rate.yaml | 18 + .../state_unemployment_rate.yaml | 89 +++++ .../unemployment_insurance/waiting_weeks.yaml | 16 + .../dol/unemployment_insurance/wba/max.yaml | 16 + .../wba/min_threshold.yaml | 14 + .../al/dol/unemployment_insurance/al_ui.yaml | 246 +++++++++++++ .../al_ui_max_weeks.yaml | 206 +++++++++++ .../al_ui_maximum_benefit_amount.yaml | 242 +++++++++++++ .../al_ui_monetarily_eligible.yaml | 180 ++++++++++ .../al_ui_partial_weekly_benefit.yaml | 252 +++++++++++++ .../al_ui_unrounded_wba.yaml | 122 +++++++ .../al_ui_weekly_benefit_amount.yaml | 284 +++++++++++++++ .../unemployment_insurance/integration.yaml | 337 ++++++++++++++++++ .../set_state_unemployment_rate.py | 60 ++++ .../al/dol/unemployment_insurance/al_ui.py | 27 ++ .../al_ui_base_period_wages.py | 12 + .../al_ui_high_quarter_wages.py | 12 + .../unemployment_insurance/al_ui_max_weeks.py | 20 ++ .../al_ui_maximum_benefit_amount.py | 23 ++ .../al_ui_monetarily_eligible.py | 28 ++ .../al_ui_partial_weekly_benefit.py | 24 ++ .../al_ui_quarters_with_wages.py | 11 + .../al_ui_second_high_quarter_wages.py | 12 + .../al_ui_unrounded_wba.py | 16 + .../al_ui_weekly_benefit_amount.py | 20 ++ .../al_ui_weekly_earnings.py | 15 + .../gov/states/unemployment_compensation.py | 1 + 32 files changed, 2403 insertions(+) create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/index.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/bpw_fraction.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml create mode 100644 policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_base_period_wages.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_high_quarter_wages.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_quarters_with_wages.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_second_high_quarter_wages.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.py create mode 100644 policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml new file mode 100644 index 00000000000..3ea012cd1e1 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml @@ -0,0 +1,16 @@ +description: Alabama requires total base period wages to be at least this multiple of high quarter wages under the Unemployment Compensation program. + +values: + 2020-01-01: 1.5 + +metadata: + unit: /1 + period: year + label: Alabama UI base period wages to high quarter wages multiplier + reference: + - title: Code of Alabama § 25-4-77(a)(4)(a) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml new file mode 100644 index 00000000000..b61770e8731 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml @@ -0,0 +1,16 @@ +description: Alabama requires wages to have been paid in at least this many calendar quarters of the base period under the Unemployment Compensation program. + +values: + 2020-01-01: 2 + +metadata: + unit: int + period: year + label: Alabama UI required quarters with wages + reference: + - title: Code of Alabama § 25-4-77(a)(4) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/index.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/index.yaml new file mode 100644 index 00000000000..620fdcc6732 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/index.yaml @@ -0,0 +1,9 @@ +metadata: + propagate_metadata_to_children: true + economy: false + household: true + reference: + - title: Code of Alabama 1975, Title 25, Chapter 4 — Unemployment Compensation + href: https://law.justia.com/codes/alabama/title-25/chapter-4/ + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/bpw_fraction.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/bpw_fraction.yaml new file mode 100644 index 00000000000..ac9558a6ad7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/bpw_fraction.yaml @@ -0,0 +1,14 @@ +description: Alabama limits the maximum benefit amount to this share of base period wages under the Unemployment Compensation program. + +values: + 2020-01-01: 0.25 + +metadata: + unit: /1 + period: year + label: Alabama UI maximum benefit base period wages fraction + reference: + - title: Code of Alabama § 25-4-74(a) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-11 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=26 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml new file mode 100644 index 00000000000..218ec08bdb7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml @@ -0,0 +1,45 @@ +description: Alabama provides this maximum number of weeks of regular benefits under the Unemployment Compensation program. + +brackets: + - threshold: + 2020-01-01: 0 + amount: + 2020-01-01: 14 + - threshold: + 2020-01-01: 0.0651 # Above 6.5% + amount: + 2020-01-01: 15 + - threshold: + 2020-01-01: 0.0701 # Above 7.0% + amount: + 2020-01-01: 16 + - threshold: + 2020-01-01: 0.0751 # Above 7.5% + amount: + 2020-01-01: 17 + - threshold: + 2020-01-01: 0.0801 # Above 8.0% + amount: + 2020-01-01: 18 + - threshold: + 2020-01-01: 0.0851 # Above 8.5% + amount: + 2020-01-01: 19 + - threshold: + 2020-01-01: 0.0901 # Above 9.0% (statute caps at 20 weeks for AAU >= 9.5%) + amount: + 2020-01-01: 20 + +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: week + period: year + label: Alabama UI maximum weeks of regular benefits by unemployment rate + reference: + - title: Code of Alabama § 25-4-74(a) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ + - title: Alabama Act 2019-204 summary + href: https://alabamaretail.org/news/unemployment-comp-weeks-weekly-benefits/ + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-12 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=29 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml new file mode 100644 index 00000000000..7b82f7bb7d0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml @@ -0,0 +1,18 @@ +description: Alabama excludes this share of the weekly benefit amount from countable earnings before reducing partial unemployment benefits under the Unemployment Compensation program. + +values: + 2020-01-01: 0.3333333333 # One-third of WBA per § 25-4-72; Admin Code 480-4-3-.11 + +metadata: + unit: /1 + period: year + label: Alabama UI partial unemployment earnings disregard rate + reference: + - title: Code of Alabama § 25-4-72 + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + - title: Ala. Admin. Code 480-4-3-.11 — Claims for Partial Unemployment + href: https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11 + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-8 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20 + - title: NELP — New Alabama Unemployment Insurance Law Makes Work Pay + href: https://www.nelp.org/new-alabama-unemployment-insurance-law-makes-work-pay/ diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml new file mode 100644 index 00000000000..e7057317fca --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml @@ -0,0 +1,89 @@ +description: Alabama uses this seasonally-adjusted statewide average unemployment rate under the Unemployment Compensation program. + +values: + 2020-01-01: 0.028 + 2020-02-01: 0.030 + 2020-03-01: 0.031 + 2020-04-01: 0.138 + 2020-05-01: 0.111 + 2020-06-01: 0.087 + 2020-07-01: 0.079 + 2020-08-01: 0.067 + 2020-09-01: 0.059 + 2020-10-01: 0.045 + 2020-11-01: 0.041 + 2020-12-01: 0.038 + 2021-01-01: 0.036 + 2021-02-01: 0.034 + 2021-03-01: 0.033 + 2021-04-01: 0.032 + 2021-05-01: 0.031 + 2021-06-01: 0.030 + 2021-07-01: 0.029 + 2021-08-01: 0.029 + 2021-09-01: 0.029 + 2021-10-01: 0.030 + 2021-11-01: 0.030 + 2021-12-01: 0.030 + 2022-01-01: 0.029 + 2022-02-01: 0.028 + 2022-03-01: 0.027 + 2022-04-01: 0.026 + 2022-05-01: 0.026 + 2022-06-01: 0.026 + 2022-07-01: 0.026 + 2022-08-01: 0.026 + 2022-09-01: 0.026 + 2022-10-01: 0.026 + 2022-11-01: 0.026 + 2022-12-01: 0.026 + 2023-01-01: 0.025 + 2023-02-01: 0.024 + 2023-03-01: 0.023 + 2023-04-01: 0.023 + 2023-05-01: 0.023 + 2023-06-01: 0.023 + 2023-07-01: 0.024 + 2023-08-01: 0.025 + 2023-09-01: 0.025 + 2023-10-01: 0.026 + 2023-11-01: 0.027 + 2023-12-01: 0.028 + 2024-01-01: 0.029 + 2024-02-01: 0.029 + 2024-03-01: 0.029 + 2024-04-01: 0.029 + 2024-05-01: 0.030 + 2024-06-01: 0.030 + 2024-07-01: 0.031 + 2024-08-01: 0.032 + 2024-09-01: 0.032 + 2024-10-01: 0.032 + 2024-11-01: 0.033 + 2024-12-01: 0.033 + 2025-01-01: 0.033 + 2025-02-01: 0.033 + 2025-03-01: 0.033 + 2025-04-01: 0.033 + 2025-05-01: 0.033 + 2025-06-01: 0.034 + 2025-07-01: 0.034 + 2025-08-01: 0.035 + 2025-09-01: 0.035 + 2025-10-01: 0.035 + 2025-11-01: 0.035 + 2025-12-01: 0.035 + 2026-01-01: 0.035 + 2026-02-01: 0.035 + 2026-03-01: 0.035 + 2026-04-01: 0.035 + +metadata: + unit: /1 + period: month + label: Alabama UI state unemployment rate + reference: + - title: FRED — Unemployment Rate in Alabama (ALUR) + href: https://fred.stlouisfed.org/series/ALUR + - title: U.S. Bureau of Labor Statistics — Local Area Unemployment Statistics (LAUS) + href: https://www.bls.gov/lau/ diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml new file mode 100644 index 00000000000..1a98091cc08 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml @@ -0,0 +1,16 @@ +description: Alabama excludes this many initial weeks from compensability under the Unemployment Compensation program. + +values: + 2020-01-01: 1 + +metadata: + unit: week + period: year + label: Alabama UI waiting weeks + reference: + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=6 + - title: Ala. Admin. Code Chapter 480-4-3 — Benefits + href: https://admincode.legislature.state.al.us/administrative-code/480-4-3 + - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-7 + href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=17 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml new file mode 100644 index 00000000000..532dbb937c6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml @@ -0,0 +1,16 @@ +description: Alabama provides this amount as the maximum weekly benefit under the Unemployment Compensation program. + +values: + 2020-01-01: 275 + +metadata: + unit: currency-USD + period: week + label: Alabama UI maximum weekly benefit amount + reference: + - title: Code of Alabama § 25-4-72(b)(5) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + - title: Alabama Act 2019-204 summary + href: https://alabamaretail.org/news/unemployment-comp-weeks-weekly-benefits/ + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml new file mode 100644 index 00000000000..cfd36bb49d8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml @@ -0,0 +1,14 @@ +description: Alabama sets this amount as the minimum unrounded weekly benefit threshold under the Unemployment Compensation program. + +values: + 2020-01-01: 44.5 + +metadata: + unit: currency-USD + period: week + label: Alabama UI minimum unrounded weekly benefit threshold + reference: + - title: Code of Alabama § 25-4-72(b)(2) + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + - title: Alabama UC Benefit Rights and Responsibilities Handbook + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml new file mode 100644 index 00000000000..8800fa1eec2 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml @@ -0,0 +1,246 @@ +# Tests for Alabama UI annual benefit (§§ 25-4-72, 25-4-74). +# Formula: +# payable_weeks = min(max(0, weeks_unemployed - waiting_weeks), max_weeks) +# al_ui = min(partial_weekly_benefit * payable_weeks, maximum_benefit_amount) +# - 1-week waiting week subtracted (BRR p.6; eff 2012-08-01). +# - Annual benefit capped at MBA from § 25-4-74(a). +# References: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ + +- name: Case 1, full 14 weeks at $275 WBA - hits N x WBA cap. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + # weeks_unemployed >= max_weeks + 1 so the entire duration is paid. + weeks_unemployed: 20 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # UR 2024 ~ 2.9% -> 14 weeks; WBA capped at 275. + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 275 + # MBA = min(14 * 275, 0.25 * 40,000) = min(3,850, 10,000) = 3,850. + al_ui_maximum_benefit_amount: 3_850 + # weeks_unemployed 20 - waiting 1 = 19, capped at 14 -> 14 weeks paid. + # Annual = 275 * 14 = 3,850. + al_ui: 3_850 + +- name: Case 2, annual capped at MBA when weeks_unemployed x WBA exceeds MBA. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 100 (from 2,613 each). 14 * 100 = 1,400 if N*WBA binds. + # BPW = 5,000 -> 0.25 * 5,000 = 1,250 -> MBA = 1,250. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 20 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # MBA = min(14 * 100, 0.25 * 5,000) = min(1,400, 1,250) = 1,250. + al_ui_maximum_benefit_amount: 1_250 + # weekly_payable * (20 - 1) = 100 * 19 = 1,900 -> capped at 1,250. + al_ui: 1_250 + +- name: Case 3, monetarily ineligible (1 quarter only) - $0 annual benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 8_000 + al_ui_quarters_with_wages: 1 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui: 0 + +- name: Case 4, zero weeks unemployed - $0 annual benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: true + al_ui_weekly_benefit_amount: 275 + # payable_weeks = min(max(0, 0 - 1), 14) = 0 -> al_ui = 0. + al_ui: 0 + +- name: Case 5, exactly 1 week unemployed - waiting week consumes it - $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 1 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # payable_weeks = min(max(0, 1 - 1), 14) = 0 -> al_ui = 0. + al_ui: 0 + +- name: Case 6, mid-duration unemployment - 10 paid weeks after waiting. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 11 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # payable_weeks = min(max(0, 11 - 1), 14) = min(10, 14) = 10. + # Annual = 275 * 10 = 2,750; MBA = 3,850 (not binding). + al_ui: 2_750 + +- name: Case 7, zero HQW (no wages) - ineligible - $0 annual benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 0 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 0 + al_ui_quarters_with_wages: 0 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Zero quarters -> ineligible -> al_ui = 0 (defined_for gate). + # Confirms graceful handling of no-wage claimants (no crash). + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui_maximum_benefit_amount: 0 + al_ui: 0 + +- name: Case 8, zero BPW with positive HQW - fails 1.5x test - ineligible. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # Pathological: HQW positive but BPW zero (inconsistent inputs). + # The formula must not crash; eligibility correctly fails. + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 4_000 + al_ui_base_period_wages: 0 + al_ui_quarters_with_wages: 2 + al_ui_weekly_earnings: 0 + weeks_unemployed: 10 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # BPW 0 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(4)(a). + al_ui_monetarily_eligible: false + al_ui: 0 + +- name: Case 9, negative weekly earnings (defensive) - full WBA paid each week. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + # Negative weekly earnings should not inflate the benefit. + al_ui_weekly_earnings: -100 + weeks_unemployed: 11 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # max_(earnings - disregard, 0) clamps negative to 0 -> partial = WBA. + al_ui_partial_weekly_benefit: 275 + # payable_weeks = 10. Annual = 275 * 10 = 2,750. + al_ui: 2_750 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml new file mode 100644 index 00000000000..a52e8f06156 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml @@ -0,0 +1,206 @@ +# Tests for Alabama UI maximum benefit weeks (§ 25-4-74(a)). +# The duration is set by a single_amount bracket on the statewide average +# unemployment rate (AAU): +# ≤6.5% -> 14 weeks +# >6.5%-≤7.0% -> 15 +# >7.0%-≤7.5% -> 16 +# >7.5%-≤8.0% -> 17 +# >8.0%-≤8.5% -> 18 +# >8.5%-≤9.0% -> 19 +# ≥9.5% -> 20 +# The bracket is encoded with thresholds at 0, 0.0651, 0.0701, 0.0751, 0.0801, +# 0.0851, 0.0901 -> 14/15/16/17/18/19/20 weeks. Each non-baseline rate is +# pinned via a reform in `set_state_unemployment_rate.py`. The natural +# baseline (2024 FRED ALUR ~ 2.9%) hits bracket 1 and is exercised in +# integration.yaml. +# +# Reference: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ + +- name: Case 1, UR at 6.5% (boundary of first bracket) - 14 weeks. + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_6_5pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.065 < 0.0651 -> first bracket -> 14 weeks (statute: ≤ 6.5%). + al_ui_max_weeks: 14 + +- name: Case 2, UR at 7.0% - 15 weeks (top of second bracket). + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_7pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0651 <= 0.07 < 0.0701 -> bracket 2 -> 15 weeks (statute: ≤ 7.0%). + al_ui_max_weeks: 15 + +- name: Case 3, UR at 7.5% - 16 weeks (top of third bracket). + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_7_5pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0701 <= 0.075 < 0.0751 -> bracket 3 -> 16 weeks. + al_ui_max_weeks: 16 + +- name: Case 4, UR at 8.0% - 17 weeks. + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_8pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0751 <= 0.08 < 0.0801 -> bracket 4 -> 17 weeks. + al_ui_max_weeks: 17 + +- name: Case 5, UR at 8.5% - 18 weeks. + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_8_5pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0801 <= 0.085 < 0.0851 -> bracket 5 -> 18 weeks. + al_ui_max_weeks: 18 + +- name: Case 6, UR at 9.0% - 19 weeks. + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_9pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0851 <= 0.09 < 0.0901 -> bracket 6 -> 19 weeks. + al_ui_max_weeks: 19 + +- name: Case 7, UR at 9.5% - 20 weeks (cap). + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_9_5pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.095 >= 0.0901 -> bracket 7 -> 20 weeks (statute caps at 20 weeks + # for AAU >= 9.5%). + al_ui_max_weeks: 20 + +- name: Case 8, UR at 12% (well above 9.5%) - still 20 weeks (capped). + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_12pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Highest bracket: 20 weeks (cap). + al_ui_max_weeks: 20 + +- name: Case 9, natural FRED UR for 2024 (~2.9%) - 14 weeks without reform. + period: 2024 + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 2024-01 UR = 0.029 from FRED ALUR -> first bracket -> 14 weeks. + al_ui_max_weeks: 14 + +- name: Case 10, UR at 6.51% (just above 6.5% boundary) - 15 weeks. + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_6_51pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0651 hits the second bracket threshold exactly -> 15 weeks. + # Confirms "above 6.5%" semantics: 0.065 -> 14, 0.0651 -> 15. + al_ui_max_weeks: 15 + +- name: Case 11, UR at 15% (extreme high) - still 20 weeks (statute cap). + period: 2024 + reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_15pct + input: + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.15 well above 0.0901 -> bracket 7 (cap) -> 20 weeks. + al_ui_max_weeks: 20 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml new file mode 100644 index 00000000000..7990b89d444 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml @@ -0,0 +1,242 @@ +# Tests for Alabama UI Maximum Benefit Amount (MBA) (§ 25-4-74(a)). +# Formula: MBA = min(N x WBA, 0.25 x BPW), rounded to the nearest dollar. +# N = al_ui_max_weeks (14-20 depending on state UR). +# Dual-cap mirrors PA UC, GA UI, MS UI, etc. +# Reference: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ + +- name: Case 1, N x WBA is the binding cap (high WBA, modest BPW). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = (7,150 + 7,150) / 52 = 275 (at cap). + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + # BPW only barely passes the 1.5x test + # (1.5 * 7,150 = 10,725; BPW = 16,500 > 10,725). + # 0.25 * BPW = 4,125; 14 * 275 = 3,850 -> N*WBA binds. + al_ui_base_period_wages: 16_500 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # UR (2024) ~ 2.9% -> max_weeks = 14. + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 275 + # min(14 * 275, 0.25 * 16,500) = min(3,850, 4,125) = 3,850. + al_ui_maximum_benefit_amount: 3_850 + +- name: Case 2, BPW/4 is the binding cap (modest WBA, very high BPW). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA from top-2 quarters of 2,613 each: + # unrounded = 5,226 / 52 = 100.50 -> rounds DOWN to 100. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + # Large BPW from other low quarters: 0.25 * 12,000 = 3,000. + # 14 * 100 = 1,400 -> N*WBA binds, BPW cap does NOT bind here. + # Adjust: need BPW cap to bind -> BPW must be < 4 * N * WBA = 5,600. + # But BPW must also pass 1.5x test: BPW >= 1.5 * 2,613 = 3,920. + # Pick BPW = 5,000: 0.25 * 5,000 = 1,250 < 1,400 -> BPW cap binds. + al_ui_base_period_wages: 5_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # min(14 * 100, 0.25 * 5,000) = min(1,400, 1,250) = 1,250. + al_ui_maximum_benefit_amount: 1_250 + +- name: Case 3, two caps exactly equal - either binds, returns same value. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # Choose so that 14 * WBA = 0.25 * BPW exactly. + # WBA = 100 -> 14 * 100 = 1,400; BPW = 5,600 -> 0.25 * 5,600 = 1,400. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_600 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # min(1,400, 1,400) = 1,400. + al_ui_maximum_benefit_amount: 1_400 + +- name: Case 4, monetarily ineligible (1 quarter) - MBA is $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 8_000 + al_ui_quarters_with_wages: 1 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Not monetarily eligible -> WBA = 0 -> MBA = min(14 * 0, 2,000) = 0. + al_ui_weekly_benefit_amount: 0 + al_ui_maximum_benefit_amount: 0 + +- name: Case 5, BPW cap with non-integer 0.25 x BPW - rounded per statute. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 100 (from 2,613 + 2,613). 14 * 100 = 1,400. + # BPW = 5,003 -> 0.25 * 5,003 = 1,250.75. + # BPW also passes 1.5x test: 5,003 >= 1.5 * 2,613 = 3,919.5. + # min(1,400, 1,250.75) = 1,250.75 -> per § 25-4-74(a), "such total + # amounts of benefits, if not a multiple of one dollar, shall be + # computed to the nearest multiple of one dollar" -> rounds to 1,251. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_003 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # min(1,400, 1,250.75) = 1,250.75; rounded to nearest dollar -> 1,251. + al_ui_maximum_benefit_amount: 1_251 + +- name: Case 6, BPW cap with 0.25 x BPW just below half - rounds DOWN. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 100 (from 2,613 + 2,613). 14 * 100 = 1,400. + # BPW = 5,001 -> 0.25 * 5,001 = 1,250.25. + # BPW passes 1.5x test: 5,001 >= 1.5 * 2,613 = 3,919.5. + # min(1,400, 1,250.25) = 1,250.25; nearest dollar -> 1,250. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_001 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # 0.25 * 5,001 = 1,250.25 -> np.round -> 1,250. + al_ui_maximum_benefit_amount: 1_250 + +- name: Case 7, large BPW, low WBA - N x WBA dominates (BPW cap not binding). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 50 (unrounded = (1,300 + 1,300) / 52 = 50, no fraction). + # 14 * 50 = 700. + # BPW = 100,000 -> 0.25 * 100,000 = 25,000 (not binding). + al_ui_high_quarter_wages: 1_300 + al_ui_second_high_quarter_wages: 1_300 + al_ui_base_period_wages: 100_000 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 50 + # min(14 * 50, 0.25 * 100,000) = min(700, 25,000) = 700. + al_ui_maximum_benefit_amount: 700 + +- name: Case 8, very small BPW, high WBA - BPW/4 dominates aggressively. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA at 275 cap; BPW just barely passes 1.5x test. + # HQW = 7,150; 1.5 * 7,150 = 10,725; BPW = 10,800 passes. + # 0.25 * 10,800 = 2,700. 14 * 275 = 3,850 -> BPW cap binds. + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 10_800 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 275 + # min(14 * 275, 0.25 * 10,800) = min(3,850, 2,700) = 2,700. + al_ui_maximum_benefit_amount: 2_700 + +- name: Case 9, zero BPW - MBA is $0 (also ineligible). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 0 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 0 + al_ui_quarters_with_wages: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Ineligible -> WBA = 0; MBA = min(14 * 0, 0.25 * 0) = 0. + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui_maximum_benefit_amount: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml new file mode 100644 index 00000000000..0f6ca8a0e13 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml @@ -0,0 +1,180 @@ +# Tests for Alabama UI monetary eligibility (§§ 25-4-77(a)(4), 25-4-72(b)(2)). +# A claimant is monetarily eligible only if ALL THREE tests pass: +# 1. Wages paid in at least 2 calendar quarters of the base period +# (§ 25-4-77(a)(4)). +# 2. Total base-period wages >= 1.5 x high-quarter wages +# (§ 25-4-77(a)(4)(a)). +# 3. Unrounded WBA = (HQW + 2HQW) / 52 must exceed $44.50 +# (§ 25-4-72(b)(2)). +# References: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + +- name: Case 1, all three tests pass - fully monetarily eligible. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 5_000 + al_ui_second_high_quarter_wages: 4_000 + al_ui_base_period_wages: 15_000 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 4 quarters >= 2; BPW 15,000 >= 1.5 * 5,000 = 7,500; + # unrounded WBA = (5,000 + 4,000) / 52 = 173.08 > 44.50. + al_ui_monetarily_eligible: true + +- name: Case 2, fails quarters test - only 1 quarter with wages. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 8_000 + al_ui_quarters_with_wages: 1 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Only 1 quarter < 2 required -> fails § 25-4-77(a)(4). + al_ui_monetarily_eligible: false + +- name: Case 3, fails 1.5x base-wages test. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 1_000 + # BPW < 1.5 * 8,000 = 12,000 -> fails test 2. + al_ui_base_period_wages: 10_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # BPW 10,000 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(4)(a). + al_ui_monetarily_eligible: false + +- name: Case 4, fails unrounded-WBA floor - unrounded WBA below $44.50. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_000 + al_ui_second_high_quarter_wages: 1_000 + al_ui_base_period_wages: 3_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded WBA = (1,000 + 1,000) / 52 = 38.46 < 44.50 -> ineligible. + # (4 qtrs and BPW pass; only the WBA floor fails.) + al_ui_monetarily_eligible: false + +- name: Case 5, boundary - unrounded WBA exactly $44.50 fails (statute uses strictly greater). + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_157 + al_ui_second_high_quarter_wages: 1_157 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded WBA = (1,157 + 1,157) / 52 = 44.50. + # Statute § 25-4-72(b)(2): "not in excess of $44.50" -> ineligible. + al_ui_monetarily_eligible: false + +- name: Case 6, boundary - unrounded WBA just above $44.50 is eligible. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_200 + al_ui_second_high_quarter_wages: 1_200 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded WBA = (1,200 + 1,200) / 52 = 46.15 > 44.50 -> passes floor. + # 3 qtrs >= 2; BPW 4,000 >= 1.5 * 1,200 = 1,800 -> all 3 tests pass. + al_ui_monetarily_eligible: true + +- name: Case 7, exactly 2 quarters and BPW exactly at 1.5x HQW - all tests pass. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 4_000 + al_ui_second_high_quarter_wages: 2_000 + # BPW = 1.5 * 4,000 = 6,000 (exactly at threshold; statute uses + # "equal to or exceeding" -> >= passes). + al_ui_base_period_wages: 6_000 + al_ui_quarters_with_wages: 2 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 2 qtrs = 2 required; BPW 6,000 = 1.5 * 4,000; + # unrounded WBA = (4,000 + 2,000) / 52 = 115.38 > 44.50. + al_ui_monetarily_eligible: true + +- name: Case 8, BPW one dollar below 1.5x HQW - fails test 2. + period: 2024 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 4_000 + al_ui_second_high_quarter_wages: 2_000 + # 1.5 * 4,000 = 6,000; BPW = 5,999 just below. + al_ui_base_period_wages: 5_999 + al_ui_quarters_with_wages: 2 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # BPW 5,999 < 1.5 * 4,000 = 6,000 -> fails § 25-4-77(a)(4)(a). + al_ui_monetarily_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml new file mode 100644 index 00000000000..a0c15a3e7eb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml @@ -0,0 +1,252 @@ +# Tests for Alabama UI partial weekly benefit (§ 25-4-72; Admin Code 480-4-3-.11). +# Formula: partial_weekly_benefit = max(0, WBA - max(0, weekly_earnings - WBA/3)) +# = WBA if weekly_earnings <= WBA/3 +# = WBA - (weekly_earnings - WBA/3) if WBA/3 < weekly_earnings < WBA +# = 0 if weekly_earnings >= WBA +# Equivalently: a week is "partial" if earnings < WBA; the first 1/3 x WBA of +# earnings is disregarded, and earnings above the disregard reduce the WBA +# dollar-for-dollar. +# Reference: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ +# https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11 + +- name: Case 1, zero earnings - full WBA paid. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # WBA = 275 (at cap). 0 earnings -> full WBA paid. + al_ui_weekly_benefit_amount: 275 + al_ui_partial_weekly_benefit: 275 + +- name: Case 2, earnings below WBA/3 disregard - still full WBA paid. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # WBA/3 = 91.67; 80 is below the disregard. + al_ui_weekly_earnings: 80 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # earnings 80 <= WBA/3 = 91.67 -> all disregarded -> full WBA. + al_ui_partial_weekly_benefit: 275 + +- name: Case 3, earnings exactly at WBA/3 - still full WBA paid. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # WBA/3 ≈ 91.67. Use 91.67 to test boundary. + al_ui_weekly_earnings: 91.67 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # earnings = 91.67 ~= WBA/3 -> excess ~= 0 -> full WBA. + al_ui_partial_weekly_benefit: 275 + +- name: Case 4, earnings above WBA/3 - partial benefit reduced dollar-for-dollar. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # WBA/3 ≈ 91.67; earnings 150 -> excess ~= 58.33. + al_ui_weekly_earnings: 150 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # WBA - (earnings - WBA/3) = 275 - (150 - 91.67) = 275 - 58.33 = 216.67. + al_ui_partial_weekly_benefit: 216.67 + +- name: Case 5, earnings just below WBA - very small partial benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # Earnings 270 < WBA 275 -> partial. + al_ui_weekly_earnings: 270 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # 275 - (270 - 91.67) = 275 - 178.33 = 96.67. + al_ui_partial_weekly_benefit: 96.67 + +- name: Case 6, earnings exactly at WBA - no partial benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # Earnings = WBA -> NOT partial (week is "partial" only when earnings + # < WBA per USDOL Tbl 3-8). Returns 0. + al_ui_weekly_earnings: 275 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # earnings >= WBA -> partial benefit = 0. + al_ui_partial_weekly_benefit: 0 + +- name: Case 7, earnings above WBA - no partial benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 500 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # earnings 500 > WBA 275 -> partial benefit = 0. + al_ui_partial_weekly_benefit: 0 + +- name: Case 8, monetarily ineligible - WBA = 0 - partial benefit also $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_000 + al_ui_second_high_quarter_wages: 1_000 + al_ui_base_period_wages: 3_000 + al_ui_quarters_with_wages: 3 + al_ui_weekly_earnings: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded WBA = 38.46 < 44.50 -> ineligible -> WBA = 0. + al_ui_weekly_benefit_amount: 0 + al_ui_partial_weekly_benefit: 0 + +- name: Case 9, negative weekly earnings (defensive) - treated as full WBA. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # Defensive case: a negative earnings value should not inflate the + # benefit beyond the WBA. max_(earnings - disregard, 0) -> 0; + # max_(WBA - 0, 0) -> WBA. The where(weekly_earnings < WBA, ...) + # branch is taken (negative < WBA) -> partial_amount returned. + al_ui_weekly_earnings: -50 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # Negative earnings clamped by max_() -> full WBA, not inflated. + al_ui_partial_weekly_benefit: 275 + +- name: Case 10, earnings exactly one cent below WBA - tiny partial. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # WBA = 275; earnings 274.99 < 275 -> still partial. + al_ui_weekly_earnings: 274.99 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # WBA/3 = 91.6667; 275 - (274.99 - 91.6667) = 275 - 183.3233 = 91.6767. + al_ui_partial_weekly_benefit: 91.68 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.yaml new file mode 100644 index 00000000000..a795f886efd --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.yaml @@ -0,0 +1,122 @@ +# Tests for Alabama UI unrounded weekly benefit amount (§ 25-4-72(b)). +# Formula: unrounded_wba = (high_quarter_wages + second_high_quarter_wages) / 52. +# Equivalent: average of top-2 quarters / 26. +# No rounding, no cap, no eligibility gate — used by eligibility and the +# rounding/capping steps downstream. +# Reference: Code of Alabama § 25-4-72(b); +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + +- name: Case 1, both top quarters equal at $10,000 - unrounded WBA is $384.62. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # (10,000 + 10,000) / 52 = 20,000 / 52 = 384.615... + al_ui_unrounded_wba: 384.62 + +- name: Case 2, unequal top quarters - unrounded WBA averages the two. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 5_200 + al_ui_second_high_quarter_wages: 2_600 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # (5,200 + 2,600) / 52 = 7,800 / 52 = 150. + al_ui_unrounded_wba: 150 + +- name: Case 3, exactly at the $44.50 ineligibility threshold. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # 44.50 * 52 = 2,314, split evenly between top 2 quarters. + al_ui_high_quarter_wages: 1_157 + al_ui_second_high_quarter_wages: 1_157 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # (1,157 + 1,157) / 52 = 2,314 / 52 = 44.50. + al_ui_unrounded_wba: 44.50 + +- name: Case 4, only one quarter of wages - other top quarter is zero. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # (8,000 + 0) / 52 = 153.846... + al_ui_unrounded_wba: 153.85 + +- name: Case 5, no wages at all - unrounded WBA is zero. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 0 + al_ui_second_high_quarter_wages: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_unrounded_wba: 0 + +- name: Case 6, well above the WBA cap - formula still returns the raw value. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 25_000 + al_ui_second_high_quarter_wages: 25_000 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # (25,000 + 25,000) / 52 = 961.538... + # No cap applied here; capping happens in al_ui_weekly_benefit_amount. + al_ui_unrounded_wba: 961.54 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml new file mode 100644 index 00000000000..aac22a7b278 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml @@ -0,0 +1,284 @@ +# Tests for Alabama UI weekly benefit amount (WBA) (§ 25-4-72(b)). +# Formula: +# 1. Compute unrounded WBA = (HQW + 2HQW) / 52. +# 2. If unrounded WBA <= $44.50 -> WBA = 0 (monetary ineligibility). +# 3. Otherwise apply Alabama rounding: fractional part > $0.50 rounds UP, +# <= $0.50 rounds DOWN (statutory half-down). § 25-4-72(b)(1). +# 4. Cap at the statutory maximum (currently $275). § 25-4-72(b)(5). +# Reference: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ + +- name: Case 1, unrounded WBA exactly $44.50 - ineligible, returns $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_157 + al_ui_second_high_quarter_wages: 1_157 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = (1,157 + 1,157) / 52 = 44.50 -> "not in excess of $44.50" + # -> WBA = 0 per § 25-4-72(b)(2). + al_ui_weekly_benefit_amount: 0 + +- name: Case 2, unrounded WBA just above $44.50 - rounded to nearest dollar. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 1_200 + al_ui_second_high_quarter_wages: 1_200 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = (1,200 + 1,200) / 52 = 46.1538... + # Fractional 0.1538 < 0.50 -> rounds DOWN to 46. + al_ui_weekly_benefit_amount: 46 + +- name: Case 3, WBA exactly at $275 cap - HQW + 2HQW = $14,300. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = 14,300 / 52 = 275.00 -> rounded 275; cap also 275. + al_ui_weekly_benefit_amount: 275 + +- name: Case 4, WBA above cap - capped at $275. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = 20,000 / 52 = 384.62 -> rounded 385 -> capped at 275. + al_ui_weekly_benefit_amount: 275 + +- name: Case 5, AL rounding ties round DOWN - unrounded $100.50 -> $100. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # 100.50 * 52 = 5,226; split as 2,613 + 2,613. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 9_000 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = 5,226 / 52 = 100.50 -> fractional 0.50. + # § 25-4-72(b)(1): "fractional parts ... which are fifty cents ($.50) + # or less shall be dropped to the next lower multiple of one dollar" + # -> WBA = 100 (rounds DOWN at the tie). + al_ui_weekly_benefit_amount: 100 + +- name: Case 6, AL rounding strictly above $0.50 rounds UP - unrounded $100.51 -> $101. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # 100.51 * 52 = 5,226.52; provide quarters that sum to 5,227 so + # unrounded = 5,227 / 52 = 100.5192... > 100.50 -> rounds UP. + al_ui_high_quarter_wages: 2_614 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 9_000 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded = (2,614 + 2,613) / 52 = 5,227 / 52 = 100.5192. + # Fractional 0.5192 > 0.50 -> rounds UP to 101. + al_ui_weekly_benefit_amount: 101 + +- name: Case 7, monetarily ineligible - only 1 quarter with wages - WBA is $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 10_000 + al_ui_quarters_with_wages: 1 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Although the unrounded WBA (10,000 / 52 = 192.31) exceeds 44.50, + # the claimant fails § 25-4-77(a)(4) (only 1 quarter < 2 required) + # -> not monetarily eligible -> WBA = 0. + al_ui_weekly_benefit_amount: 0 + +- name: Case 8, monetarily ineligible - BPW below 1.5x HQW - WBA is $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 1_000 + # 1.5 * 8,000 = 12,000; BPW = 10,000 fails the 1.5x test. + al_ui_base_period_wages: 10_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Fails § 25-4-77(a)(4)(a) -> monetarily ineligible -> WBA = 0. + al_ui_weekly_benefit_amount: 0 + +- name: Case 9, zero wages everywhere - no inputs - WBA is $0. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 0 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 0 + al_ui_quarters_with_wages: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Zero quarters fails quarters test -> ineligible -> WBA = 0. + # Also confirms graceful handling of empty inputs (no crash, no NaN). + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + +- name: Case 10, unrounded WBA just above $44.50 ($44.5192) - eligible, rounds to $45. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # (1,158 + 1,157) / 52 = 2,315 / 52 = 44.5192 > 44.50 -> eligible. + al_ui_high_quarter_wages: 1_158 + al_ui_second_high_quarter_wages: 1_157 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Fractional 0.5192 > 0.50 -> rounds UP to 45 per § 25-4-72(b)(1). + al_ui_monetarily_eligible: true + al_ui_weekly_benefit_amount: 45 + +- name: Case 11, unrounded WBA just above $275 ($275.48) - capped at $275. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # (7,163 + 7,162) / 52 = 14,325 / 52 = 275.4808. + # Rounding: ceil(275.4808 - 0.5) = ceil(274.9808) = 275. + # Cap at 275 -> still 275 (cap not actually binding here because + # rounding already produced 275). + al_ui_high_quarter_wages: 7_163 + al_ui_second_high_quarter_wages: 7_162 + al_ui_base_period_wages: 28_650 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + +- name: Case 12, AL rounding ties round DOWN at $1.50 -> $1 (sub-threshold ineligible). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # (39 + 39) / 52 = 78 / 52 = 1.50. + al_ui_high_quarter_wages: 39 + al_ui_second_high_quarter_wages: 39 + al_ui_base_period_wages: 200 + al_ui_quarters_with_wages: 2 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Unrounded WBA 1.50 < 44.50 -> ineligible -> WBA = 0. + # Verifies graceful handling of very low wages without crash; the + # half-down rounding rule itself is exercised in Case 5. + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml new file mode 100644 index 00000000000..a663250c799 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -0,0 +1,337 @@ +# Integration tests for Alabama Unemployment Insurance (AL UI). +# End-to-end scenarios verifying al_ui together with all intermediate +# variables. Each case documents the full chain: monetary eligibility -> +# WBA -> max_weeks -> MBA -> partial weekly benefit -> annual benefit. +# References: +# Code of Alabama §§ 25-4-72, 25-4-74, 25-4-77: +# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/ +# Alabama UC Benefit Rights & Responsibilities (BRR) handbook: +# https://labor.alabama.gov/docs/guides/uc_brr.pdf +# USDOL Comparison of State UI Laws 2023: +# https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf + +- name: Case 1, maximum-benefit claimant - full WBA, full duration, no earnings. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 20 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Monetarily eligible: 4 quarters >= 2, BPW 40k >= 1.5 * 10k = 15k, + # unrounded WBA = 20k / 52 = 384.62 > 44.50. + al_ui_monetarily_eligible: true + # Unrounded WBA = 384.62; rounded then capped at 275. + al_ui_unrounded_wba: 384.62 + al_ui_weekly_benefit_amount: 275 + # UR (2024-01) = 0.029 -> max_weeks = 14. + al_ui_max_weeks: 14 + # MBA = min(14 * 275, 0.25 * 40,000) = min(3,850, 10,000) = 3,850. + al_ui_maximum_benefit_amount: 3_850 + # No earnings -> full WBA per week. + al_ui_partial_weekly_benefit: 275 + # payable_weeks = min(max(0, 20 - 1), 14) = 14. + # Annual = 275 * 14 = 3,850 (matches MBA). + al_ui: 3_850 + +- name: Case 2, minimum-benefit claimant - unrounded WBA just above $44.50. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 28 + al_ui_high_quarter_wages: 1_200 + al_ui_second_high_quarter_wages: 1_200 + al_ui_base_period_wages: 4_000 + al_ui_quarters_with_wages: 3 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 3 quarters >= 2; BPW 4,000 >= 1.5 * 1,200 = 1,800. + # Unrounded WBA = 2,400 / 52 = 46.15 > 44.50. + al_ui_monetarily_eligible: true + al_ui_unrounded_wba: 46.15 + # 46.15 fractional < 0.5 -> rounds DOWN to 46. + al_ui_weekly_benefit_amount: 46 + al_ui_max_weeks: 14 + # MBA = min(14 * 46, 0.25 * 4,000) = min(644, 1,000) = 644. + al_ui_maximum_benefit_amount: 644 + al_ui_partial_weekly_benefit: 46 + # payable_weeks = min(max(0, 14 - 1), 14) = 13. + # Annual = 46 * 13 = 598; MBA 644 not binding. + al_ui: 598 + +- name: Case 3, monetarily ineligible - only 1 quarter with wages. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 0 + al_ui_base_period_wages: 10_000 + al_ui_quarters_with_wages: 1 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Only 1 quarter < 2 required -> ineligible per § 25-4-77(a)(4). + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui_maximum_benefit_amount: 0 + al_ui_partial_weekly_benefit: 0 + al_ui: 0 + +- name: Case 4, monetarily ineligible - BPW below 1.5x HQW. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + al_ui_high_quarter_wages: 8_000 + al_ui_second_high_quarter_wages: 1_000 + # 1.5 * 8,000 = 12,000; BPW 10,000 fails. + al_ui_base_period_wages: 10_000 + al_ui_quarters_with_wages: 3 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Fails § 25-4-77(a)(4)(a) -> ineligible. + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui: 0 + +- name: Case 5, partial-week claimant - earnings between WBA/3 and WBA. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 32 + al_ui_high_quarter_wages: 7_150 + al_ui_second_high_quarter_wages: 7_150 + al_ui_base_period_wages: 28_600 + al_ui_quarters_with_wages: 4 + # WBA/3 = 91.67; earnings 150 falls in partial range. + al_ui_weekly_earnings: 150 + weeks_unemployed: 15 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: true + al_ui_unrounded_wba: 275 + al_ui_weekly_benefit_amount: 275 + al_ui_max_weeks: 14 + # MBA = min(14 * 275, 0.25 * 28,600) = min(3,850, 7,150) = 3,850. + al_ui_maximum_benefit_amount: 3_850 + # Partial weekly benefit = 275 - (150 - 91.67) = 216.67. + al_ui_partial_weekly_benefit: 216.67 + # payable_weeks = min(max(0, 15 - 1), 14) = 14. + # Annual = 216.67 * 14 = 3,033.33 (below MBA 3,850). + al_ui: 3_033.33 + +- name: Case 6, MBA capped by BPW/4 - low-wage but eligible long-claim. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 38 + # WBA = 100 (unrounded = 100.50 -> half-down -> 100). + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + # BPW = 5,000 -> 0.25 * 5,000 = 1,250. 14 * 100 = 1,400. + # min(1,400, 1,250) = 1,250 -> BPW cap binds. + al_ui_base_period_wages: 5_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 15 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: true + al_ui_unrounded_wba: 100.50 + al_ui_weekly_benefit_amount: 100 + al_ui_max_weeks: 14 + # MBA = min(14 * 100, 0.25 * 5,000) = min(1,400, 1,250) = 1,250. + al_ui_maximum_benefit_amount: 1_250 + al_ui_partial_weekly_benefit: 100 + # payable_weeks = min(max(0, 15 - 1), 14) = 14. + # Annual = 100 * 14 = 1,400 -> capped at MBA 1,250. + al_ui: 1_250 + +- name: Case 7, short claim - waiting week subtracted, well below MBA cap. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + al_ui_high_quarter_wages: 5_200 + al_ui_second_high_quarter_wages: 5_200 + al_ui_base_period_wages: 18_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 6 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: true + # Unrounded WBA = 10,400 / 52 = 200 exactly. + al_ui_unrounded_wba: 200 + al_ui_weekly_benefit_amount: 200 + al_ui_max_weeks: 14 + # MBA = min(14 * 200, 0.25 * 18,000) = min(2,800, 4,500) = 2,800. + al_ui_maximum_benefit_amount: 2_800 + al_ui_partial_weekly_benefit: 200 + # payable_weeks = min(max(0, 6 - 1), 14) = 5. + # Annual = 200 * 5 = 1,000 (well below MBA 2,800). + al_ui: 1_000 + +- name: Case 8, two AL unemployed earners in one household - both receive al_ui. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + person2: + age: 33 + al_ui_high_quarter_wages: 5_200 + al_ui_second_high_quarter_wages: 5_200 + al_ui_base_period_wages: 18_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 10 + households: + household: + members: [person1, person2] + state_code: AL + output: + people: + person1: + # WBA = 275 capped; max_weeks 14; payable = min(14-1, 14) = 13. + # Annual = 275 * 13 = 3,575; MBA = 3,850 (not binding). + al_ui_weekly_benefit_amount: 275 + al_ui: 3_575 + person2: + # WBA = 200; max_weeks 14; payable = min(10-1, 14) = 9. + # Annual = 200 * 9 = 1,800; MBA = min(2800, 4500) = 2800 (not binding). + al_ui_weekly_benefit_amount: 200 + al_ui: 1_800 + +- name: Case 9, AL person with no UI inputs (all defaults) - $0 al_ui. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # No al_ui_* inputs supplied. All default to 0. + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # Quarters 0, BPW 0, unrounded WBA 0 -> ineligible -> $0. + al_ui_monetarily_eligible: false + al_ui: 0 + +- name: Case 10, GA person (non-AL) with full inputs - al_ui is $0 (defined_for filter). + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + # Inputs supplied but defined_for=StateCode.AL filters them out for + # any non-AL household; al_ui must be 0 regardless of input values. + # Note: al_ui_* input variables are themselves defined_for=AL, so + # they return 0 for non-AL persons. The full chain therefore yields 0. + households: + household: + members: [person1] + state_code: GA + output: + people: + person1: + # Non-AL state -> defined_for filter applies -> al_ui = 0. + al_ui: 0 + +- name: Case 11, federal aggregator includes al_ui correctly. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 14 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # al_ui = 275 * 13 = 3,575; the federal aggregator + # unemployment_compensation adds al_ui (and ny_ui in the upstream + # version) so the federal total should equal al_ui here. + al_ui: 3_575 + unemployment_compensation: 3_575 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py new file mode 100644 index 00000000000..17f29e51c01 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py @@ -0,0 +1,60 @@ +"""Test reforms that override the Alabama state unemployment rate parameter. + +These reforms exist solely to drive `al_ui_max_weeks.yaml` boundary tests +through each rung of the duration-by-unemployment-rate bracket in +§ 25-4-74(a). Each reform pins the `state_unemployment_rate` parameter to a +fixed value so the bracket lookup deterministically returns the expected +number of weeks regardless of the FRED ALUR time series. +""" + +from policyengine_core.model_api import * +from policyengine_core.periods import instant + + +def _make_reform(value): + """Return a Reform subclass that pins state_unemployment_rate to ``value``.""" + + def modify_parameters(parameters): + parameters.gov.states.al.dol.unemployment_insurance.state_unemployment_rate.update( + start=instant("2020-01-01"), + stop=instant("2030-12-31"), + value=value, + ) + return parameters + + class _R(Reform): + def apply(self): + self.modify_parameters(modify_parameters) + + return _R + + +# Exactly 6.5% — boundary of the first bracket; expect 14 weeks. +set_state_unemployment_rate_to_6_5pct = _make_reform(0.065) + +# 7.0% — boundary of the second bracket; expect 15 weeks. +set_state_unemployment_rate_to_7pct = _make_reform(0.07) + +# 7.5% — boundary of the third bracket; expect 16 weeks. +set_state_unemployment_rate_to_7_5pct = _make_reform(0.075) + +# 8.0% — boundary of the fourth bracket; expect 17 weeks. +set_state_unemployment_rate_to_8pct = _make_reform(0.08) + +# 8.5% — boundary of the fifth bracket; expect 18 weeks. +set_state_unemployment_rate_to_8_5pct = _make_reform(0.085) + +# 9.0% — boundary of the sixth bracket; expect 19 weeks. +set_state_unemployment_rate_to_9pct = _make_reform(0.09) + +# 9.5% — boundary of the seventh (cap) bracket; expect 20 weeks. +set_state_unemployment_rate_to_9_5pct = _make_reform(0.095) + +# 12% — well above the cap; expect 20 weeks. +set_state_unemployment_rate_to_12pct = _make_reform(0.12) + +# 6.51% — just above the 6.5% boundary; expect 15 weeks (statute: > 6.5%). +set_state_unemployment_rate_to_6_51pct = _make_reform(0.0651) + +# 15% — very high (well above 9.5% cap); expect 20 weeks. +set_state_unemployment_rate_to_15pct = _make_reform(0.15) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py new file mode 100644 index 00000000000..47ce567e2c6 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class al_ui(Variable): + value_type = float + entity = Person + label = "Alabama Unemployment Insurance" + unit = USD + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/", + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/", + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/", + ) + defined_for = "al_ui_monetarily_eligible" + + def formula(person, period, parameters): + p = parameters(period).gov.states.al.dol.unemployment_insurance + wba = person("al_ui_weekly_benefit_amount", period) + partial_weekly_benefit = person("al_ui_partial_weekly_benefit", period) + weekly_earnings = person("al_ui_weekly_earnings", period) + max_weeks = person("al_ui_max_weeks", period) + mba = person("al_ui_maximum_benefit_amount", period) + weeks_unemployed = person("weeks_unemployed", period) + weeks_paid = clip(weeks_unemployed - p.waiting_weeks, 0, max_weeks) + weekly_amount = where(weekly_earnings < wba, partial_weekly_benefit, wba) + return min_(weeks_paid * weekly_amount, mba) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_base_period_wages.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_base_period_wages.py new file mode 100644 index 00000000000..f90c5c39a52 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_base_period_wages.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class al_ui_base_period_wages(Variable): + value_type = float + entity = Person + label = "Alabama UI base period wages" + unit = USD + definition_period = YEAR + default_value = 0 + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/" + defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_high_quarter_wages.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_high_quarter_wages.py new file mode 100644 index 00000000000..897f9b31f2f --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_high_quarter_wages.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class al_ui_high_quarter_wages(Variable): + value_type = float + entity = Person + label = "Alabama UI high quarter wages" + unit = USD + definition_period = YEAR + default_value = 0 + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/" + defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py new file mode 100644 index 00000000000..209f909fd95 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class al_ui_max_weeks(Variable): + value_type = int + entity = Person + label = "Alabama UI maximum number of weeks of regular benefits" + unit = "week" + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/", + "https://alabamaretail.org/news/unemployment-comp-weeks-weekly-benefits/", + ) + defined_for = StateCode.AL + + def formula(person, period, parameters): + # NOTE: state_unemployment_rate is monthly; at YEAR period the + # framework returns the January value of the benefit year. + p = parameters(period).gov.states.al.dol.unemployment_insurance + return p.mba.duration_weeks.calc(p.state_unemployment_rate) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py new file mode 100644 index 00000000000..78455c2e0e7 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class al_ui_maximum_benefit_amount(Variable): + value_type = float + entity = Person + label = "Alabama UI maximum benefit amount" + unit = USD + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/", + "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=26", + ) + defined_for = StateCode.AL + + def formula(person, period, parameters): + p = parameters(period).gov.states.al.dol.unemployment_insurance + wba = person("al_ui_weekly_benefit_amount", period) + max_weeks = person("al_ui_max_weeks", period) + base_period_wages = person("al_ui_base_period_wages", period) + weeks_cap = max_weeks * wba + bpw_cap = p.mba.bpw_fraction * base_period_wages + return np.round(min_(weeks_cap, bpw_cap)) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.py new file mode 100644 index 00000000000..f3ee71bc0f1 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class al_ui_monetarily_eligible(Variable): + value_type = bool + entity = Person + label = "Monetarily eligible for Alabama Unemployment Insurance" + definition_period = YEAR + reference = ( + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/", + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/", + ) + defined_for = StateCode.AL + + def formula(person, period, parameters): + p = parameters(period).gov.states.al.dol.unemployment_insurance + high_quarter_wages = person("al_ui_high_quarter_wages", period) + base_period_wages = person("al_ui_base_period_wages", period) + quarters_with_wages = person("al_ui_quarters_with_wages", period) + unrounded_wba = person("al_ui_unrounded_wba", period) + + meets_quarters_test = quarters_with_wages >= p.eligibility.quarters_with_wages + meets_wages_test = ( + base_period_wages + >= p.eligibility.bpw_to_hqw_multiplier * high_quarter_wages + ) + meets_unrounded_wba_floor = unrounded_wba > p.wba.min_threshold + return meets_quarters_test & meets_wages_test & meets_unrounded_wba_floor diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py new file mode 100644 index 00000000000..93cb986f235 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * + + +class al_ui_partial_weekly_benefit(Variable): + value_type = float + entity = Person + label = "Alabama UI partial weekly benefit amount" + unit = USD + definition_period = YEAR + reference = ( + "https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11", + "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", + "https://www.nelp.org/new-alabama-unemployment-insurance-law-makes-work-pay/", + ) + defined_for = StateCode.AL + + def formula(person, period, parameters): + p = parameters(period).gov.states.al.dol.unemployment_insurance.partial + wba = person("al_ui_weekly_benefit_amount", period) + weekly_earnings = person("al_ui_weekly_earnings", period) + disregard = wba * p.disregard_rate + countable_earnings = max_(weekly_earnings - disregard, 0) + partial_amount = max_(wba - countable_earnings, 0) + return where(weekly_earnings < wba, partial_amount, 0) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_quarters_with_wages.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_quarters_with_wages.py new file mode 100644 index 00000000000..2fb4822eb88 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_quarters_with_wages.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class al_ui_quarters_with_wages(Variable): + value_type = int + entity = Person + label = "Alabama UI quarters with wages" + definition_period = YEAR + default_value = 0 + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/" + defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_second_high_quarter_wages.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_second_high_quarter_wages.py new file mode 100644 index 00000000000..21003791269 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_second_high_quarter_wages.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class al_ui_second_high_quarter_wages(Variable): + value_type = float + entity = Person + label = "Alabama UI second high quarter wages" + unit = USD + definition_period = YEAR + default_value = 0 + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/" + defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py new file mode 100644 index 00000000000..14fc4247a47 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class al_ui_unrounded_wba(Variable): + value_type = float + entity = Person + label = "Alabama UI unrounded weekly benefit amount" + unit = USD + definition_period = YEAR + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/" + defined_for = StateCode.AL + + def formula(person, period, parameters): + high_quarter_wages = person("al_ui_high_quarter_wages", period) + second_high_quarter_wages = person("al_ui_second_high_quarter_wages", period) + return (high_quarter_wages + second_high_quarter_wages) / WEEKS_IN_YEAR diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.py new file mode 100644 index 00000000000..13605f24bae --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class al_ui_weekly_benefit_amount(Variable): + value_type = float + entity = Person + label = "Alabama UI weekly benefit amount" + unit = USD + definition_period = YEAR + reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/" + defined_for = StateCode.AL + + def formula(person, period, parameters): + p = parameters(period).gov.states.al.dol.unemployment_insurance.wba + unrounded_wba = person("al_ui_unrounded_wba", period) + monetarily_eligible = person("al_ui_monetarily_eligible", period) + # NOTE: § 25-4-72(b)(1) half-down rounding: ties at $0.50 round DOWN. + # np.ceil(x - 0.5): 100.50 -> 100, 100.51 -> 101. + rounded_wba = np.ceil(unrounded_wba - 0.5) + return min_(rounded_wba, p.max) * monetarily_eligible diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py new file mode 100644 index 00000000000..52ee885e676 --- /dev/null +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py @@ -0,0 +1,15 @@ +from policyengine_us.model_api import * + + +class al_ui_weekly_earnings(Variable): + value_type = float + entity = Person + label = "Alabama UI gross weekly earnings during a partial unemployment week" + unit = USD + definition_period = YEAR + default_value = 0 + reference = ( + "https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11", + "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", + ) + defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/unemployment_compensation.py b/policyengine_us/variables/gov/states/unemployment_compensation.py index 0857864ab17..d04dad71b99 100644 --- a/policyengine_us/variables/gov/states/unemployment_compensation.py +++ b/policyengine_us/variables/gov/states/unemployment_compensation.py @@ -9,3 +9,4 @@ class unemployment_compensation(Variable): documentation = "Income from unemployment compensation programs." definition_period = YEAR uprating = "calibration.gov.irs.soi.unemployment_compensation" + adds = ["al_ui"] From 8b5165f45bcb7ce2f61172bcb55a3e0a21ff0442 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Wed, 13 May 2026 18:05:33 -0400 Subject: [PATCH 02/13] Add changelog fragment for al_ui --- changelog.d/al-ui.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/al-ui.added.md diff --git a/changelog.d/al-ui.added.md b/changelog.d/al-ui.added.md new file mode 100644 index 00000000000..d63188bac85 --- /dev/null +++ b/changelog.d/al-ui.added.md @@ -0,0 +1 @@ +Implement Alabama Unemployment Insurance (al_ui). From 359552a333a0a5b6041cbbaf7d99ae39519b5fbe Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 08:57:39 -0400 Subject: [PATCH 03/13] Review-fix round 1: address critical issues from /review-program MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - al_ui.py: fix partial-benefit fallback (was returning full WBA when weekly_earnings >= WBA; should return 0 per "deemed employed" rule in § 25-4-72 / USDOL Tbl 3-8). Now uses al_ui_partial_weekly_benefit directly, which already handles this case correctly. - Fix 5 BRR PDF page anchors (printed page vs file page; off by 3): eligibility/{bpw_to_hqw_multiplier,quarters_with_wages}.yaml, wba/{max,min_threshold}.yaml, waiting_weeks.yaml. - Replace Ala. Admin. Code 480-4-3-.11 citation with statute § 25-4-73 for the 1/3 WBA partial disregard (480-4-3-.11 is procedural). - Add al_ui entry to programs.yaml registry. - Add test case for weekly_earnings >= WBA returning 0 (locks in the al_ui.py fix). - Fix two parameter description verbs ("requires" → "sets"). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../eligibility/bpw_to_hqw_multiplier.yaml | 4 +-- .../eligibility/quarters_with_wages.yaml | 4 +-- .../partial/disregard_rate.yaml | 8 ++--- .../unemployment_insurance/waiting_weeks.yaml | 2 +- .../dol/unemployment_insurance/wba/max.yaml | 2 +- .../wba/min_threshold.yaml | 2 +- policyengine_us/programs.yaml | 11 +++++++ .../al/dol/unemployment_insurance/al_ui.yaml | 29 ++++++++++++++++++- .../al/dol/unemployment_insurance/al_ui.py | 5 +--- .../al_ui_partial_weekly_benefit.py | 2 +- .../al_ui_weekly_earnings.py | 2 +- 11 files changed, 52 insertions(+), 19 deletions(-) diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml index 3ea012cd1e1..372f9c78a0b 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml @@ -1,4 +1,4 @@ -description: Alabama requires total base period wages to be at least this multiple of high quarter wages under the Unemployment Compensation program. +description: Alabama sets this multiple of high quarter wages as the minimum total base period wages required under the Unemployment Compensation program. values: 2020-01-01: 1.5 @@ -11,6 +11,6 @@ metadata: - title: Code of Alabama § 25-4-77(a)(4)(a) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml index b61770e8731..e344d47d9eb 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml @@ -1,4 +1,4 @@ -description: Alabama requires wages to have been paid in at least this many calendar quarters of the base period under the Unemployment Compensation program. +description: Alabama sets this minimum number of calendar quarters of the base period in which wages must have been paid under the Unemployment Compensation program. values: 2020-01-01: 2 @@ -11,6 +11,6 @@ metadata: - title: Code of Alabama § 25-4-77(a)(4) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml index 7b82f7bb7d0..20d3cfa4864 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml @@ -1,17 +1,15 @@ description: Alabama excludes this share of the weekly benefit amount from countable earnings before reducing partial unemployment benefits under the Unemployment Compensation program. values: - 2020-01-01: 0.3333333333 # One-third of WBA per § 25-4-72; Admin Code 480-4-3-.11 + 2020-01-01: 0.3333333333 # One-third of WBA per § 25-4-73 (amended 2015). metadata: unit: /1 period: year label: Alabama UI partial unemployment earnings disregard rate reference: - - title: Code of Alabama § 25-4-72 - href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ - - title: Ala. Admin. Code 480-4-3-.11 — Claims for Partial Unemployment - href: https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11 + - title: Code of Alabama § 25-4-73 + href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-73/ - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-8 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20 - title: NELP — New Alabama Unemployment Insurance Law Makes Work Pay diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml index 1a98091cc08..6dc5b98abec 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml @@ -9,7 +9,7 @@ metadata: label: Alabama UI waiting weeks reference: - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=6 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=9 - title: Ala. Admin. Code Chapter 480-4-3 — Benefits href: https://admincode.legislature.state.al.us/administrative-code/480-4-3 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-7 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml index 532dbb937c6..712a669cb7d 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml @@ -13,4 +13,4 @@ metadata: - title: Alabama Act 2019-204 summary href: https://alabamaretail.org/news/unemployment-comp-weeks-weekly-benefits/ - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml index cfd36bb49d8..51a634a7930 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/min_threshold.yaml @@ -11,4 +11,4 @@ metadata: - title: Code of Alabama § 25-4-72(b)(2) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/ - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=5 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 88e0008d86b..e5fe729556b 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1101,6 +1101,17 @@ programs: verified_start_year: 2023 # --- State programs --- + - id: al_ui + name: Alabama UI + full_name: Alabama Unemployment Compensation + category: Benefits + agency: Alabama Department of Workforce + status: complete + coverage: AL + variable: al_ui + parameter_prefix: gov.states.al.dol.unemployment_insurance + verified_years: "2020-2025" + - id: co_oap name: Colorado OAP full_name: Colorado Old Age Pension diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml index 8800fa1eec2..e90f2233025 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml @@ -218,7 +218,34 @@ al_ui_monetarily_eligible: false al_ui: 0 -- name: Case 9, negative weekly earnings (defensive) - full WBA paid each week. +- name: Case 9, weekly earnings >= WBA - deemed employed, $0 annual benefit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + # Weekly earnings >= WBA -> "deemed employed" per § 25-4-72 / USDOL Tbl 3-8. + al_ui_weekly_earnings: 300 + weeks_unemployed: 11 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 275 + # earnings (300) >= WBA (275) -> partial weekly benefit = 0. + al_ui_partial_weekly_benefit: 0 + # Annual benefit = 0 (no compensable weeks). + al_ui: 0 + +- name: Case 10, negative weekly earnings (defensive) - full WBA paid each week. period: 2024 absolute_error_margin: 0.01 input: diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py index 47ce567e2c6..69a259d34b9 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py @@ -16,12 +16,9 @@ class al_ui(Variable): def formula(person, period, parameters): p = parameters(period).gov.states.al.dol.unemployment_insurance - wba = person("al_ui_weekly_benefit_amount", period) partial_weekly_benefit = person("al_ui_partial_weekly_benefit", period) - weekly_earnings = person("al_ui_weekly_earnings", period) max_weeks = person("al_ui_max_weeks", period) mba = person("al_ui_maximum_benefit_amount", period) weeks_unemployed = person("weeks_unemployed", period) weeks_paid = clip(weeks_unemployed - p.waiting_weeks, 0, max_weeks) - weekly_amount = where(weekly_earnings < wba, partial_weekly_benefit, wba) - return min_(weeks_paid * weekly_amount, mba) + return min_(weeks_paid * partial_weekly_benefit, mba) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py index 93cb986f235..49259519081 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py @@ -8,7 +8,7 @@ class al_ui_partial_weekly_benefit(Variable): unit = USD definition_period = YEAR reference = ( - "https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11", + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-73/", "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", "https://www.nelp.org/new-alabama-unemployment-insurance-law-makes-work-pay/", ) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py index 52ee885e676..de12d817afb 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py @@ -9,7 +9,7 @@ class al_ui_weekly_earnings(Variable): definition_period = YEAR default_value = 0 reference = ( - "https://admincode.legislature.state.al.us/administrative-code/480-4-3-.11", + "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-73/", "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", ) defined_for = StateCode.AL From 04dae41d27b7cec1db8bdd6357e5776da5023034 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 09:12:13 -0400 Subject: [PATCH 04/13] Review-fix round 2: tighten BRR page anchor for quarters_with_wages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 2 review identified that the "two quarters of your base period" sentence is on file page 7 of the BRR handbook (printed page 4), not page 8. Page 8 has the related "two highest base period quarters" phrasing, which supports the value but is less direct. Tightening the anchor for precision. Round 2 found 0 critical issues otherwise — all Round 1 fixes verified correct, no regressions. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../unemployment_insurance/eligibility/quarters_with_wages.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml index e344d47d9eb..f03c67c989d 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml @@ -11,6 +11,6 @@ metadata: - title: Code of Alabama § 25-4-77(a)(4) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 + href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=7 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 From 1fe812aba46c04b0fa171966018eff4306cc8cf9 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 09:17:17 -0400 Subject: [PATCH 05/13] Add lessons from Alabama UI implementation --- lessons/agent-lessons.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 lessons/agent-lessons.md diff --git a/lessons/agent-lessons.md b/lessons/agent-lessons.md new file mode 100644 index 00000000000..25ae07fefce --- /dev/null +++ b/lessons/agent-lessons.md @@ -0,0 +1,37 @@ +# Agent Lessons Learned + +Accumulated from /encode-policy-v2 and /backdate-program runs across all contributors. +Loaded by implementation agents on future runs. + +## New Lessons from Alabama UI Implementation (2026-05-14) + +### PARAMETER +- Restrict parameter descriptions to the authorized verb set (limits/provides/sets/excludes/deducts/uses); reject synonyms like "requires" even when grammatically correct. +- Drop explanatory tails from parameter descriptions after the canonical "X provides this Y under the Z program." form; trailing "to determine..." or "based on..." clauses are noise. +- Strip trailing zeros from decimal parameter values (e.g., 44.50 to 44.5) so the YAML matches the canonical numeric form. +- Include the program acronym in parameter labels (e.g., "Alabama UI state unemployment rate" not "Alabama state unemployment rate") so labels are unambiguous when multiple programs share a state. + +### VARIABLE +- Remove inline statute-restatement comments when the class-level `reference` already cites the same authority; keep inline comments only for non-obvious idioms (e.g., half-down rounding) or non-obvious parameter timing. +- Replace hard-coded weeks-per-year integers (52) with framework constants like `WEEKS_IN_YEAR`; the same rule applies to other calendar constants. +- Inline single-use intermediates (e.g., `uncapped_mba = ...; return np.round(uncapped_mba)`) into the return expression when the intermediate name adds no clarity over the operations. + +### FORMULA +- Do not wrap a partial-benefit calculation that already returns 0 at the unemployed boundary in a redundant `where(earnings < WBA, partial, WBA)` selector; the wrapper silently pays full WBA when earnings meet or exceed WBA. +- Test the partial-benefit boundary explicitly at earnings = WBA and earnings > WBA expecting 0; passing tests on earnings < WBA do not exercise the formula's exit branch. + +### REFERENCE +- A procedural Admin Code section (filing mechanics, deadlines, recordkeeping) cannot serve as the citation authority for a substantive amount or rate; cite the substantive statute or rule that establishes the value. +- When an Admin Code rule has not been republished since a later statutory amendment changed the value, follow the statute and cite it; document the staleness so future contributors do not "fix" the model back to the obsolete admin-code figure. +- USDOL Comparison-of-State-UI-Laws "Minimum Wages Needed to Qualify" columns are derived illustrative figures (assumes equal-quarter wage pattern), not statutory floors; do not encode them as parameters. + +### TEST +- Reform-helper Python modules that set parameter-driven enum or bracket values for YAML tests belong alongside the YAML tests in the test directory; document the unusual placement in a header comment so future readers do not move them. +- After fixing a formula bug discovered post-implementation, add the missing boundary test in the same commit so the regression is locked in immediately. + +### WORKFLOW +- When the reference implementation for a new program lives in a sibling PR not yet merged, read it from the parent repo path rather than expecting it on the current worktree base; coordinate this in the implementation spec. +- Clean `__pycache__` directories between worktrees that share a parent path to avoid `conftest.py` collisions that intermittently block test collection. +- When two reviewers conflict on statute-vs-admin-code authority, resolve via the more recent enactment date and the substantive-vs-procedural distinction; record the rationale in the PR so the next contributor inherits the decision. +- Honor user-driven historical-scope simplifications (e.g., "2020 onward only") by collapsing pre-scope dated entries and trimming reference research to the in-scope window; do not silently encode out-of-scope history. + From 54f4374a9de764677635cf4bf85776cc3127d001 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 10:53:16 -0400 Subject: [PATCH 06/13] Round Alabama UI partial weekly benefits --- .../al_ui_partial_weekly_benefit.yaml | 14 +++++++------- .../al/dol/unemployment_insurance/integration.yaml | 9 +++++---- .../al_ui_partial_weekly_benefit.py | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml index a0c15a3e7eb..95b4c7b571e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml @@ -1,7 +1,7 @@ # Tests for Alabama UI partial weekly benefit (§ 25-4-72; Admin Code 480-4-3-.11). # Formula: partial_weekly_benefit = max(0, WBA - max(0, weekly_earnings - WBA/3)) # = WBA if weekly_earnings <= WBA/3 -# = WBA - (weekly_earnings - WBA/3) if WBA/3 < weekly_earnings < WBA +# = rounded(WBA - (weekly_earnings - WBA/3)) if WBA/3 < weekly_earnings < WBA # = 0 if weekly_earnings >= WBA # Equivalently: a week is "partial" if earnings < WBA; the first 1/3 x WBA of # earnings is disregarded, and earnings above the disregard reduce the WBA @@ -102,8 +102,8 @@ people: person1: al_ui_weekly_benefit_amount: 275 - # WBA - (earnings - WBA/3) = 275 - (150 - 91.67) = 275 - 58.33 = 216.67. - al_ui_partial_weekly_benefit: 216.67 + # WBA - (earnings - WBA/3) = 275 - (150 - 91.67) = 216.67 -> rounded to $217. + al_ui_partial_weekly_benefit: 217 - name: Case 5, earnings just below WBA - very small partial benefit. period: 2024 @@ -126,8 +126,8 @@ people: person1: al_ui_weekly_benefit_amount: 275 - # 275 - (270 - 91.67) = 275 - 178.33 = 96.67. - al_ui_partial_weekly_benefit: 96.67 + # 275 - (270 - 91.67) = 96.67 -> rounded to $97. + al_ui_partial_weekly_benefit: 97 - name: Case 6, earnings exactly at WBA - no partial benefit. period: 2024 @@ -248,5 +248,5 @@ people: person1: al_ui_weekly_benefit_amount: 275 - # WBA/3 = 91.6667; 275 - (274.99 - 91.6667) = 275 - 183.3233 = 91.6767. - al_ui_partial_weekly_benefit: 91.68 + # WBA/3 = 91.6667; 275 - (274.99 - 91.6667) = 91.6767 -> rounded to $92. + al_ui_partial_weekly_benefit: 92 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml index a663250c799..e198344ed7f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -45,6 +45,7 @@ # payable_weeks = min(max(0, 20 - 1), 14) = 14. # Annual = 275 * 14 = 3,850 (matches MBA). al_ui: 3_850 + unemployment_compensation: 3_850 - name: Case 2, minimum-benefit claimant - unrounded WBA just above $44.50. period: 2024 @@ -160,11 +161,11 @@ al_ui_max_weeks: 14 # MBA = min(14 * 275, 0.25 * 28,600) = min(3,850, 7,150) = 3,850. al_ui_maximum_benefit_amount: 3_850 - # Partial weekly benefit = 275 - (150 - 91.67) = 216.67. - al_ui_partial_weekly_benefit: 216.67 + # Partial weekly benefit = 275 - (150 - 91.67) = 216.67, rounded to $217. + al_ui_partial_weekly_benefit: 217 # payable_weeks = min(max(0, 15 - 1), 14) = 14. - # Annual = 216.67 * 14 = 3,033.33 (below MBA 3,850). - al_ui: 3_033.33 + # Annual = 217 * 14 = 3,038 (below MBA 3,850). + al_ui: 3_038 - name: Case 6, MBA capped by BPW/4 - low-wage but eligible long-claim. period: 2024 diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py index 49259519081..65ef23f2a01 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py @@ -21,4 +21,4 @@ def formula(person, period, parameters): disregard = wba * p.disregard_rate countable_earnings = max_(weekly_earnings - disregard, 0) partial_amount = max_(wba - countable_earnings, 0) - return where(weekly_earnings < wba, partial_amount, 0) + return where(weekly_earnings < wba, np.round(partial_amount), 0) From d9688715561b82a720050e757126d2c386601b77 Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 13:10:02 -0400 Subject: [PATCH 07/13] Trim Alabama UI max weeks tests --- .../al_ui_max_weeks.yaml | 148 ++---------------- .../set_state_unemployment_rate.py | 60 ------- 2 files changed, 12 insertions(+), 196 deletions(-) delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml index a52e8f06156..09401657ad2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml @@ -1,26 +1,11 @@ -# Tests for Alabama UI maximum benefit weeks (§ 25-4-74(a)). -# The duration is set by a single_amount bracket on the statewide average -# unemployment rate (AAU): -# ≤6.5% -> 14 weeks -# >6.5%-≤7.0% -> 15 -# >7.0%-≤7.5% -> 16 -# >7.5%-≤8.0% -> 17 -# >8.0%-≤8.5% -> 18 -# >8.5%-≤9.0% -> 19 -# ≥9.5% -> 20 -# The bracket is encoded with thresholds at 0, 0.0651, 0.0701, 0.0751, 0.0801, -# 0.0851, 0.0901 -> 14/15/16/17/18/19/20 weeks. Each non-baseline rate is -# pinned via a reform in `set_state_unemployment_rate.py`. The natural -# baseline (2024 FRED ALUR ~ 2.9%) hits bracket 1 and is exercised in -# integration.yaml. -# -# Reference: -# https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ +# Tests for Alabama UI maximum benefit weeks (Code of Alabama § 25-4-74(a)). +# Bracket behavior is covered at the statutory floor, first increment, pre-cap, +# and cap. Other benefit-flow tests exercise the natural 2024 ALUR baseline. -- name: Case 1, UR at 6.5% (boundary of first bracket) - 14 weeks. +- name: Case 1, UR at 6.5 percent gets 14 weeks. period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_6_5pct input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.065 people: person1: age: 30 @@ -31,13 +16,12 @@ output: people: person1: - # 0.065 < 0.0651 -> first bracket -> 14 weeks (statute: ≤ 6.5%). al_ui_max_weeks: 14 -- name: Case 2, UR at 7.0% - 15 weeks (top of second bracket). +- name: Case 2, UR just above 6.5 percent gets 15 weeks. period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_7pct input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0651 people: person1: age: 30 @@ -48,13 +32,12 @@ output: people: person1: - # 0.0651 <= 0.07 < 0.0701 -> bracket 2 -> 15 weeks (statute: ≤ 7.0%). al_ui_max_weeks: 15 -- name: Case 3, UR at 7.5% - 16 weeks (top of third bracket). +- name: Case 3, UR at 9.0 percent gets 19 weeks. period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_7_5pct input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.09 people: person1: age: 30 @@ -65,64 +48,12 @@ output: people: person1: - # 0.0701 <= 0.075 < 0.0751 -> bracket 3 -> 16 weeks. - al_ui_max_weeks: 16 - -- name: Case 4, UR at 8.0% - 17 weeks. - period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_8pct - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 0.0751 <= 0.08 < 0.0801 -> bracket 4 -> 17 weeks. - al_ui_max_weeks: 17 - -- name: Case 5, UR at 8.5% - 18 weeks. - period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_8_5pct - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 0.0801 <= 0.085 < 0.0851 -> bracket 5 -> 18 weeks. - al_ui_max_weeks: 18 - -- name: Case 6, UR at 9.0% - 19 weeks. - period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_9pct - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 0.0851 <= 0.09 < 0.0901 -> bracket 6 -> 19 weeks. al_ui_max_weeks: 19 -- name: Case 7, UR at 9.5% - 20 weeks (cap). +- name: Case 4, UR at 9.5 percent reaches the 20-week cap. period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_9_5pct input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.095 people: person1: age: 30 @@ -133,13 +64,10 @@ output: people: person1: - # 0.095 >= 0.0901 -> bracket 7 -> 20 weeks (statute caps at 20 weeks - # for AAU >= 9.5%). al_ui_max_weeks: 20 -- name: Case 8, UR at 12% (well above 9.5%) - still 20 weeks (capped). +- name: Case 5, natural 2024 ALUR baseline gets 14 weeks. period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_12pct input: people: person1: @@ -151,56 +79,4 @@ output: people: person1: - # Highest bracket: 20 weeks (cap). - al_ui_max_weeks: 20 - -- name: Case 9, natural FRED UR for 2024 (~2.9%) - 14 weeks without reform. - period: 2024 - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 2024-01 UR = 0.029 from FRED ALUR -> first bracket -> 14 weeks. al_ui_max_weeks: 14 - -- name: Case 10, UR at 6.51% (just above 6.5% boundary) - 15 weeks. - period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_6_51pct - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 0.0651 hits the second bracket threshold exactly -> 15 weeks. - # Confirms "above 6.5%" semantics: 0.065 -> 14, 0.0651 -> 15. - al_ui_max_weeks: 15 - -- name: Case 11, UR at 15% (extreme high) - still 20 weeks (statute cap). - period: 2024 - reforms: policyengine_us.tests.policy.baseline.gov.states.al.dol.unemployment_insurance.set_state_unemployment_rate.set_state_unemployment_rate_to_15pct - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 0.15 well above 0.0901 -> bracket 7 (cap) -> 20 weeks. - al_ui_max_weeks: 20 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py deleted file mode 100644 index 17f29e51c01..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/set_state_unemployment_rate.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Test reforms that override the Alabama state unemployment rate parameter. - -These reforms exist solely to drive `al_ui_max_weeks.yaml` boundary tests -through each rung of the duration-by-unemployment-rate bracket in -§ 25-4-74(a). Each reform pins the `state_unemployment_rate` parameter to a -fixed value so the bracket lookup deterministically returns the expected -number of weeks regardless of the FRED ALUR time series. -""" - -from policyengine_core.model_api import * -from policyengine_core.periods import instant - - -def _make_reform(value): - """Return a Reform subclass that pins state_unemployment_rate to ``value``.""" - - def modify_parameters(parameters): - parameters.gov.states.al.dol.unemployment_insurance.state_unemployment_rate.update( - start=instant("2020-01-01"), - stop=instant("2030-12-31"), - value=value, - ) - return parameters - - class _R(Reform): - def apply(self): - self.modify_parameters(modify_parameters) - - return _R - - -# Exactly 6.5% — boundary of the first bracket; expect 14 weeks. -set_state_unemployment_rate_to_6_5pct = _make_reform(0.065) - -# 7.0% — boundary of the second bracket; expect 15 weeks. -set_state_unemployment_rate_to_7pct = _make_reform(0.07) - -# 7.5% — boundary of the third bracket; expect 16 weeks. -set_state_unemployment_rate_to_7_5pct = _make_reform(0.075) - -# 8.0% — boundary of the fourth bracket; expect 17 weeks. -set_state_unemployment_rate_to_8pct = _make_reform(0.08) - -# 8.5% — boundary of the fifth bracket; expect 18 weeks. -set_state_unemployment_rate_to_8_5pct = _make_reform(0.085) - -# 9.0% — boundary of the sixth bracket; expect 19 weeks. -set_state_unemployment_rate_to_9pct = _make_reform(0.09) - -# 9.5% — boundary of the seventh (cap) bracket; expect 20 weeks. -set_state_unemployment_rate_to_9_5pct = _make_reform(0.095) - -# 12% — well above the cap; expect 20 weeks. -set_state_unemployment_rate_to_12pct = _make_reform(0.12) - -# 6.51% — just above the 6.5% boundary; expect 15 weeks (statute: > 6.5%). -set_state_unemployment_rate_to_6_51pct = _make_reform(0.0651) - -# 15% — very high (well above 9.5% cap); expect 20 weeks. -set_state_unemployment_rate_to_15pct = _make_reform(0.15) From 698b8980df9a6aef6d84a30e82eb2409de29988d Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Thu, 14 May 2026 14:42:59 -0400 Subject: [PATCH 08/13] Avoid backdated Alabama UI aggregation --- .../states/al/dol/unemployment_insurance/integration.yaml | 8 ++------ .../variables/gov/states/unemployment_compensation.py | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml index e198344ed7f..da0a6a802d5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -45,7 +45,6 @@ # payable_weeks = min(max(0, 20 - 1), 14) = 14. # Annual = 275 * 14 = 3,850 (matches MBA). al_ui: 3_850 - unemployment_compensation: 3_850 - name: Case 2, minimum-benefit claimant - unrounded WBA just above $44.50. period: 2024 @@ -311,7 +310,7 @@ # Non-AL state -> defined_for filter applies -> al_ui = 0. al_ui: 0 -- name: Case 11, federal aggregator includes al_ui correctly. +- name: Case 11, waiting week reduces a 14-week claim. period: 2024 absolute_error_margin: 0.01 input: @@ -331,8 +330,5 @@ output: people: person1: - # al_ui = 275 * 13 = 3,575; the federal aggregator - # unemployment_compensation adds al_ui (and ny_ui in the upstream - # version) so the federal total should equal al_ui here. + # al_ui = 275 * max(0, 14 - 1) = 3,575. al_ui: 3_575 - unemployment_compensation: 3_575 diff --git a/policyengine_us/variables/gov/states/unemployment_compensation.py b/policyengine_us/variables/gov/states/unemployment_compensation.py index d04dad71b99..0857864ab17 100644 --- a/policyengine_us/variables/gov/states/unemployment_compensation.py +++ b/policyengine_us/variables/gov/states/unemployment_compensation.py @@ -9,4 +9,3 @@ class unemployment_compensation(Variable): documentation = "Income from unemployment compensation programs." definition_period = YEAR uprating = "calibration.gov.irs.soi.unemployment_compensation" - adds = ["al_ui"] From 72b04e4fdd076016081ae9f6caa1974aac1cc16c Mon Sep 17 00:00:00 2001 From: Daphne Hansell <128793799+daphnehanse11@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:50:30 -0400 Subject: [PATCH 09/13] Address review: scratch file, agency field, bracket tests, doc comments - Remove stray lessons/agent-lessons.md - Set programs.yaml agency to State per convention - Add just-below/just-at boundary tests for the 16/17/18-week duration brackets (Cases 5-14) - Document intentional modeling choices: partial weekly benefit applied to every payable week, and the statutory WBA/MBA formulas as authoritative over the BRR handbook's simplified gloss - Justia URLs and pinpoint cites verified correct as-is (Article 4; (b)(5)/(b)(2)) and left unchanged Co-Authored-By: Claude Fable 5 --- lessons/agent-lessons.md | 37 ----- policyengine_us/programs.yaml | 2 +- .../al_ui_max_weeks.yaml | 152 +++++++++++++++++- .../al/dol/unemployment_insurance/al_ui.py | 3 + .../al_ui_maximum_benefit_amount.py | 4 + .../al_ui_unrounded_wba.py | 4 + 6 files changed, 161 insertions(+), 41 deletions(-) delete mode 100644 lessons/agent-lessons.md diff --git a/lessons/agent-lessons.md b/lessons/agent-lessons.md deleted file mode 100644 index 25ae07fefce..00000000000 --- a/lessons/agent-lessons.md +++ /dev/null @@ -1,37 +0,0 @@ -# Agent Lessons Learned - -Accumulated from /encode-policy-v2 and /backdate-program runs across all contributors. -Loaded by implementation agents on future runs. - -## New Lessons from Alabama UI Implementation (2026-05-14) - -### PARAMETER -- Restrict parameter descriptions to the authorized verb set (limits/provides/sets/excludes/deducts/uses); reject synonyms like "requires" even when grammatically correct. -- Drop explanatory tails from parameter descriptions after the canonical "X provides this Y under the Z program." form; trailing "to determine..." or "based on..." clauses are noise. -- Strip trailing zeros from decimal parameter values (e.g., 44.50 to 44.5) so the YAML matches the canonical numeric form. -- Include the program acronym in parameter labels (e.g., "Alabama UI state unemployment rate" not "Alabama state unemployment rate") so labels are unambiguous when multiple programs share a state. - -### VARIABLE -- Remove inline statute-restatement comments when the class-level `reference` already cites the same authority; keep inline comments only for non-obvious idioms (e.g., half-down rounding) or non-obvious parameter timing. -- Replace hard-coded weeks-per-year integers (52) with framework constants like `WEEKS_IN_YEAR`; the same rule applies to other calendar constants. -- Inline single-use intermediates (e.g., `uncapped_mba = ...; return np.round(uncapped_mba)`) into the return expression when the intermediate name adds no clarity over the operations. - -### FORMULA -- Do not wrap a partial-benefit calculation that already returns 0 at the unemployed boundary in a redundant `where(earnings < WBA, partial, WBA)` selector; the wrapper silently pays full WBA when earnings meet or exceed WBA. -- Test the partial-benefit boundary explicitly at earnings = WBA and earnings > WBA expecting 0; passing tests on earnings < WBA do not exercise the formula's exit branch. - -### REFERENCE -- A procedural Admin Code section (filing mechanics, deadlines, recordkeeping) cannot serve as the citation authority for a substantive amount or rate; cite the substantive statute or rule that establishes the value. -- When an Admin Code rule has not been republished since a later statutory amendment changed the value, follow the statute and cite it; document the staleness so future contributors do not "fix" the model back to the obsolete admin-code figure. -- USDOL Comparison-of-State-UI-Laws "Minimum Wages Needed to Qualify" columns are derived illustrative figures (assumes equal-quarter wage pattern), not statutory floors; do not encode them as parameters. - -### TEST -- Reform-helper Python modules that set parameter-driven enum or bracket values for YAML tests belong alongside the YAML tests in the test directory; document the unusual placement in a header comment so future readers do not move them. -- After fixing a formula bug discovered post-implementation, add the missing boundary test in the same commit so the regression is locked in immediately. - -### WORKFLOW -- When the reference implementation for a new program lives in a sibling PR not yet merged, read it from the parent repo path rather than expecting it on the current worktree base; coordinate this in the implementation spec. -- Clean `__pycache__` directories between worktrees that share a parent path to avoid `conftest.py` collisions that intermittently block test collection. -- When two reviewers conflict on statute-vs-admin-code authority, resolve via the more recent enactment date and the substantive-vs-procedural distinction; record the rationale in the PR so the next contributor inherits the decision. -- Honor user-driven historical-scope simplifications (e.g., "2020 onward only") by collapsing pre-scope dated entries and trimming reference research to the in-scope window; do not silently encode out-of-scope history. - diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index e5fe729556b..ef1cdc13f7b 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1105,7 +1105,7 @@ programs: name: Alabama UI full_name: Alabama Unemployment Compensation category: Benefits - agency: Alabama Department of Workforce + agency: State status: complete coverage: AL variable: al_ui diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml index 09401657ad2..9be41574e7a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml @@ -1,6 +1,8 @@ # Tests for Alabama UI maximum benefit weeks (Code of Alabama § 25-4-74(a)). -# Bracket behavior is covered at the statutory floor, first increment, pre-cap, -# and cap. Other benefit-flow tests exercise the natural 2024 ALUR baseline. +# Bracket behavior is covered at the statutory floor, first increment, every +# middle bracket boundary (just-below/just-at pairs for the 16-, 17-, 18-, 19-, +# and 20-week thresholds), and the cap. Other benefit-flow tests exercise the +# natural 2024 ALUR baseline. - name: Case 1, UR at 6.5 percent gets 14 weeks. period: 2024 @@ -66,7 +68,151 @@ person1: al_ui_max_weeks: 20 -- name: Case 5, natural 2024 ALUR baseline gets 14 weeks. +- name: Case 5, UR at 7.0 percent stays at 15 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.07 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 15 + +- name: Case 6, UR just above 7.0 percent gets 16 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0701 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 16 + +- name: Case 7, UR at 7.5 percent stays at 16 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.075 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 16 + +- name: Case 8, UR just above 7.5 percent gets 17 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0751 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 17 + +- name: Case 9, UR at 8.0 percent stays at 17 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.08 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 17 + +- name: Case 10, UR just above 8.0 percent gets 18 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0801 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 18 + +- name: Case 11, UR at 8.5 percent stays at 18 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.085 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 18 + +- name: Case 12, UR just above 8.5 percent gets 19 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0851 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 19 + +- name: Case 13, UR just above 9.0 percent gets 20 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0901 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 20 + +- name: Case 14, natural 2024 ALUR baseline gets 14 weeks. period: 2024 input: people: diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py index 69a259d34b9..6d264913503 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py @@ -21,4 +21,7 @@ def formula(person, period, parameters): mba = person("al_ui_maximum_benefit_amount", period) weeks_unemployed = person("weeks_unemployed", period) weeks_paid = clip(weeks_unemployed - p.waiting_weeks, 0, max_weeks) + # Intentional: the partial weekly benefit is applied to every payable + # week, assuming the same weekly earnings across the whole spell + # (equivalent to the full WBA whenever weekly earnings are zero). return min_(weeks_paid * partial_weekly_benefit, mba) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py index 78455c2e0e7..a8dfa5d8b55 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py @@ -19,5 +19,9 @@ def formula(person, period, parameters): max_weeks = person("al_ui_max_weeks", period) base_period_wages = person("al_ui_base_period_wages", period) weeks_cap = max_weeks * wba + # Statutory formula per Code of Ala. § 25-4-74(a): the lesser of + # max-weeks x WBA and one-fourth of base-period wages (confirmed by + # USDOL Comparison of State UI Laws). This is authoritative over the + # BRR handbook's simplified gloss and should not be "corrected". bpw_cap = p.mba.bpw_fraction * base_period_wages return np.round(min_(weeks_cap, bpw_cap)) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py index 14fc4247a47..d775159cd2e 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py @@ -13,4 +13,8 @@ class al_ui_unrounded_wba(Variable): def formula(person, period, parameters): high_quarter_wages = person("al_ui_high_quarter_wages", period) second_high_quarter_wages = person("al_ui_second_high_quarter_wages", period) + # Statutory formula per Code of Ala. § 25-4-72(b): 1/52 of the sum of + # the two highest base-period quarters (confirmed by USDOL Comparison + # of State UI Laws). This is authoritative over the BRR handbook's + # simplified gloss and should not be "corrected" to match it. return (high_quarter_wages + second_high_quarter_wages) / WEEKS_IN_YEAR From 91399692f6f14c818f61b52efb934d33e55f9be2 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 4 Aug 2026 10:30:41 -0400 Subject: [PATCH 10/13] Address program review: duration brackets, ALUR data, citations, tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Correct duration_weeks brackets to the completed-increment reading of § 25-4-74(a) (14 + 1 week per completed 0.5pp above 6.5%, cap 20 at >= 9.5%); rewrite interior bracket tests to match - Replace state unemployment rate series with the FRED ALUR (seasonally adjusted) vintage, truncated at last published month; document the January-value approximation of the statutory prior-Q3 average - Fix § 25-4-77 pin-cites to (a)(6); drop stale NELP citation and non-corroborating anchors; add backdating breadcrumbs - Narrow programs.yaml verified_years to 2024; document unmodeled provisions and microsim-inert defaults in al_ui.py - Add edge tests: UR floor, week clamp, MBA min crossover, half-to-even ties, negative wages, non-AL zero WBA, unrounded WBA assertions - Bump states-shard test batches 16 -> 20 to fix runner OOM in CI Co-Authored-By: Claude Fable 5 --- .github/workflows/pr.yaml | 13 +- .../eligibility/bpw_to_hqw_multiplier.yaml | 4 +- .../eligibility/quarters_with_wages.yaml | 2 +- .../mba/duration_weeks.yaml | 14 +- .../partial/disregard_rate.yaml | 2 - .../state_unemployment_rate.yaml | 129 +++++++++--------- .../unemployment_insurance/waiting_weeks.yaml | 4 +- .../dol/unemployment_insurance/wba/max.yaml | 2 + policyengine_us/programs.yaml | 2 +- .../al/dol/unemployment_insurance/al_ui.yaml | 2 +- .../al_ui_max_weeks.yaml | 79 ++++++++--- .../al_ui_maximum_benefit_amount.yaml | 55 ++++++++ .../al_ui_monetarily_eligible.yaml | 12 +- .../al_ui_partial_weekly_benefit.yaml | 53 +++++++ .../al_ui_weekly_benefit_amount.yaml | 11 +- .../unemployment_insurance/integration.yaml | 41 +++++- .../al/dol/unemployment_insurance/al_ui.py | 13 ++ .../unemployment_insurance/al_ui_max_weeks.py | 12 +- .../al_ui_maximum_benefit_amount.py | 16 ++- .../al_ui_partial_weekly_benefit.py | 1 - .../al_ui_unrounded_wba.py | 10 +- .../al_ui_weekly_earnings.py | 3 + 22 files changed, 352 insertions(+), 128 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 042dbecf8dd..5edde3855b2 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -214,16 +214,17 @@ jobs: fail-fast: false matrix: include: - # 16 batches / 4 shards / --workers 1: the 8-batch 2-wide shards - # OOM'd on run 28698452678; see the Makefile memory-layout notes. + # 20 batches / 4 shards / --workers 1: smaller per-batch memory + # footprint. The prior 16-batch layout OOM'd the runner (not a test + # failure) per the Jul 30 diagnosis; see the Makefile memory-layout notes. - group: states-shard-1 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 1/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 1/4 - group: states-shard-2 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 2/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 2/4 - group: states-shard-3 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 3/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 3/4 - group: states-shard-4 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 4/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 4/4 - group: irs cmd: make test-yaml-no-structural-other-irs - group: household diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml index 372f9c78a0b..72bbafc7e09 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/bpw_to_hqw_multiplier.yaml @@ -8,9 +8,7 @@ metadata: period: year label: Alabama UI base period wages to high quarter wages multiplier reference: - - title: Code of Alabama § 25-4-77(a)(4)(a) + - title: Code of Alabama § 25-4-77(a)(6) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ - - title: Alabama UC Benefit Rights and Responsibilities Handbook - href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=8 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-2 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml index f03c67c989d..19a50ed5e2d 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/eligibility/quarters_with_wages.yaml @@ -8,7 +8,7 @@ metadata: period: year label: Alabama UI required quarters with wages reference: - - title: Code of Alabama § 25-4-77(a)(4) + - title: Code of Alabama § 25-4-77(a)(6) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-77/ - title: Alabama UC Benefit Rights and Responsibilities Handbook href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=7 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml index 218ec08bdb7..acc815e8422 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/mba/duration_weeks.yaml @@ -6,27 +6,27 @@ brackets: amount: 2020-01-01: 14 - threshold: - 2020-01-01: 0.0651 # Above 6.5% + 2020-01-01: 0.07 # >= 7.0% — 1 completed 0.5pp increment above 6.5% amount: 2020-01-01: 15 - threshold: - 2020-01-01: 0.0701 # Above 7.0% + 2020-01-01: 0.075 # >= 7.5% — 2 completed 0.5pp increments above 6.5% amount: 2020-01-01: 16 - threshold: - 2020-01-01: 0.0751 # Above 7.5% + 2020-01-01: 0.08 # >= 8.0% — 3 completed 0.5pp increments above 6.5% amount: 2020-01-01: 17 - threshold: - 2020-01-01: 0.0801 # Above 8.0% + 2020-01-01: 0.085 # >= 8.5% — 4 completed 0.5pp increments above 6.5% amount: 2020-01-01: 18 - threshold: - 2020-01-01: 0.0851 # Above 8.5% + 2020-01-01: 0.09 # >= 9.0% — 5 completed 0.5pp increments above 6.5% amount: 2020-01-01: 19 - threshold: - 2020-01-01: 0.0901 # Above 9.0% (statute caps at 20 weeks for AAU >= 9.5%) + 2020-01-01: 0.095 # >= 9.5% — 20-week cap per § 25-4-74(a): 14 weeks + 1 per completed 0.5pp above 6.5% amount: 2020-01-01: 20 @@ -39,7 +39,5 @@ metadata: reference: - title: Code of Alabama § 25-4-74(a) href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-74/ - - title: Alabama Act 2019-204 summary - href: https://alabamaretail.org/news/unemployment-comp-weeks-weekly-benefits/ - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-12 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=29 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml index 20d3cfa4864..9fe52d8f60d 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/partial/disregard_rate.yaml @@ -12,5 +12,3 @@ metadata: href: https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-73/ - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-8 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20 - - title: NELP — New Alabama Unemployment Insurance Law Makes Work Pay - href: https://www.nelp.org/new-alabama-unemployment-insurance-law-makes-work-pay/ diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml index e7057317fca..4973c8e02ce 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/state_unemployment_rate.yaml @@ -1,89 +1,96 @@ -description: Alabama uses this seasonally-adjusted statewide average unemployment rate under the Unemployment Compensation program. +description: Alabama sets this seasonally-adjusted statewide average unemployment rate under the Unemployment Compensation program. +# The statutory measure (§ 25-4-74(d)) is the seasonally-adjusted statewide +# average unemployment rate for the three months of the most recently completed +# calendar quarter ending before the benefit year; this parameter approximates it +# with the benefit year's January value. Values past the end of the FRED series +# are carried forward by PolicyEngine. values: - 2020-01-01: 0.028 - 2020-02-01: 0.030 - 2020-03-01: 0.031 + 2020-01-01: 0.032 + 2020-02-01: 0.033 + 2020-03-01: 0.034 2020-04-01: 0.138 - 2020-05-01: 0.111 - 2020-06-01: 0.087 - 2020-07-01: 0.079 - 2020-08-01: 0.067 + 2020-05-01: 0.104 + 2020-06-01: 0.086 + 2020-07-01: 0.075 + 2020-08-01: 0.063 2020-09-01: 0.059 - 2020-10-01: 0.045 - 2020-11-01: 0.041 - 2020-12-01: 0.038 - 2021-01-01: 0.036 - 2021-02-01: 0.034 - 2021-03-01: 0.033 - 2021-04-01: 0.032 - 2021-05-01: 0.031 - 2021-06-01: 0.030 - 2021-07-01: 0.029 - 2021-08-01: 0.029 - 2021-09-01: 0.029 - 2021-10-01: 0.030 - 2021-11-01: 0.030 - 2021-12-01: 0.030 - 2022-01-01: 0.029 - 2022-02-01: 0.028 - 2022-03-01: 0.027 + 2020-10-01: 0.052 + 2020-11-01: 0.048 + 2020-12-01: 0.045 + 2021-01-01: 0.042 + 2021-02-01: 0.039 + 2021-03-01: 0.038 + 2021-04-01: 0.037 + 2021-05-01: 0.036 + 2021-06-01: 0.035 + 2021-07-01: 0.034 + 2021-08-01: 0.032 + 2021-09-01: 0.030 + 2021-10-01: 0.029 + 2021-11-01: 0.028 + 2021-12-01: 0.027 + 2022-01-01: 0.026 + 2022-02-01: 0.026 + 2022-03-01: 0.026 2022-04-01: 0.026 2022-05-01: 0.026 2022-06-01: 0.026 2022-07-01: 0.026 2022-08-01: 0.026 - 2022-09-01: 0.026 - 2022-10-01: 0.026 - 2022-11-01: 0.026 - 2022-12-01: 0.026 - 2023-01-01: 0.025 - 2023-02-01: 0.024 + 2022-09-01: 0.025 + 2022-10-01: 0.025 + 2022-11-01: 0.024 + 2022-12-01: 0.023 + 2023-01-01: 0.023 + 2023-02-01: 0.023 2023-03-01: 0.023 2023-04-01: 0.023 2023-05-01: 0.023 - 2023-06-01: 0.023 + 2023-06-01: 0.024 2023-07-01: 0.024 2023-08-01: 0.025 - 2023-09-01: 0.025 - 2023-10-01: 0.026 - 2023-11-01: 0.027 + 2023-09-01: 0.026 + 2023-10-01: 0.027 + 2023-11-01: 0.028 2023-12-01: 0.028 2024-01-01: 0.029 2024-02-01: 0.029 2024-03-01: 0.029 2024-04-01: 0.029 - 2024-05-01: 0.030 - 2024-06-01: 0.030 - 2024-07-01: 0.031 - 2024-08-01: 0.032 - 2024-09-01: 0.032 - 2024-10-01: 0.032 - 2024-11-01: 0.033 - 2024-12-01: 0.033 - 2025-01-01: 0.033 - 2025-02-01: 0.033 - 2025-03-01: 0.033 - 2025-04-01: 0.033 - 2025-05-01: 0.033 - 2025-06-01: 0.034 - 2025-07-01: 0.034 - 2025-08-01: 0.035 - 2025-09-01: 0.035 - 2025-10-01: 0.035 - 2025-11-01: 0.035 - 2025-12-01: 0.035 - 2026-01-01: 0.035 - 2026-02-01: 0.035 - 2026-03-01: 0.035 - 2026-04-01: 0.035 + 2024-05-01: 0.029 + 2024-06-01: 0.029 + 2024-07-01: 0.030 + 2024-08-01: 0.030 + 2024-09-01: 0.031 + 2024-10-01: 0.031 + 2024-11-01: 0.031 + 2024-12-01: 0.031 + 2025-01-01: 0.031 + 2025-02-01: 0.030 + 2025-03-01: 0.030 + 2025-04-01: 0.029 + 2025-05-01: 0.029 + 2025-06-01: 0.029 + 2025-07-01: 0.028 + 2025-08-01: 0.028 + 2025-09-01: 0.028 + 2025-10-01: 0.028 # FRED value missing for this month; prior value carried forward + 2025-11-01: 0.027 + 2025-12-01: 0.027 + 2026-01-01: 0.027 + 2026-02-01: 0.027 + 2026-03-01: 0.027 + 2026-04-01: 0.028 + 2026-05-01: 0.030 + 2026-06-01: 0.032 metadata: unit: /1 period: month label: Alabama UI state unemployment rate reference: - - title: FRED — Unemployment Rate in Alabama (ALUR) + - title: FRED — Unemployment Rate in Alabama (ALUR, seasonally adjusted), retrieved 2026-08-03 href: https://fred.stlouisfed.org/series/ALUR - title: U.S. Bureau of Labor Statistics — Local Area Unemployment Statistics (LAUS) href: https://www.bls.gov/lau/ diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml index 6dc5b98abec..8fe50d2f732 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/waiting_weeks.yaml @@ -1,6 +1,8 @@ description: Alabama excludes this many initial weeks from compensability under the Unemployment Compensation program. values: + # The one-week waiting period applies to claims filed on or after 2012-08-01; + # a backdating pass covering earlier claims must add a 0-week tier before then. 2020-01-01: 1 metadata: @@ -10,7 +12,7 @@ metadata: reference: - title: Alabama UC Benefit Rights and Responsibilities Handbook href: https://labor.alabama.gov/docs/guides/uc_brr.pdf#page=9 - - title: Ala. Admin. Code Chapter 480-4-3 — Benefits + - title: Ala. Admin. Code Chapter 480-4-3 — Benefits (waiting-week rule) href: https://admincode.legislature.state.al.us/administrative-code/480-4-3 - title: U.S. DOL Comparison of State UI Laws 2023, Table 3-7 href: https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=17 diff --git a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml index 712a669cb7d..294a3b3e7cd 100644 --- a/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml +++ b/policyengine_us/parameters/gov/states/al/dol/unemployment_insurance/wba/max.yaml @@ -1,6 +1,8 @@ description: Alabama provides this amount as the maximum weekly benefit under the Unemployment Compensation program. values: + # The maximum weekly benefit was $265 before 2020-01-01 (raised to $275 by + # Act 2019-204); a backdating pass must add the earlier $265 (and prior) tier. 2020-01-01: 275 metadata: diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index ef1cdc13f7b..8b4b26f9595 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1110,7 +1110,7 @@ programs: coverage: AL variable: al_ui parameter_prefix: gov.states.al.dol.unemployment_insurance - verified_years: "2020-2025" + verified_years: "2024" - id: co_oap name: Colorado OAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml index e90f2233025..95fb545ee5d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui.yaml @@ -214,7 +214,7 @@ output: people: person1: - # BPW 0 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(4)(a). + # BPW 0 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(6). al_ui_monetarily_eligible: false al_ui: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml index 9be41574e7a..1eb3077c68f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml @@ -1,8 +1,13 @@ # Tests for Alabama UI maximum benefit weeks (Code of Alabama § 25-4-74(a)). -# Bracket behavior is covered at the statutory floor, first increment, every -# middle bracket boundary (just-below/just-at pairs for the 16-, 17-, 18-, 19-, -# and 20-week thresholds), and the cap. Other benefit-flow tests exercise the -# natural 2024 ALUR baseline. +# Weeks = 14 + one week per completed 0.5-percentage-point increment of the +# state unemployment rate above 6.5%, capped at 20 weeks once the rate reaches +# 9.5%. The single_amount bracket lower bounds give >= semantics, so each week +# tier begins exactly at its threshold (>= 6.5% -> 14, >= 7.0% -> 15, ..., +# >= 9.5% -> 20) and holds until the next 0.5pp threshold is reached. +# Bracket behavior is covered at the statutory floor, each threshold, and a +# just-below point inside each interior tier (confirming the increment only +# advances at completed 0.5pp steps), plus the cap. Other benefit-flow tests +# exercise the natural 2024 ALUR baseline. - name: Case 1, UR at 6.5 percent gets 14 weeks. period: 2024 @@ -20,10 +25,10 @@ person1: al_ui_max_weeks: 14 -- name: Case 2, UR just above 6.5 percent gets 15 weeks. +- name: Case 2, UR 6.6 percent (below the 7.0 percent step) stays at 14 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0651 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.066 people: person1: age: 30 @@ -34,7 +39,9 @@ output: people: person1: - al_ui_max_weeks: 15 + # 6.6% is above 6.5% but has not completed the first 0.5pp step + # (7.0%), so weeks remain at the 14-week floor. + al_ui_max_weeks: 14 - name: Case 3, UR at 9.0 percent gets 19 weeks. period: 2024 @@ -84,10 +91,10 @@ person1: al_ui_max_weeks: 15 -- name: Case 6, UR just above 7.0 percent gets 16 weeks. +- name: Case 6, UR 7.4 percent (below the 7.5 percent step) stays at 15 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0701 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.074 people: person1: age: 30 @@ -98,7 +105,9 @@ output: people: person1: - al_ui_max_weeks: 16 + # 7.4% has completed one 0.5pp step (>= 7.0%) but not the next + # (7.5%), so weeks stay at 15. + al_ui_max_weeks: 15 - name: Case 7, UR at 7.5 percent stays at 16 weeks. period: 2024 @@ -116,10 +125,10 @@ person1: al_ui_max_weeks: 16 -- name: Case 8, UR just above 7.5 percent gets 17 weeks. +- name: Case 8, UR 7.9 percent (below the 8.0 percent step) stays at 16 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0751 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.079 people: person1: age: 30 @@ -130,7 +139,9 @@ output: people: person1: - al_ui_max_weeks: 17 + # 7.9% has completed two 0.5pp steps (>= 7.5%) but not the next + # (8.0%), so weeks stay at 16. + al_ui_max_weeks: 16 - name: Case 9, UR at 8.0 percent stays at 17 weeks. period: 2024 @@ -148,10 +159,10 @@ person1: al_ui_max_weeks: 17 -- name: Case 10, UR just above 8.0 percent gets 18 weeks. +- name: Case 10, UR 8.4 percent (below the 8.5 percent step) stays at 17 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0801 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.084 people: person1: age: 30 @@ -162,7 +173,9 @@ output: people: person1: - al_ui_max_weeks: 18 + # 8.4% has completed three 0.5pp steps (>= 8.0%) but not the next + # (8.5%), so weeks stay at 17. + al_ui_max_weeks: 17 - name: Case 11, UR at 8.5 percent stays at 18 weeks. period: 2024 @@ -180,10 +193,10 @@ person1: al_ui_max_weeks: 18 -- name: Case 12, UR just above 8.5 percent gets 19 weeks. +- name: Case 12, UR 8.9 percent (below the 9.0 percent step) stays at 18 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0851 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.089 people: person1: age: 30 @@ -194,12 +207,14 @@ output: people: person1: - al_ui_max_weeks: 19 + # 8.9% has completed four 0.5pp steps (>= 8.5%) but not the next + # (9.0%), so weeks stay at 18. + al_ui_max_weeks: 18 -- name: Case 13, UR just above 9.0 percent gets 20 weeks. +- name: Case 13, UR 9.4 percent (below the 9.5 percent cap) stays at 19 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0901 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.094 people: person1: age: 30 @@ -210,7 +225,9 @@ output: people: person1: - al_ui_max_weeks: 20 + # 9.4% has completed five 0.5pp steps (>= 9.0%) but not the 9.5% + # step that reaches the 20-week cap, so weeks stay at 19. + al_ui_max_weeks: 19 - name: Case 14, natural 2024 ALUR baseline gets 14 weeks. period: 2024 @@ -226,3 +243,21 @@ people: person1: al_ui_max_weeks: 14 + +- name: Case 15, UR 0.0 percent - explicit floor stays at 14 weeks. + period: 2024 + input: + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0 + people: + person1: + age: 30 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + # 0.0% is below the 6.5% first bracket lower bound, so the schedule + # returns the 14-week floor (no completed 0.5pp steps above 6.5%). + al_ui_max_weeks: 14 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml index 7990b89d444..0683b1c21d9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.yaml @@ -240,3 +240,58 @@ al_ui_monetarily_eligible: false al_ui_weekly_benefit_amount: 0 al_ui_maximum_benefit_amount: 0 + +- name: Case 10, BPW cap 0.25 x BPW at an exact .50 tie - half-to-even rounds DOWN. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 100 (unrounded = 5,226 / 52 = 100.50 -> half-down -> 100). + # 14 * 100 = 1,400. + # BPW = 5,002 -> 0.25 * 5,002 = 1,250.50 exactly. + # BPW passes 1.5x test: 5,002 >= 1.5 * 2,613 = 3,919.5. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_002 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # min(1,400, 1,250.50) = 1,250.50. np.round uses half-to-even, and + # 1,250 is even, so 1,250.50 -> 1,250 (rounds DOWN at the tie). + al_ui_maximum_benefit_amount: 1_250 + +- name: Case 11, min_ crossover - N x WBA one dollar below the BPW cap. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 100 (5,226 / 52 = 100.50 -> half-down -> 100). 14 * 100 = 1,400. + # BPW = 5,604 -> 0.25 * 5,604 = 1,401 (integer, no rounding). + # BPW passes 1.5x test: 5,604 >= 1.5 * 2,613 = 3,919.5. + al_ui_high_quarter_wages: 2_613 + al_ui_second_high_quarter_wages: 2_613 + al_ui_base_period_wages: 5_604 + al_ui_quarters_with_wages: 4 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_max_weeks: 14 + al_ui_weekly_benefit_amount: 100 + # min(14 * 100, 0.25 * 5,604) = min(1,400, 1,401) = 1,400. + # N x WBA binds, exactly $1 below the BPW cap (just-below direction). + al_ui_maximum_benefit_amount: 1_400 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml index 0f6ca8a0e13..47add55069a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_monetarily_eligible.yaml @@ -1,9 +1,9 @@ -# Tests for Alabama UI monetary eligibility (§§ 25-4-77(a)(4), 25-4-72(b)(2)). +# Tests for Alabama UI monetary eligibility (§§ 25-4-77(a)(6), 25-4-72(b)(2)). # A claimant is monetarily eligible only if ALL THREE tests pass: # 1. Wages paid in at least 2 calendar quarters of the base period -# (§ 25-4-77(a)(4)). +# (§ 25-4-77(a)(6)). # 2. Total base-period wages >= 1.5 x high-quarter wages -# (§ 25-4-77(a)(4)(a)). +# (§ 25-4-77(a)(6)). # 3. Unrounded WBA = (HQW + 2HQW) / 52 must exceed $44.50 # (§ 25-4-72(b)(2)). # References: @@ -48,7 +48,7 @@ output: people: person1: - # Only 1 quarter < 2 required -> fails § 25-4-77(a)(4). + # Only 1 quarter < 2 required -> fails § 25-4-77(a)(6). al_ui_monetarily_eligible: false - name: Case 3, fails 1.5x base-wages test. @@ -69,7 +69,7 @@ output: people: person1: - # BPW 10,000 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(4)(a). + # BPW 10,000 < 1.5 * 8,000 = 12,000 -> fails § 25-4-77(a)(6). al_ui_monetarily_eligible: false - name: Case 4, fails unrounded-WBA floor - unrounded WBA below $44.50. @@ -176,5 +176,5 @@ output: people: person1: - # BPW 5,999 < 1.5 * 4,000 = 6,000 -> fails § 25-4-77(a)(4)(a). + # BPW 5,999 < 1.5 * 4,000 = 6,000 -> fails § 25-4-77(a)(6). al_ui_monetarily_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml index 95b4c7b571e..ef933398c80 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.yaml @@ -250,3 +250,56 @@ al_ui_weekly_benefit_amount: 275 # WBA/3 = 91.6667; 275 - (274.99 - 91.6667) = 91.6767 -> rounded to $92. al_ui_partial_weekly_benefit: 92 + +- name: Case 11, partial benefit at an exact .50 tie - half-to-even rounds DOWN. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # WBA = 273 exactly: (7,098 + 7,098) / 52 = 14,196 / 52 = 273.00. + # Chosen divisible by 3 so the 1/3 disregard is a clean 91. + al_ui_high_quarter_wages: 7_098 + al_ui_second_high_quarter_wages: 7_098 + al_ui_base_period_wages: 28_392 + al_ui_quarters_with_wages: 4 + # disregard = 273 / 3 = 91; earnings 147.50 -> countable = 56.50; + # partial = 273 - 56.50 = 216.50 exactly. + al_ui_weekly_earnings: 147.50 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_weekly_benefit_amount: 273 + # np.round(216.50) uses half-to-even; 216 is even, so 216.50 -> 216. + al_ui_partial_weekly_benefit: 216 + +- name: Case 12, negative wage inputs (defensive) - ineligible, $0 partial. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + # Defensive: negative reported wages must not produce a benefit. + # Unrounded WBA = (-2,000 + -2,000) / 52 = -76.92 < 44.50 -> the WBA + # floor fails -> not monetarily eligible -> WBA = 0 -> partial = 0. + al_ui_high_quarter_wages: -2_000 + al_ui_second_high_quarter_wages: -2_000 + al_ui_base_period_wages: -8_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: false + al_ui_weekly_benefit_amount: 0 + al_ui_partial_weekly_benefit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml index aac22a7b278..108229ad942 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_weekly_benefit_amount.yaml @@ -28,6 +28,7 @@ person1: # Unrounded = (1,157 + 1,157) / 52 = 44.50 -> "not in excess of $44.50" # -> WBA = 0 per § 25-4-72(b)(2). + al_ui_unrounded_wba: 44.50 al_ui_weekly_benefit_amount: 0 - name: Case 2, unrounded WBA just above $44.50 - rounded to nearest dollar. @@ -50,6 +51,7 @@ person1: # Unrounded = (1,200 + 1,200) / 52 = 46.1538... # Fractional 0.1538 < 0.50 -> rounds DOWN to 46. + al_ui_unrounded_wba: 46.15 al_ui_weekly_benefit_amount: 46 - name: Case 3, WBA exactly at $275 cap - HQW + 2HQW = $14,300. @@ -117,6 +119,7 @@ # § 25-4-72(b)(1): "fractional parts ... which are fifty cents ($.50) # or less shall be dropped to the next lower multiple of one dollar" # -> WBA = 100 (rounds DOWN at the tie). + al_ui_unrounded_wba: 100.50 al_ui_weekly_benefit_amount: 100 - name: Case 6, AL rounding strictly above $0.50 rounds UP - unrounded $100.51 -> $101. @@ -141,6 +144,7 @@ person1: # Unrounded = (2,614 + 2,613) / 52 = 5,227 / 52 = 100.5192. # Fractional 0.5192 > 0.50 -> rounds UP to 101. + al_ui_unrounded_wba: 100.52 al_ui_weekly_benefit_amount: 101 - name: Case 7, monetarily ineligible - only 1 quarter with wages - WBA is $0. @@ -162,7 +166,7 @@ people: person1: # Although the unrounded WBA (10,000 / 52 = 192.31) exceeds 44.50, - # the claimant fails § 25-4-77(a)(4) (only 1 quarter < 2 required) + # the claimant fails § 25-4-77(a)(6) (only 1 quarter < 2 required) # -> not monetarily eligible -> WBA = 0. al_ui_weekly_benefit_amount: 0 @@ -185,7 +189,7 @@ output: people: person1: - # Fails § 25-4-77(a)(4)(a) -> monetarily ineligible -> WBA = 0. + # Fails § 25-4-77(a)(6) -> monetarily ineligible -> WBA = 0. al_ui_weekly_benefit_amount: 0 - name: Case 9, zero wages everywhere - no inputs - WBA is $0. @@ -232,6 +236,7 @@ person1: # Fractional 0.5192 > 0.50 -> rounds UP to 45 per § 25-4-72(b)(1). al_ui_monetarily_eligible: true + al_ui_unrounded_wba: 44.52 al_ui_weekly_benefit_amount: 45 - name: Case 11, unrounded WBA just above $275 ($275.48) - capped at $275. @@ -256,6 +261,8 @@ output: people: person1: + # Unrounded = 14,325 / 52 = 275.48; rounds to 275, cap 275 -> 275. + al_ui_unrounded_wba: 275.48 al_ui_weekly_benefit_amount: 275 - name: Case 12, AL rounding ties round DOWN at $1.50 -> $1 (sub-threshold ineligible). diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml index da0a6a802d5..8d2581e2aae 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/integration.yaml @@ -100,7 +100,7 @@ output: people: person1: - # Only 1 quarter < 2 required -> ineligible per § 25-4-77(a)(4). + # Only 1 quarter < 2 required -> ineligible per § 25-4-77(a)(6). al_ui_monetarily_eligible: false al_ui_weekly_benefit_amount: 0 al_ui_maximum_benefit_amount: 0 @@ -128,7 +128,7 @@ output: people: person1: - # Fails § 25-4-77(a)(4)(a) -> ineligible. + # Fails § 25-4-77(a)(6) -> ineligible. al_ui_monetarily_eligible: false al_ui_weekly_benefit_amount: 0 al_ui: 0 @@ -307,7 +307,9 @@ output: people: person1: - # Non-AL state -> defined_for filter applies -> al_ui = 0. + # Non-AL state -> defined_for filter applies -> every AL UI variable + # returns 0 for this person. + al_ui_weekly_benefit_amount: 0 al_ui: 0 - name: Case 11, waiting week reduces a 14-week claim. @@ -332,3 +334,36 @@ person1: # al_ui = 275 * max(0, 14 - 1) = 3,575. al_ui: 3_575 + +- name: Case 12, long claim - upper week clamp binds at max_weeks. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 35 + al_ui_high_quarter_wages: 10_000 + al_ui_second_high_quarter_wages: 10_000 + al_ui_base_period_wages: 40_000 + al_ui_quarters_with_wages: 4 + al_ui_weekly_earnings: 0 + weeks_unemployed: 15 + households: + household: + members: [person1] + state_code: AL + output: + people: + person1: + al_ui_monetarily_eligible: true + al_ui_weekly_benefit_amount: 275 + # UR (2024-01) = 0.029 -> max_weeks = 14. + al_ui_max_weeks: 14 + # MBA = min(14 * 275, 0.25 * 40,000) = min(3,850, 10,000) = 3,850. + al_ui_maximum_benefit_amount: 3_850 + al_ui_partial_weekly_benefit: 275 + # payable_weeks = min(max(0, 15 - 1), 14) = min(14, 14) = 14. + # The 15 - 1 = 14 waiting-week reduction ties the 14-week upper clamp, + # so the clamp binds. Annual = 275 * 14 = 3,850 (equals MBA, so the + # MBA cap is not additionally binding here). + al_ui: 3_850 diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py index 6d264913503..b2d3dbe528f 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui.py @@ -14,6 +14,19 @@ class al_ui(Variable): ) defined_for = "al_ui_monetarily_eligible" + # Not modeled: the 5 additional weeks of benefits available during approved + # training (Code of Ala. § 25-4-74(f)); non-monetary eligibility conditions + # (able and available for work, active work search, and separation + # qualification); employer experience rating; and combined-wage claims + # (wages earned in other states). Only monetary eligibility and the regular + # benefit amount are captured here. + # + # Microsim limitation: the five wage/quarter inputs feeding this program + # (base-period and quarterly wages, weekly earnings, and weeks unemployed) + # default to 0 and are not imputed in the national dataset, so the program + # is inert in dataset runs and produces nonzero benefits only when those + # inputs are supplied through the household calculator. + def formula(person, period, parameters): p = parameters(period).gov.states.al.dol.unemployment_insurance partial_weekly_benefit = person("al_ui_partial_weekly_benefit", period) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py index 209f909fd95..1c765af1d41 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.py @@ -13,8 +13,16 @@ class al_ui_max_weeks(Variable): ) defined_for = StateCode.AL + # Statute (Code of Ala. § 25-4-74(d)) sets the number of weeks off the + # seasonally adjusted 3-month average statewide unemployment rate for the + # calendar quarter ending immediately before the benefit year begins (the + # prior third calendar quarter). The model approximates this with the + # benefit year's January value of state_unemployment_rate, which is a + # monthly series returned at its January reading under a YEAR period. The + # two measures diverge materially only in the COVID window, when the rate + # moved sharply within a quarter; in ordinary years the January reading and + # the prior-quarter 3-month average land in the same duration bracket. + def formula(person, period, parameters): - # NOTE: state_unemployment_rate is monthly; at YEAR period the - # framework returns the January value of the benefit year. p = parameters(period).gov.states.al.dol.unemployment_insurance return p.mba.duration_weeks.calc(p.state_unemployment_rate) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py index a8dfa5d8b55..69a16be2432 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_maximum_benefit_amount.py @@ -13,15 +13,23 @@ class al_ui_maximum_benefit_amount(Variable): ) defined_for = StateCode.AL + # Statutory formula per Code of Ala. § 25-4-74(a): the maximum benefit + # amount is the lesser of max-weeks x WBA and one-fourth of base-period + # wages. This encoding follows the USDOL Comparison of State UI Laws, which + # is authoritative over the BRR handbook's simplified gloss; where the two + # disagree the statute/USDOL reading governs and should not be "corrected" + # to match the handbook. + def formula(person, period, parameters): p = parameters(period).gov.states.al.dol.unemployment_insurance wba = person("al_ui_weekly_benefit_amount", period) max_weeks = person("al_ui_max_weeks", period) base_period_wages = person("al_ui_base_period_wages", period) weeks_cap = max_weeks * wba - # Statutory formula per Code of Ala. § 25-4-74(a): the lesser of - # max-weeks x WBA and one-fourth of base-period wages (confirmed by - # USDOL Comparison of State UI Laws). This is authoritative over the - # BRR handbook's simplified gloss and should not be "corrected". bpw_cap = p.mba.bpw_fraction * base_period_wages + # np.round rounds to the nearest whole dollar, breaking exact .50 ties + # to the even dollar (banker's rounding). No statutory rounding + # provision for the maximum benefit amount was located (the § 25-4-72 + # / § 25-4-73 rounding rules govern the WBA and partial benefit only); + # this whole-dollar rounding is a modeling convenience. return np.round(min_(weeks_cap, bpw_cap)) diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py index 65ef23f2a01..a9ae1339280 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_partial_weekly_benefit.py @@ -10,7 +10,6 @@ class al_ui_partial_weekly_benefit(Variable): reference = ( "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-73/", "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", - "https://www.nelp.org/new-alabama-unemployment-insurance-law-makes-work-pay/", ) defined_for = StateCode.AL diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py index d775159cd2e..1f2ce66ff93 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_unrounded_wba.py @@ -10,11 +10,13 @@ class al_ui_unrounded_wba(Variable): reference = "https://law.justia.com/codes/alabama/title-25/chapter-4/article-4/section-25-4-72/" defined_for = StateCode.AL + # Statutory formula per Code of Ala. § 25-4-72(b): 1/52 of the sum of the + # two highest base-period quarters. This encoding follows the USDOL + # Comparison of State UI Laws, which is authoritative over the BRR + # handbook's simplified gloss; where the two disagree the statute/USDOL + # reading governs and should not be "corrected" to match the handbook. + def formula(person, period, parameters): high_quarter_wages = person("al_ui_high_quarter_wages", period) second_high_quarter_wages = person("al_ui_second_high_quarter_wages", period) - # Statutory formula per Code of Ala. § 25-4-72(b): 1/52 of the sum of - # the two highest base-period quarters (confirmed by USDOL Comparison - # of State UI Laws). This is authoritative over the BRR handbook's - # simplified gloss and should not be "corrected" to match it. return (high_quarter_wages + second_high_quarter_wages) / WEEKS_IN_YEAR diff --git a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py index de12d817afb..916b14c414e 100644 --- a/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py +++ b/policyengine_us/variables/gov/states/al/dol/unemployment_insurance/al_ui_weekly_earnings.py @@ -13,3 +13,6 @@ class al_ui_weekly_earnings(Variable): "https://oui.doleta.gov/unemploy/pdf/uilawcompar/2023/monetary.pdf#page=20", ) defined_for = StateCode.AL + # Despite the YEAR definition period, this input holds a single week's gross + # earnings during a partial unemployment week, not an annual total. It is + # assumed constant across every payable week of the unemployment spell. From 1de61b33244428186aa252af78563e754faea4c2 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 4 Aug 2026 13:15:41 -0400 Subject: [PATCH 11/13] Bump states-shard test batches to 24 after runner shutdown at 20 Co-Authored-By: Claude Fable 5 --- .github/workflows/pr.yaml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 5edde3855b2..acdc1a61c27 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -214,17 +214,19 @@ jobs: fail-fast: false matrix: include: - # 20 batches / 4 shards / --workers 1: smaller per-batch memory + # 24 batches / 4 shards / --workers 1: smaller per-batch memory # footprint. The prior 16-batch layout OOM'd the runner (not a test - # failure) per the Jul 30 diagnosis; see the Makefile memory-layout notes. + # failure) per the Jul 30 diagnosis, and 20 batches still hit a + # runner shutdown (exit 143) on states-shard-1; see the Makefile + # memory-layout notes. - group: states-shard-1 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 1/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 1/4 - group: states-shard-2 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 2/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 2/4 - group: states-shard-3 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 3/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 3/4 - group: states-shard-4 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 20 --workers 1 --shard 4/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 4/4 - group: irs cmd: make test-yaml-no-structural-other-irs - group: household From 5f570b010d9d94b10530c440850482ec3ba2b6a4 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 4 Aug 2026 14:01:43 -0400 Subject: [PATCH 12/13] Bump states-shard test batches to 32; 24 still stalled out shard 1 Co-Authored-By: Claude Fable 5 --- .github/workflows/pr.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index acdc1a61c27..9960086d290 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -214,19 +214,19 @@ jobs: fail-fast: false matrix: include: - # 24 batches / 4 shards / --workers 1: smaller per-batch memory + # 32 batches / 4 shards / --workers 1: smaller per-batch memory # footprint. The prior 16-batch layout OOM'd the runner (not a test - # failure) per the Jul 30 diagnosis, and 20 batches still hit a - # runner shutdown (exit 143) on states-shard-1; see the Makefile - # memory-layout notes. + # failure) per the Jul 30 diagnosis, and 20/24 batches still hit + # runner kills (exit 143/cancel after progressive stalls) on states-shard-1; + # see the Makefile memory-layout notes. - group: states-shard-1 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 1/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 1/4 - group: states-shard-2 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 2/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 2/4 - group: states-shard-3 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 3/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 3/4 - group: states-shard-4 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 24 --workers 1 --shard 4/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 4/4 - group: irs cmd: make test-yaml-no-structural-other-irs - group: household From d1d446b32c22cec9bda04d346f352a51f9abe098 Mon Sep 17 00:00:00 2001 From: David Trimmer Date: Tue, 4 Aug 2026 14:39:17 -0400 Subject: [PATCH 13/13] Trim unemployment-rate override sweep that OOM-killed states-shard-1 Each distinct dotted-path parameter override deep-copies and caches a full tax-benefit system for the whole test subprocess, and states batching runs all AL files in one subprocess at every batch count. The 14-value state_unemployment_rate sweep in al_ui_max_weeks.yaml (~6.5+ GB resident) deterministically killed the runner at 16, 20, 24, and 32 batches. Keep four probes (floor threshold, completed-increment interior, exact step, cap) plus the no-override baseline, and restore the workflow's original 16-batch layout since the batching bumps were addressing a symptom. Co-Authored-By: Claude Fable 5 --- .github/workflows/pr.yaml | 15 +- .../al_ui_max_weeks.yaml | 201 ++---------------- 2 files changed, 25 insertions(+), 191 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 9960086d290..042dbecf8dd 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -214,19 +214,16 @@ jobs: fail-fast: false matrix: include: - # 32 batches / 4 shards / --workers 1: smaller per-batch memory - # footprint. The prior 16-batch layout OOM'd the runner (not a test - # failure) per the Jul 30 diagnosis, and 20/24 batches still hit - # runner kills (exit 143/cancel after progressive stalls) on states-shard-1; - # see the Makefile memory-layout notes. + # 16 batches / 4 shards / --workers 1: the 8-batch 2-wide shards + # OOM'd on run 28698452678; see the Makefile memory-layout notes. - group: states-shard-1 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 1/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 1/4 - group: states-shard-2 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 2/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 2/4 - group: states-shard-3 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 3/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 3/4 - group: states-shard-4 - cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 32 --workers 1 --shard 4/4 + cmd: python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/states --batches 16 --workers 1 --shard 4/4 - group: irs cmd: make test-yaml-no-structural-other-irs - group: household diff --git a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml index 1eb3077c68f..01dd3c9f0eb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/al/dol/unemployment_insurance/al_ui_max_weeks.yaml @@ -4,10 +4,15 @@ # 9.5%. The single_amount bracket lower bounds give >= semantics, so each week # tier begins exactly at its threshold (>= 6.5% -> 14, >= 7.0% -> 15, ..., # >= 9.5% -> 20) and holds until the next 0.5pp threshold is reached. -# Bracket behavior is covered at the statutory floor, each threshold, and a -# just-below point inside each interior tier (confirming the increment only -# advances at completed 0.5pp steps), plus the cap. Other benefit-flow tests -# exercise the natural 2024 ALUR baseline. +# +# MEMORY NOTE: each DISTINCT dotted-path parameter override below makes +# policyengine-core deep-copy and cache a full tax-benefit system for the +# whole test process (all AL tests share one CI subprocess). Keep the number +# of distinct state_unemployment_rate override values at or below FOUR — +# a 14-value sweep of this file OOM-killed the states-shard-1 runner at every +# batch layout. The four probes below pin the schedule's semantics (floor +# threshold, completed-increment interior, exact interior step, cap); the +# bracket table itself is source-verified parameter data. - name: Case 1, UR at 6.5 percent gets 14 weeks. period: 2024 @@ -25,10 +30,10 @@ person1: al_ui_max_weeks: 14 -- name: Case 2, UR 6.6 percent (below the 7.0 percent step) stays at 14 weeks. +- name: Case 2, UR 7.4 percent (one completed step, below the 7.5 step) gets 15 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.066 + gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.074 people: person1: age: 30 @@ -39,9 +44,11 @@ output: people: person1: - # 6.6% is above 6.5% but has not completed the first 0.5pp step - # (7.0%), so weeks remain at the 14-week floor. - al_ui_max_weeks: 14 + # 7.4% has completed exactly one 0.5pp step above 6.5% (>= 7.0%) but + # not the next (7.5%), so weeks are 15 — pinning the + # completed-increment reading against the over-granting + # just-past-each-half-point interpretation. + al_ui_max_weeks: 15 - name: Case 3, UR at 9.0 percent gets 19 weeks. period: 2024 @@ -75,179 +82,9 @@ person1: al_ui_max_weeks: 20 -- name: Case 5, UR at 7.0 percent stays at 15 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.07 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - al_ui_max_weeks: 15 - -- name: Case 6, UR 7.4 percent (below the 7.5 percent step) stays at 15 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.074 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 7.4% has completed one 0.5pp step (>= 7.0%) but not the next - # (7.5%), so weeks stay at 15. - al_ui_max_weeks: 15 - -- name: Case 7, UR at 7.5 percent stays at 16 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.075 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - al_ui_max_weeks: 16 - -- name: Case 8, UR 7.9 percent (below the 8.0 percent step) stays at 16 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.079 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 7.9% has completed two 0.5pp steps (>= 7.5%) but not the next - # (8.0%), so weeks stay at 16. - al_ui_max_weeks: 16 - -- name: Case 9, UR at 8.0 percent stays at 17 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.08 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - al_ui_max_weeks: 17 - -- name: Case 10, UR 8.4 percent (below the 8.5 percent step) stays at 17 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.084 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 8.4% has completed three 0.5pp steps (>= 8.0%) but not the next - # (8.5%), so weeks stay at 17. - al_ui_max_weeks: 17 - -- name: Case 11, UR at 8.5 percent stays at 18 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.085 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - al_ui_max_weeks: 18 - -- name: Case 12, UR 8.9 percent (below the 9.0 percent step) stays at 18 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.089 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 8.9% has completed four 0.5pp steps (>= 8.5%) but not the next - # (9.0%), so weeks stay at 18. - al_ui_max_weeks: 18 - -- name: Case 13, UR 9.4 percent (below the 9.5 percent cap) stays at 19 weeks. - period: 2024 - input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.094 - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - # 9.4% has completed five 0.5pp steps (>= 9.0%) but not the 9.5% - # step that reaches the 20-week cap, so weeks stay at 19. - al_ui_max_weeks: 19 - -- name: Case 14, natural 2024 ALUR baseline gets 14 weeks. - period: 2024 - input: - people: - person1: - age: 30 - households: - household: - members: [person1] - state_code: AL - output: - people: - person1: - al_ui_max_weeks: 14 - -- name: Case 15, UR 0.0 percent - explicit floor stays at 14 weeks. +- name: Case 5, natural 2024 ALUR baseline gets 14 weeks. period: 2024 input: - gov.states.al.dol.unemployment_insurance.state_unemployment_rate: 0.0 people: person1: age: 30 @@ -258,6 +95,6 @@ output: people: person1: - # 0.0% is below the 6.5% first bracket lower bound, so the schedule - # returns the 14-week floor (no completed 0.5pp steps above 6.5%). + # No override: the January 2024 ALUR value (~2.9%) is far below 6.5%, + # so the schedule returns the 14-week floor. al_ui_max_weeks: 14