diff --git a/changelog.d/mi-ssp.added.md b/changelog.d/mi-ssp.added.md new file mode 100644 index 00000000000..34b602db9f5 --- /dev/null +++ b/changelog.d/mi-ssp.added.md @@ -0,0 +1 @@ +Added Michigan State Supplementary Payment (SSP) program. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 619bf682302..9d3249a8127 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -12,6 +12,8 @@ values: - ga_ssp # Massachusetts benefits - ma_state_supplement + # Michigan benefits + - mi_ssp # Colorado benefits - co_state_supplement - co_oap @@ -67,6 +69,8 @@ values: - ga_ssp # Massachusetts benefits - ma_state_supplement + # Michigan benefits + - mi_ssp # Colorado benefits - co_state_supplement - co_oap diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/couple.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/couple.yaml new file mode 100644 index 00000000000..fafbaddee14 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/couple.yaml @@ -0,0 +1,55 @@ +description: Michigan provides this amount as the monthly couple payment standard under the State Supplementary Payment program. + +metadata: + unit: currency-USD + period: month + label: Michigan SSP couple payment + breakdown: + - mi_ssp_payment_category + reference: + - title: MDHHS RFT 248, SSI Payment Levels + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2 + - title: MDHHS BEM 660, State SSI Payment, Legal Base + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4 + - title: MCL 400.10, Social Welfare Act, supplementary payments + href: https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-400-10 + - title: SSA 2011 State Assistance Programs, Michigan baseline + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mi.html + - title: MDHHS RFT 248, SSI Payment Levels (RFB 2016-001 archive) + href: https://web.archive.org/web/20161223122049id_/https://dhhs.michigan.gov/OLMWEB/EX/RF/Public/RFT/248.pdf#page=2 + - title: MDHHS RFT 248, SSI Payment Levels (RFB 2020-001 archive) + href: https://web.archive.org/web/20200923083110id_/https://dhhs.michigan.gov/OLMWEB/EX/RF/Public/RFT/248.pdf#page=2 + - title: MDHHS RFT 248, SSI Payment Levels (RFB 2022-002 archive) + href: https://web.archive.org/web/20220309172236id_/https://dhhs.michigan.gov/OLMWEB/EX/RF/Public/RFT/248.pdf#page=2 + - title: MDHHS RFT 248, SSI Payment Levels (RFB 2024-001 archive) + href: https://web.archive.org/web/20240112012718id_/https://dhhs.michigan.gov/OLMWEB/EX/RF/Public/RFT/248.pdf#page=2 + +# Couple rates start at 2016-01-01. SSA 2011 baseline shows MI couple +# rates were restructured between 2011 and 2016 (e.g. IL couple was $28 +# in 2011, $21 from 2016 onward); pre-2016 history is not modeled. +INDEPENDENT_LIVING: + 2016-01-01: 21 +HOUSEHOLD_OF_ANOTHER: + 2016-01-01: 13.96 +DOMICILIARY_CARE: + 2016-01-01: 540 + 2020-01-01: 565 + 2022-01-01: 595 + 2024-01-01: 645 + 2026-01-01: 671 +PERSONAL_CARE: + 2016-01-01: 681 + 2020-01-01: 706 + 2022-01-01: 736 + 2024-01-01: 786 + 2026-01-01: 812 +HOME_FOR_AGED: + 2016-01-01: 724.6 + 2020-01-01: 749.6 + 2022-01-01: 779.6 + 2024-01-01: 829.6 + 2026-01-01: 855.6 +INSTITUTION: + 2016-01-01: 14 +NONE: + 2016-01-01: 0 diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/individual.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/individual.yaml new file mode 100644 index 00000000000..a25db30bcfc --- /dev/null +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/ssp/payment/individual.yaml @@ -0,0 +1,36 @@ +description: Michigan provides this amount as the monthly individual payment standard under the State Supplementary Payment program. + +metadata: + unit: currency-USD + period: month + label: Michigan SSP individual payment + breakdown: + - mi_ssp_payment_category + reference: + - title: MDHHS RFT 248, SSI Payment Levels + href: https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2 + - title: MDHHS BEM 660, State SSI Payment, Legal Base + href: https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4 + - title: MCL 400.10, Social Welfare Act, supplementary payments + href: https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-400-10 + - title: SSA State Assistance Programs for SSI Recipients, Michigan, Table 1 + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mi.html + - title: MDHHS RFT 248, SSI Payment Levels (RFB 2016-001 archive) + href: https://web.archive.org/web/20161223122049id_/https://dhhs.michigan.gov/OLMWEB/EX/RF/Public/RFT/248.pdf#page=2 + +# Individual rates have not changed since 2016 across all six living +# arrangements (verified against RFT 248 RFB 2016-001 through RFB 2026-001). +INDEPENDENT_LIVING: + 2016-01-01: 14 +HOUSEHOLD_OF_ANOTHER: + 2016-01-01: 9.33 +DOMICILIARY_CARE: + 2016-01-01: 87 +PERSONAL_CARE: + 2016-01-01: 157.5 +HOME_FOR_AGED: + 2016-01-01: 179.3 +INSTITUTION: + 2016-01-01: 7 +NONE: + 2016-01-01: 0 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 801129254f1..5ecc1850a8d 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -651,6 +651,12 @@ programs: name: Massachusetts SSP full_name: Massachusetts State Supplementary Payment variable: ma_state_supplement + - state: MI + status: complete + name: Michigan SSP + full_name: Michigan State Supplementary Payment + variable: mi_ssp + parameter_prefix: gov.states.mi.mdhhs.ssp - state: KS status: complete name: Kansas SSPP diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/integration.yaml new file mode 100644 index 00000000000..f639e388e1e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/integration.yaml @@ -0,0 +1,839 @@ +- name: Case 1, single eligible person in Independent Living gets $14. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Single eligible person, IL → individual rate $14/mo + mi_ssp_eligible: true + mi_ssp_person: 14 + mi_ssp: 14 + +- name: Case 2, single eligible person in Personal Care gets $157.50. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: PERSONAL_CARE + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Single eligible person, Personal Care → individual rate $157.50/mo + mi_ssp_eligible: true + mi_ssp_person: 157.50 + mi_ssp: 157.50 + +- name: Case 3, single eligible person in Home for Aged gets $179.30. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOME_FOR_AGED + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Single eligible person, Home for Aged → individual rate $179.30/mo + mi_ssp_eligible: true + mi_ssp_person: 179.30 + mi_ssp: 179.30 + +- name: Case 4, joint SSI couple both in Independent Living gets $21 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Joint claim + both eligible + same LA → couple rate $21 / 2 = $10.50 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [10.50, 10.50] + # SPM unit total = $10.50 + $10.50 = $21 + mi_ssp: 21 + +- name: Case 5, joint SSI couple both in Domiciliary Care 2024 gets $645 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + person2: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2024 Domiciliary Care couple rate $645 / 2 = $322.50 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [322.50, 322.50] + # SPM unit total = $322.50 + $322.50 = $645 + mi_ssp: 645 + +- name: Case 6, joint SSI couple in mismatched arrangements falls back to individual. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Different arrangements → individual rates + # person1: IL individual = $14, person2: HoA individual = $9.33 + mi_ssp_eligible: [true, true] + mi_ssp_person: [14, 9.33] + # SPM unit total = $14 + $9.33 = $23.33 + mi_ssp: 23.33 + +- name: Case 7, person not receiving SSI gets zero. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 0 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # SSI = 0 (income too high for federal SSI) → not eligible + mi_ssp_eligible: false + mi_ssp_person: 0 + mi_ssp: 0 + +- name: Case 8, Home for Aged couple boundary 2023 vs 2024 reflects COLA. + absolute_error_margin: 0.01 + period: 2023-01 + input: + people: + person1: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + person2: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2022-2023 Home for Aged couple rate $779.60 / 2 = $389.80 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [389.80, 389.80] + # SPM unit total = $779.60 + mi_ssp: 779.60 + +- name: Case 9, family with one SSI recipient and non-SSI spouse pays only recipient. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $943/mo SSI = $11,316/yr individual FBR + ssi: 11_316 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + ssi: 0 + is_ssi_aged_blind_disabled: false + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Non-SSI spouse breaks joint-claim test → person1 gets individual $14 + mi_ssp_eligible: [true, false] + mi_ssp_person: [14, 0] + # SPM unit total = $14 + mi_ssp: 14 + +- name: Case 10, couple same LA but only one is SSI-eligible falls back to individual. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_aged_blind_disabled: true + is_ssi_eligible: false + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Only person1 receives SSI → person1 eligible, person2 not eligible + # Couple rate doesn't apply (person2 not eligible) → individual rate $14 + mi_ssp_eligible: [true, false] + mi_ssp_person: [14, 0] + mi_ssp: 14 + +- name: Case 11, Domiciliary Care couple boundary 2019 reflects pre-COLA rate. + absolute_error_margin: 0.01 + period: 2019-01 + input: + people: + person1: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + person2: + # $30/mo SSI = $360/yr institutional rate + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2019 (pre-2020-01 boundary) Domiciliary Care couple rate = $540 + # $540 / 2 = $270 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [270, 270] + mi_ssp: 540 + +- name: Case 12, Domiciliary Care couple at 2020-01 boundary reflects new COLA rate. + absolute_error_margin: 0.01 + period: 2020-01 + input: + people: + person1: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2020-01 boundary: Domiciliary Care couple rate = $565 + # $565 / 2 = $282.50 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [282.50, 282.50] + mi_ssp: 565 + +- name: Case 13, Personal Care couple at 2023-12 reflects pre-COLA rate. + absolute_error_margin: 0.01 + period: 2023-01 + input: + people: + person1: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: PERSONAL_CARE + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: PERSONAL_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Pre-2024-01 (2022-2023 era) Personal Care couple rate = $736 + # $736 / 2 = $368 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [368, 368] + mi_ssp: 736 + +- name: Case 14, Personal Care couple at 2024-01 boundary reflects new COLA rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: PERSONAL_CARE + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: PERSONAL_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2024-01 boundary: Personal Care couple rate = $786 + # $786 / 2 = $393 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [393, 393] + mi_ssp: 786 + +- name: Case 15, Home for Aged couple at 2025-12 reflects pre-COLA rate. + absolute_error_margin: 0.01 + period: 2025-01 + input: + people: + person1: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Pre-2026-01 (2024-2025 era) Home for Aged couple rate = $829.60 + # $829.60 / 2 = $414.80 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [414.80, 414.80] + mi_ssp: 829.60 + +- name: Case 16, Home for Aged couple at 2026-01 boundary reflects new COLA rate. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person1: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOME_FOR_AGED + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # 2026-01 boundary: Home for Aged couple rate = $855.60 + # $855.60 / 2 = $427.80 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [427.80, 427.80] + mi_ssp: 855.60 + +- name: Case 17, person with ssi=0 and high income gets zero benefit. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # High earnings prevent SSI eligibility downstream + employment_income: 60_000 + ssi: 0 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # ssi=0 gate fails → not eligible regardless of LA + mi_ssp_eligible: false + mi_ssp_person: 0 + mi_ssp: 0 + +- name: Case 18, single eligible person in NONE arrangement gets zero. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 11_316 + mi_ssp_living_arrangement: NONE + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # NONE arrangement fails the in-qualifying-arrangement gate + mi_ssp_eligible: false + mi_ssp_person: 0 + mi_ssp: 0 + +- name: Case 19, single eligible person in Household of Another gets $9.33. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # HoA individual rate = $9.33/mo (frozen) + mi_ssp_eligible: true + mi_ssp_person: 9.33 + mi_ssp: 9.33 + +- name: Case 20, single eligible person in Domiciliary Care gets $87. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Domiciliary Care individual rate = $87/mo (frozen) + mi_ssp_eligible: true + mi_ssp_person: 87 + mi_ssp: 87 + +- name: Case 21, single eligible person in Institution gets $7. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INSTITUTION + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Institution individual rate = $7/mo (frozen) + mi_ssp_eligible: true + mi_ssp_person: 7 + mi_ssp: 7 + +- name: Case 22, joint SSI couple both in Household of Another gets $13.96 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + person2: + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # HoA couple rate $13.96 / 2 = $6.98 each + mi_ssp_eligible: [true, true] + mi_ssp_person: [6.98, 6.98] + mi_ssp: 13.96 + +- name: Case 23, couple with one in Independent Living and one in Personal Care falls back to individual. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + ssi: 360 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: PERSONAL_CARE + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Mismatched LAs → individual rates + # person1: IL individual = $14, person2: Personal Care individual = $157.50 + mi_ssp_eligible: [true, true] + mi_ssp_person: [14, 157.50] + # SPM unit total = $14 + $157.50 = $171.50 + mi_ssp: 171.50 + +- name: Case 24, three SSI recipients in same household sum correctly. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_eligible: true + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person3: + # Third SSI recipient (e.g., adult relative) - not in marital unit + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + marital_unit2: + members: [person3] + tax_units: + tax_unit: + members: [person1, person2] + tax_unit2: + members: [person3] + spm_units: + spm_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: MI + output: + # person1+person2 = couple rate $21 → $10.50 each + # person3 = individual rate $14 + mi_ssp_eligible: [true, true, true] + mi_ssp_person: [10.50, 10.50, 14] + # SPM unit total = $10.50 + $10.50 + $14 = $35 + mi_ssp: 35 + +- name: Case 25, non-Michigan resident SSI recipient gets zero MI SSP. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 11_316 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + # Non-MI state → defined_for=StateCode.MI zeros mi_ssp + state_code: CA + output: + # defined_for filter on mi_ssp returns 0 outside Michigan + mi_ssp: 0 + +- name: Case 26, child SSI recipient gets Independent Living individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + age: 8 + is_disabled: true + # Child receives federal SSI at 2024 individual FBR + # $943/mo = $11,316/yr + ssi: 11_316 + # mi_ssp_living_arrangement defaults to INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # BEM 660 covers all SSI recipients including children; RFT 248 + # "Eligible Child" rate equals the IL individual rate ($14/mo). + mi_ssp_eligible: true + mi_ssp_individual_amount: 14 + mi_ssp_person: 14 + mi_ssp: 14 + +- name: Case 27, Independent Living person with no regular federal SSI payment is ineligible. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + age: 70 + is_ssi_aged_blind_disabled: true + # $30,000/yr social security retirement = $2,500/mo. After the + # $20 general unearned income exclusion, annual countable + # unearned ≈ $29,760. Uncapped SSI = $11,316 - $29,760 = -$18,444. + # Monthly spillover = -uncapped_ssi/12 ≈ $1,537. BEM 660 limits + # MDHHS-issued Independent Living SSPs to months with a regular + # first-of-month federal SSI benefit, so this person is not + # eligible for the MDHHS-issued payment. + social_security_retirement: 30_000 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # MDHHS-administered Independent Living category fails the federal + # SSI payment gate. + mi_ssp_eligible: false + mi_ssp_person: 0 + mi_ssp: 0 + +- name: Case 28, Personal Care recipient in spillover window receives partial SSP. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + # uncapped_ssi annual = -600 → monthly spillover = $50. + # Personal Care SSP base = $157.50/mo. Result = max_(0, 157.50 - 50) = $107.50. + uncapped_ssi: -600 + mi_ssp_living_arrangement: PERSONAL_CARE + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Federal SSI exhausted ($0) but state supplement only partially + # spilled into → recipient gets $107.50 SSP. + mi_ssp_eligible: true + mi_ssp_person: 107.50 + mi_ssp: 107.50 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp.yaml new file mode 100644 index 00000000000..09b9a812f14 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp.yaml @@ -0,0 +1,89 @@ +- name: Case 1, single eligible person sums to individual amount. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + # Single person IL = $14 + mi_ssp: 14 + +- name: Case 2, joint couple in same arrangement sums per-person amounts. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Couple rate $21 total ($10.50 + $10.50) + mi_ssp: 21 + +- name: Case 3, no one receives SSI so SPM unit total is zero. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 0 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp: 0 + +- name: Case 4, non-Michigan SPM unit gets zero. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $943/mo SSI = $11,316/yr individual FBR + ssi: 11_316 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: OH + output: + mi_ssp: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.yaml new file mode 100644 index 00000000000..0458e1f38fe --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.yaml @@ -0,0 +1,111 @@ +- name: Case 1, Independent Living couple amount frozen at $21 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + # $21 / 2 = $10.50 per person + mi_ssp_couple_amount: 10.50 + +- name: Case 2, Household of Another couple amount frozen at $13.96 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + households: + household: + members: [person1] + state_code: MI + output: + # $13.96 / 2 = $6.98 per person + mi_ssp_couple_amount: 6.98 + +- name: Case 3, Domiciliary Care couple in 2024 uses 2024 rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + households: + household: + members: [person1] + state_code: MI + output: + # 2024 rate $645 / 2 = $322.50 per person + mi_ssp_couple_amount: 322.50 + +- name: Case 4, Personal Care couple in 2026 uses 2026 rate. + absolute_error_margin: 0.01 + period: 2026-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: PERSONAL_CARE + households: + household: + members: [person1] + state_code: MI + output: + # 2026 rate $812 / 2 = $406 per person + mi_ssp_couple_amount: 406 + +- name: Case 5, Home for Aged couple in 2020 uses 2020 rate. + absolute_error_margin: 0.01 + period: 2020-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOME_FOR_AGED + households: + household: + members: [person1] + state_code: MI + output: + # 2020 rate $749.60 / 2 = $374.80 per person + mi_ssp_couple_amount: 374.80 + +- name: Case 6, Institution couple frozen at $14 total. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INSTITUTION + households: + household: + members: [person1] + state_code: MI + output: + # $14 / 2 = $7 per person + mi_ssp_couple_amount: 7 + +- name: Case 7, Domiciliary Care couple in 2016 uses 2016 rate. + absolute_error_margin: 0.01 + period: 2016-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + households: + household: + members: [person1] + state_code: MI + output: + # 2016 rate $540 / 2 = $270 per person + mi_ssp_couple_amount: 270 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.yaml new file mode 100644 index 00000000000..2d5b7ef3146 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.yaml @@ -0,0 +1,105 @@ +- name: Case 1, joint claim with both eligible in same LA returns true. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + mi_ssp_couple_eligible: [true, true] + +- name: Case 2, joint claim with both eligible but different LAs returns false. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + mi_ssp_couple_eligible: [false, false] + +- name: Case 3, joint claim with only one SSI-eligible spouse returns false. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_aged_blind_disabled: false + is_ssi_eligible: false + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # person2 is filtered to false by defined_for=mi_ssp_eligible; + # person1 fails the both_eligible (marital_unit.sum == 2) gate. + mi_ssp_couple_eligible: [false, false] + +- name: Case 4, two SSI recipients in same LA but not joint claim returns false. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + is_ssi_aged_blind_disabled: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit1: + members: [person1] + marital_unit2: + members: [person2] + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Separate marital units → ssi_claim_is_joint = false → couple gate fails. + mi_ssp_couple_eligible: [false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.yaml new file mode 100644 index 00000000000..d99d2729ce6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.yaml @@ -0,0 +1,74 @@ +- name: Case 1, Michigan resident categorically SSI-eligible is eligible. + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_eligible: true + +- name: Case 2, Michigan resident not SSI-eligible is ineligible. + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: false + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_eligible: false + +- name: Case 3, non-Michigan resident categorically SSI-eligible is ineligible. + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + households: + household: + members: [person1] + state_code: OH + output: + mi_ssp_eligible: false + +- name: Case 4, MDHHS-administered category with no federal SSI payment is ineligible. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + is_ssi_eligible: true + ssi: 0 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + # BEM 660 limits MDHHS-issued SSPs to months with a regular + # first-of-month federal SSI benefit. + mi_ssp_eligible: false + +- name: Case 5, SSA-administered facility category keeps categorical spillover eligibility. + period: 2024-01 + input: + people: + person1: + is_ssi_aged_blind_disabled: true + is_ssi_eligible: true + ssi: 0 + mi_ssp_living_arrangement: PERSONAL_CARE + households: + household: + members: [person1] + state_code: MI + output: + # SSA-administered facility categories use the combined SSI/SSP + # income calculation; mi_ssp_person applies the spillover reduction. + mi_ssp_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.yaml new file mode 100644 index 00000000000..c36363ae236 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.yaml @@ -0,0 +1,104 @@ +- name: Case 1, Independent Living looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 14 + +- name: Case 2, Household of Another looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 9.33 + +- name: Case 3, Domiciliary Care looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: DOMICILIARY_CARE + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 87 + +- name: Case 4, Personal Care looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: PERSONAL_CARE + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 157.50 + +- name: Case 5, Home for Aged looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: HOME_FOR_AGED + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 179.30 + +- name: Case 6, Institution looks up individual rate. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INSTITUTION + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 7 + +- name: Case 7, NONE arrangement returns zero. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_individual_amount: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.yaml new file mode 100644 index 00000000000..415c59ec525 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.yaml @@ -0,0 +1,72 @@ +- name: Case 1, default Independent Living input passes through to category. + period: 2024-01 + input: + people: + person1: + # ssi_federal_living_arrangement defaults to OWN_HOUSEHOLD; + # mi_ssp_living_arrangement defaults to INDEPENDENT_LIVING. + age: 70 + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_payment_category: INDEPENDENT_LIVING + +- name: Case 2, federal MEDICAL_TREATMENT_FACILITY overrides input to INSTITUTION. + period: 2024-01 + input: + people: + person1: + ssi_federal_living_arrangement: MEDICAL_TREATMENT_FACILITY + # Even if user sets a different MI category, federal classification wins. + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_payment_category: INSTITUTION + +- name: Case 3, federal ANOTHER_PERSONS_HOUSEHOLD overrides input to HOUSEHOLD_OF_ANOTHER. + period: 2024-01 + input: + people: + person1: + ssi_federal_living_arrangement: ANOTHER_PERSONS_HOUSEHOLD + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_payment_category: HOUSEHOLD_OF_ANOTHER + +- name: Case 4, MI-specific Personal Care input passes through when federal LA does not override. + period: 2024-01 + input: + people: + person1: + ssi_federal_living_arrangement: OWN_HOUSEHOLD + mi_ssp_living_arrangement: PERSONAL_CARE + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_payment_category: PERSONAL_CARE + +- name: Case 5, federal CHILD_IN_PARENTAL_HOUSEHOLD does not override input. + period: 2024-01 + input: + people: + person1: + age: 12 + ssi_federal_living_arrangement: CHILD_IN_PARENTAL_HOUSEHOLD + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_payment_category: INDEPENDENT_LIVING diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_person.yaml new file mode 100644 index 00000000000..e955392fc05 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ssp/mi_ssp_person.yaml @@ -0,0 +1,124 @@ +- name: Case 1, single eligible person uses individual amount. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + is_ssi_eligible: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + # Individual rate for IL = $14 + mi_ssp_person: 14 + +- name: Case 2, ineligible person gets zero regardless of arrangement. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + ssi: 0 + mi_ssp_living_arrangement: INDEPENDENT_LIVING + households: + household: + members: [person1] + state_code: MI + output: + mi_ssp_person: 0 + +- name: Case 3, joint SSI couple in same arrangement gets couple amount. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Couple rate $21 / 2 = $10.50 per person + mi_ssp_person: [10.50, 10.50] + +- name: Case 4, joint SSI couple in mismatched arrangements falls back to individual. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + # $707/mo SSI = $8,484/yr couple half + ssi: 8_484 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: HOUSEHOLD_OF_ANOTHER + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # Different arrangements → each person gets their individual rate + # person1: IL individual = $14, person2: HoA individual = $9.33 + mi_ssp_person: [14, 9.33] + +- name: Case 5, only one spouse SSI-eligible falls back to individual amounts. + absolute_error_margin: 0.01 + period: 2024-01 + input: + people: + person1: + # $943/mo SSI = $11,316/yr individual FBR + ssi: 11_316 + is_ssi_aged_blind_disabled: true + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + person2: + ssi: 0 + is_ssi_aged_blind_disabled: false + is_tax_unit_head_or_spouse: true + mi_ssp_living_arrangement: INDEPENDENT_LIVING + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MI + output: + # person1 eligible → individual rate $14 + # person2 not SSI receiving → not eligible → $0 + mi_ssp_person: [14, 0] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp.py new file mode 100644 index 00000000000..e0c4125c2c7 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class mi_ssp(Variable): + value_type = float + entity = SPMUnit + label = "Michigan State Supplementary Payment" + unit = USD + definition_period = MONTH + defined_for = StateCode.MI + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=2", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + "https://www.legislature.mi.gov/Laws/MCL?objectName=mcl-400-10", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mi.html", + ) + + adds = ["mi_ssp_person"] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.py new file mode 100644 index 00000000000..93862411ad2 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_amount.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class mi_ssp_couple_amount(Variable): + value_type = float + entity = Person + label = "Michigan SSP couple payment amount per spouse" + unit = USD + definition_period = MONTH + defined_for = "mi_ssp_eligible" + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=2", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + ) + + def formula(person, period, parameters): + category = person("mi_ssp_payment_category", period) + p = parameters(period).gov.states.mi.mdhhs.ssp.payment + return p.couple[category] / 2 diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.py new file mode 100644 index 00000000000..f356ddf099f --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_couple_eligible.py @@ -0,0 +1,40 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mi.mdhhs.ssp.mi_ssp_living_arrangement import ( + MISSPLivingArrangement, +) + + +class mi_ssp_couple_eligible(Variable): + value_type = bool + entity = Person + label = "Michigan SSP couple rate applies" + definition_period = MONTH + defined_for = "mi_ssp_eligible" + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=1", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + ) + + def formula(person, period, parameters): + joint_claim = person("ssi_claim_is_joint", period.this_year) + eligible = person("mi_ssp_eligible", period) + # Summing the same variable used in defined_for is safe here; if a + # future contributor swaps to summing a different variable across + # the marital unit, the defined_for filter could silently halve + # the result. + both_eligible = person.marital_unit.sum(eligible) == 2 + category = person("mi_ssp_payment_category", period) + qualifying_arrangements = [ + value + for value in MISSPLivingArrangement + if value != MISSPLivingArrangement.NONE + ] + both_same_arrangement = ( + sum( + person.marital_unit.sum(category == value) == 2 + for value in qualifying_arrangements + ) + > 0 + ) + return joint_claim & both_eligible & both_same_arrangement diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.py new file mode 100644 index 00000000000..43625dd2409 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_eligible.py @@ -0,0 +1,41 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mi.mdhhs.ssp.mi_ssp_living_arrangement import ( + MISSPLivingArrangement, +) + + +class mi_ssp_eligible(Variable): + value_type = bool + entity = Person + label = "Michigan SSP eligible" + definition_period = MONTH + defined_for = StateCode.MI + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=2", + ) + + def formula(person, period, parameters): + # MDHHS-administered categories (Independent Living and Household + # of Another) require an actual regular federal SSI check per BEM + # 660. + # SSA-administered Adult Foster Care (Domiciliary, Personal) and + # Home for the Aged/Institution categories use categorical + # eligibility plus the spillover reduction in mi_ssp_person, per + # the SSA baseline: countable income deducts from federal SSI + # first, and any remaining countable income reduces the state + # supplement. + # We don't track section 1619 working-disabled status at the moment. + category = person("mi_ssp_payment_category", period) + is_strict_category = (category == MISSPLivingArrangement.INDEPENDENT_LIVING) | ( + category == MISSPLivingArrangement.HOUSEHOLD_OF_ANOTHER + ) + receives_ssi = person("ssi", period) > 0 + is_ssi_eligible = person("is_ssi_eligible", period.this_year) + arrangement_gate = where( + is_strict_category, + receives_ssi, + is_ssi_eligible, + ) + in_qualifying_arrangement = category != MISSPLivingArrangement.NONE + return arrangement_gate & in_qualifying_arrangement diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.py new file mode 100644 index 00000000000..0867c3a428e --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_individual_amount.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class mi_ssp_individual_amount(Variable): + value_type = float + entity = Person + label = "Michigan SSP individual payment amount" + unit = USD + definition_period = MONTH + defined_for = "mi_ssp_eligible" + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + ) + + def formula(person, period, parameters): + category = person("mi_ssp_payment_category", period) + p = parameters(period).gov.states.mi.mdhhs.ssp.payment + return p.individual[category] diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_living_arrangement.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_living_arrangement.py new file mode 100644 index 00000000000..5feae7dd471 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_living_arrangement.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class MISSPLivingArrangement(Enum): + INDEPENDENT_LIVING = "Independent living" + HOUSEHOLD_OF_ANOTHER = "Household of another" + DOMICILIARY_CARE = "Adult foster care - domiciliary care" + PERSONAL_CARE = "Adult foster care - personal care" + HOME_FOR_AGED = "Home for the aged" + INSTITUTION = "Institution" + NONE = "Not in a qualifying arrangement" + + +class mi_ssp_living_arrangement(Variable): + value_type = Enum + entity = Person + label = "Michigan SSP living arrangement" + definition_period = MONTH + defined_for = StateCode.MI + possible_values = MISSPLivingArrangement + default_value = MISSPLivingArrangement.INDEPENDENT_LIVING + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=1", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + ) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.py new file mode 100644 index 00000000000..308f6657188 --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_payment_category.py @@ -0,0 +1,43 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mi.mdhhs.ssp.mi_ssp_living_arrangement import ( + MISSPLivingArrangement, +) + + +class mi_ssp_payment_category(Variable): + value_type = Enum + entity = Person + label = "Michigan SSP payment category" + definition_period = MONTH + defined_for = StateCode.MI + possible_values = MISSPLivingArrangement + default_value = MISSPLivingArrangement.INDEPENDENT_LIVING + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=1", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + "https://www.ecfr.gov/current/title-20/section-416.1131", + ) + + def formula(person, period, parameters): + # Federal living-arrangement codes (20 CFR 416.1131-1133, 416.414) + # override the manual input for the categories Michigan shares with + # federal SSI. Adult Foster Care and Home for the Aged categories + # are MI-specific and fall back to the user-provided input. + federal_arrangement = person("ssi_federal_living_arrangement", period.this_year) + federal_values = federal_arrangement.possible_values + in_medical_facility = ( + federal_arrangement == federal_values.MEDICAL_TREATMENT_FACILITY + ) + in_another_household = ( + federal_arrangement == federal_values.ANOTHER_PERSONS_HOUSEHOLD + ) + living_arrangement = person("mi_ssp_living_arrangement", period) + return select( + [in_medical_facility, in_another_household], + [ + MISSPLivingArrangement.INSTITUTION, + MISSPLivingArrangement.HOUSEHOLD_OF_ANOTHER, + ], + default=living_arrangement, + ) diff --git a/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_person.py b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_person.py new file mode 100644 index 00000000000..a49362741ef --- /dev/null +++ b/policyengine_us/variables/gov/states/mi/mdhhs/ssp/mi_ssp_person.py @@ -0,0 +1,35 @@ +from policyengine_us.model_api import * + + +class mi_ssp_person(Variable): + value_type = float + entity = Person + label = "Michigan State Supplementary Payment per person" + unit = USD + definition_period = MONTH + defined_for = "mi_ssp_eligible" + reference = ( + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=2", + "https://mdhhs-pres-prod.michigan.gov/olmweb/EX/BP/Public/BEM/660.pdf#page=4", + "https://mdhhs-pres-prod.michigan.gov/olmweb/ex/RF/Public/RFT/248.pdf#page=2", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mi.html", + ) + + def formula(person, period, parameters): + couple_eligible = person("mi_ssp_couple_eligible", period) + base_amount = where( + couple_eligible, + person("mi_ssp_couple_amount", period), + person("mi_ssp_individual_amount", period), + ) + # Per SSA 2011 baseline: countable income deducts from federal SSI + # first; any remaining countable income reduces the state + # supplement. uncapped_ssi = SSI amount if eligible − countable + # income; when countable exceeds the federal FBR, uncapped_ssi + # goes negative and that negative magnitude is the residual income + # to deduct from the SSP. uncapped_ssi has definition_period=YEAR + # but unit=USD, so the framework auto-divides by 12 when accessed + # from this MONTH formula via period. + uncapped_ssi = person("uncapped_ssi", period) + reduction = max_(0, -uncapped_ssi) + return max_(0, base_amount - reduction) diff --git a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py index 9fdb27b3401..e59028de50b 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -24,6 +24,7 @@ def formula(spm_unit, period, parameters): "ks_sspp", # Kansas benefits "hi_oss", "ma_state_supplement", # Massachusetts benefits + "mi_ssp", # Michigan benefits # California programs. "ca_cvrp", # California Clean Vehicle Rebate Project. # Colorado programs.