diff --git a/changelog.d/mo-ssp.added.md b/changelog.d/mo-ssp.added.md new file mode 100644 index 00000000000..1a5b2b6f0ab --- /dev/null +++ b/changelog.d/mo-ssp.added.md @@ -0,0 +1 @@ +Implement Missouri State Supplementary Payment (SAB and SNC components) under DSS Family Support Division. diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index b74241a36b5..a3b239baca6 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 # Maine benefits - me_ssp + # Missouri benefits + - mo_ssp # Massachusetts benefits - ma_state_supplement # Michigan benefits @@ -79,6 +81,8 @@ values: - ga_ssp # Maine benefits - me_ssp + # Missouri benefits + - mo_ssp # Massachusetts benefits - ma_state_supplement # Michigan benefits diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/sab.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/sab.yaml new file mode 100644 index 00000000000..d06f98ce569 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/sab.yaml @@ -0,0 +1,12 @@ +description: Missouri sets this age as the minimum age for eligibility under the Supplemental Aid to the Blind program. +values: + 1900-01-01: 18 +metadata: + unit: year + period: year + label: Missouri SAB minimum age + reference: + - title: RSMo § 209.030 — Aid to the blind eligibility (age 18+) + href: https://revisor.mo.gov/main/OneSection.aspx?section=209.030 + - title: RSMo § 208.030(4) — Aid to the blind supplemental payments + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/snc.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/snc.yaml new file mode 100644 index 00000000000..dd856ace73e --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/snc.yaml @@ -0,0 +1,10 @@ +description: Missouri sets this age as the minimum age for eligibility under the Supplemental Nursing Care program. +values: + 1900-01-01: 21 +metadata: + unit: year + period: year + label: Missouri SNC minimum age + reference: + - title: RSMo § 208.030(5) — Supplemental Nursing Care eligibility + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/consolidated_standard.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/consolidated_standard.yaml new file mode 100644 index 00000000000..37815206b89 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/consolidated_standard.yaml @@ -0,0 +1,20 @@ +description: Missouri provides this amount as the consolidated standard under the Supplemental Aid to the Blind program. +values: + 2011-01-01: 510 + 2023-07-01: 789 + 2024-07-01: 828 + 2025-07-01: 917 + 2026-01-01: 1_073 +metadata: + unit: currency-USD + period: month + label: Missouri SAB consolidated standard + reference: + - title: 13 CSR 40-2.120(3) — Methods Used to Determine the Amount of Cash Payments + href: https://www.law.cornell.edu/regulations/missouri/13-CSR-40-2-120 + - title: RSMo § 208.030(4) — Aid to the blind supplemental payments + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 + - title: MO DSS MHABD Appendix J — SAB consolidated standard + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 + - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Missouri + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/maximum_payment.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/maximum_payment.yaml new file mode 100644 index 00000000000..dfc3cc2a9a6 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/maximum_payment.yaml @@ -0,0 +1,23 @@ +description: Missouri provides this amount as the maximum payment under the Supplemental Aid to the Blind program. +values: + 2011-01-01: 510 + 2023-07-01: 789 + 2024-07-01: 828 + 2025-07-01: 917 +metadata: + unit: currency-USD + period: month + label: Missouri SAB maximum payment + reference: + - title: RSMo § 208.030(4) — Aid to the blind supplemental payments + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 + - title: RSMo § 209.040 — Blind Pension grant amount + href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 + - title: 13 CSR 40-2.130(2) — Maximum Supplemental Aid to the Blind Payments + href: https://www.law.cornell.edu/regulations/missouri/13-CSR-40-2-130 + - title: MO DSS Family Support Manual § 0415.005.00 — Maximum and Minimum Payments + href: https://dssmanuals.mo.gov/supplemental-aid-to-the-blind/0415-000-00/0415-005-00/ + - title: MO DSS MHABD Appendix K — Supplemental Aid to the Blind grant amount + href: https://dssmanuals.mo.gov/wp-content/uploads/2018/10/appendix_k.pdf#page=5 + - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Missouri + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_i.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_i.yaml new file mode 100644 index 00000000000..7b248f9be2b --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_i.yaml @@ -0,0 +1,12 @@ +description: Missouri provides this amount as the maximum monthly grant for a recipient residing in a Residential Care Facility Level I under the Supplemental Nursing Care program. +values: + 2000-07-01: 156 +metadata: + unit: currency-USD + period: month + label: Missouri SNC RCF Level I maximum grant + reference: + - title: MO DSS MHABD Appendix J — SNC maximum grants + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 208.030(5) — Supplemental Nursing Care + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_ii_or_alf.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_ii_or_alf.yaml new file mode 100644 index 00000000000..103ecf6f2aa --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_ii_or_alf.yaml @@ -0,0 +1,12 @@ +description: Missouri provides this amount as the maximum monthly grant for a recipient residing in a Residential Care Facility Level II or Assisted Living Facility under the Supplemental Nursing Care program. +values: + 2000-07-01: 292 +metadata: + unit: currency-USD + period: month + label: Missouri SNC RCF Level II or ALF maximum grant + reference: + - title: MO DSS MHABD Appendix J — SNC maximum grants + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 208.030(5) — Supplemental Nursing Care + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/snf_or_icf_non_medicaid.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/snf_or_icf_non_medicaid.yaml new file mode 100644 index 00000000000..0f40c3e5a1f --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/snf_or_icf_non_medicaid.yaml @@ -0,0 +1,12 @@ +description: Missouri provides this amount as the maximum monthly grant for a recipient residing in a non-Medicaid Skilled Nursing Facility or Intermediate Care Facility under the Supplemental Nursing Care program. +values: + 2000-07-01: 390 +metadata: + unit: currency-USD + period: month + label: Missouri SNC SNF or ICF non-Medicaid maximum grant + reference: + - title: MO DSS MHABD Appendix J — SNC maximum grants + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 208.030(5) — Supplemental Nursing Care + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/personal_needs_allowance.yaml new file mode 100644 index 00000000000..924e1d0c8df --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/personal_needs_allowance.yaml @@ -0,0 +1,13 @@ +description: Missouri provides this amount as the additional monthly Personal Needs Allowance for recipients of the Supplemental Nursing Care program. +values: + 2000-07-01: 0 + 2015-01-01: 50 +metadata: + unit: currency-USD + period: month + label: Missouri SNC personal needs allowance + reference: + - title: MO DSS MHABD Appendix J — Personal Needs Allowance + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 208.030(5) — Supplemental Nursing Care personal needs allowance + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 76b5377b5f3..1d54e0b156f 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -712,6 +712,13 @@ programs: name: Maine SSP full_name: Maine State Supplemental Income Program variable: me_ssp + - state: MO + status: complete + name: Missouri SSP + full_name: Missouri State Supplementary Payment + variable: mo_ssp + parameter_prefix: gov.states.mo.dss.ssp + verified_years: "2023-2026" - state: KS status: complete name: Kansas SSPP diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml new file mode 100644 index 00000000000..2b7b5b6e8f6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml @@ -0,0 +1,262 @@ +- name: Case 1, blind Missouri adult with low SSI receives SAB grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + is_ssi_disabled: true + ssi: 2_400 # $200/month annualized + mo_ssp_living_arrangement: SAB + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 maximum SAB payment $917 - $200 SSI = $717. + mo_ssp_eligible: true + mo_ssp: 717 + # SAB pathway: no PNA add-on. + mo_ssp_personal_needs_allowance: 0 + # spm_unit and household aggregation passes mo_ssp through. + household_state_benefits: 717 + +- name: Case 2, aged Missouri resident in SNF non-Medicaid receives SNC plus PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 75 + is_ssi_aged: true + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MO + output: + # SNC SNF base $390 + $50 PNA = $440. + mo_ssp_eligible: true + mo_ssp_personal_needs_allowance: 50 + mo_ssp: 440 + household_state_benefits: 440 + +- name: Case 3, disabled Missouri resident in RCF Level II receives flat SNC amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + is_ssi_disabled: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_eligible: true + # SNC RCF Level II/ALF $292 + PNA $50 = $342. + mo_ssp: 342 + mo_ssp_personal_needs_allowance: 50 + household_state_benefits: 342 + +- name: Case 4, non-Missouri resident with same SAB inputs receives nothing. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + is_ssi_disabled: true + ssi: 200 + mo_ssp_living_arrangement: SAB + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: KS + output: + # defined_for = StateCode.MO zeroes out non-MO output. + mo_ssp_eligible: false + mo_ssp: 0 + household_state_benefits: 0 + +- name: Case 5, FY2025 Missouri couple with one blind spouse receiving SSI. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + is_blind: true + is_ssi_aged: true + ssi: 2_400 # $200/month annualized + mo_ssp_living_arrangement: SAB + person2: + age: 63 + is_ssi_aged: true + ssi: 0 + mo_ssp_living_arrangement: NONE + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MO + output: + # FY2025 maximum SAB payment $828 - $200 = $628 for blind spouse. + # Other spouse: ssi == 0 and arrangement NONE => not eligible => $0. + mo_ssp_eligible: [true, false] + mo_ssp: [628, 0] + # household_state_benefits is YEAR; in 2025 there's a FY transition at + # 2025-07-01 (FY2025 -> FY2026). Jan-Jun: $628/mo; Jul-Dec: $717/mo. + # Annual = 6*628 + 6*717 = 8,070; monthly average = 672.5. + household_state_benefits: 672.5 + +- name: Case 6, FY2024 SNF non-Medicaid resident. + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 80 + is_ssi_aged: true + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MO + output: + # SNC values frozen since 2000-07-01: $390 base + $50 PNA = $440. + mo_ssp_eligible: true + mo_ssp: 440 + mo_ssp_personal_needs_allowance: 50 + household_state_benefits: 440 + +- name: Case 7, Missouri adult age 22 disabled in RCF Level I receives flat amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 22 + is_ssi_disabled: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + tax_units: + tax_unit: + members: [person1] + marital_units: + marital_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MO + output: + # SNC RCF Level I $156 + PNA $50 = $206. + mo_ssp_eligible: true + mo_ssp: 206 + mo_ssp_personal_needs_allowance: 50 + household_state_benefits: 206 + +- name: Case 8, Missouri couple both eligible in RCF Level II each receive the per-person amount. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 65 + is_ssi_aged: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + person2: + age: 65 + is_ssi_aged: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MO + output: + # SNC formula is per-person; both spouses eligible so each gets + # $292 base + $50 PNA = $342, and the spm_unit total is 2 x $342 = $684. + mo_ssp_eligible: [true, true] + mo_ssp: [342, 342] + household_state_benefits: 684 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_sab_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_sab_countable_income.yaml new file mode 100644 index 00000000000..5b847b795a5 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_sab_countable_income.yaml @@ -0,0 +1,45 @@ +- name: Zero income gives zero SAB countable income. + period: 2026 + input: + people: + person1: + age: 50 + employment_income: 0 + households: + household: + members: [person1] + state_code: MO + output: + mo_sab_countable_income: 0 + +- name: $12,000 annual employment income gives ~$5,490 annual SAB countable income after SSI-style exclusions. + period: 2026 + input: + people: + person1: + age: 50 + employment_income: 12_000 + households: + household: + members: [person1] + state_code: MO + output: + # Monthly earned = $1,000. After $20 general + $65 earned exclusion = + # $915, then 50% remainder = $457.50/month, annualized to $5,490. + mo_sab_countable_income: 5_490 + +- name: $30,000 annual employment income gives ~$14,490 annual SAB countable income. + period: 2026 + input: + people: + person1: + age: 50 + employment_income: 30_000 + households: + household: + members: [person1] + state_code: MO + output: + # Monthly earned = $2,500. After $20 + $65 = $2,415, then 50% = $1,207.50, + # annualized to $14,490. Monthly $1,207.50 exceeds $1,073 SAB limit. + mo_sab_countable_income: 14_490 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp.yaml new file mode 100644 index 00000000000..2b2dea1deb9 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp.yaml @@ -0,0 +1,480 @@ +- name: Case 1, SAB recipient with low SSI receives maximum payment minus SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 2_400 # $200/month annualized; SSI is YEAR-defined + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 maximum SAB payment $917 - $200 SSI = $717. + mo_ssp: 717 + +- name: Case 2, SAB recipient with SSI above the maximum payment gets zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 11_604 # $967/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 maximum SAB payment $917; max(917 - 967, 0) = 0. + mo_ssp: 0 + +- name: Case 3, SAB recipient with $1 SSI receives maximum payment minus SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 maximum SAB payment $917 - $1 SSI = $916. + mo_ssp: 916 + +- name: Case 3b, SAB applicant with no SSI receives the full maximum payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 0 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 maximum SAB payment $917 - $0 SSI = $917. + mo_ssp: 917 + +- name: Case 4, SNC RCF Level I recipient receives flat amount plus PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # SNC RCF Level I $156 + PNA $50 = $206. + mo_ssp: 206 + +- name: Case 5, SNC RCF Level II or ALF recipient receives flat amount plus PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # SNC RCF Level II or ALF $292 + PNA $50 = $342. + mo_ssp: 342 + +- name: Case 6, SNC SNF non-Medicaid resident receives base plus PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # SNC SNF/ICF base $390 plus PNA $50 = $440. + mo_ssp: 440 + +- name: Case 7, person not in Missouri receives zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: KS + output: + # defined_for = StateCode.MO filters output to zero. + mo_ssp: 0 + +- name: Case 8, SNC applicant with no SSI but income below facility charge gets a grant. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 0 + social_security: 14_400 # $1,200/month annualized + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_300 + households: + household: + members: [person1] + state_code: MO + output: + # SNC need: $1,300 - $1,200 = $100, below the $156 RCF Level I cap. + # Plus $50 PNA (paid to all SNC tiers). $100 + $50 = $150. + mo_ssp: 150 + +- name: Case 9, FY2024 SAB recipient with $0.50 SSI rounds to the nearest dollar. + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 0.5 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2024 maximum SAB payment $789 - $0.50 SSI = $788.50, rounded to $789. + mo_ssp: 789 + +- name: Case 10, child under 18 in SAB arrangement is not eligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 16 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # Age below SAB threshold (18) => mo_ssp_age_eligible false. + mo_ssp: 0 + +- name: Case 11, person in NONE arrangement receives zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MO + output: + # NONE arrangement is not category-eligible. + mo_ssp: 0 + +- name: Case 12, FY2025 SAB recipient with low SSI uses FY2025 maximum payment. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 2_400 # $200/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 maximum SAB payment $828 - $200 SSI = $628. + mo_ssp: 628 + +- name: Case 13, FY2025 SAB grant with nominal SSI uses maximum payment minus SSI. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 maximum SAB payment $828 - $1 SSI = $827. + mo_ssp: 827 + +- name: Case 14, FY2025 SAB grant with SSI 200 uses maximum payment minus SSI. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 2_400 # $200/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 maximum SAB payment $828 - $200 SSI = $628. + mo_ssp: 628 + +- name: Case 15, FY2025 SAB grant with SSI exactly equal to maximum payment is zero. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 9_936 # $828/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 maximum SAB payment $828; max(828 - 828, 0) = 0. + mo_ssp: 0 + +- name: Case 16, FY2025 SAB grant with SSI exceeding maximum payment is zero. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12_000 # $1,000/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 maximum SAB payment $828; max(828 - 1000, 0) = 0. + mo_ssp: 0 + +- name: Case 17, negative self-employment income does not inflate SAB benefit. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized + self_employment_income: -60_000_000 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # mo_ssp formula reads ssi only. Negative SE income cannot inflate output; + # benefit is bounded by max(maximum payment - ssi, 0) = $827. + mo_ssp: 827 + +- name: Case 18, FY2024 SAB grant with nominal SSI uses maximum payment minus SSI. + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2024 maximum SAB payment $789 - $1 SSI = $788. + mo_ssp: 788 + +- name: Case 18b, FY2026 SAB grant with sub-dollar remainder uses minimum payment. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 11_000.4 # $916.70/month annualized + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # 13 CSR 40-2.130 gives a $1 payment when the SAB remainder is positive + # but below 50 cents; $917 - $916.70 = $0.30, so payment is $1. + mo_ssp: 1 + +- name: Case 19, SNF resident before PNA effective date receives base only. + period: 2014-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # PNA effective 2015-01-01; pre-effective period gives SNF base $390 + $0 PNA. + mo_ssp: 390 + +- name: Case 20, SNF resident at PNA effective date receives base plus PNA. + period: 2015-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # SNC SNF/ICF base $390 + PNA $50 effective 2015-01-01 = $440. + mo_ssp: 440 + +- name: Case 21, resident in a Medicaid facility receives zero. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MO + output: + # MOSSPLivingArrangement has no enum value for Title XIX Medicaid + # facilities; residents of those facilities are recorded as NONE and + # excluded from category eligibility, so SNC pays nothing. + mo_ssp: 0 + +- name: Case 22, intermediate care without LOC determination receives $342 (RCF II rate plus PNA). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Intermediate or skilled facility without level-of-care determination + # is paid the same SNC rate as RCF Level II / ALF = $292, plus PNA $50. + mo_ssp: 342 + +- name: Case 23, RCF Level I resident with OASDI and partial SSI has no SNC need. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_ssi_aged: true + social_security: 914 * 12 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # $914 OASDI + $100 SSI = $1,014 income, above the $1,000 facility base + # charge, so the SNC need test fails. + ssi: 100 + mo_ssp_eligible: false + mo_ssp: 0 + +- name: Case 24, RCF Level I resident with OASDI and partial SSI has partial SNC need. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_ssi_aged: true + social_security: 914 * 12 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_100 + households: + household: + members: [person1] + state_code: MO + output: + # $1,100 facility base charge - $1,014 income = $86 SNC grant, plus $50 PNA. + ssi: 100 + mo_ssp_eligible: true + mo_ssp: 136 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_age_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_age_eligible.yaml new file mode 100644 index 00000000000..dcab31e7c89 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_age_eligible.yaml @@ -0,0 +1,113 @@ +- name: SAB age 17 is below the 18+ threshold. + period: 2026-01 + input: + people: + person1: + age: 17 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: false + +- name: SAB age 18 exactly meets the threshold. + period: 2026-01 + input: + people: + person1: + age: 18 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: true + +- name: SAB age 19 is above the threshold. + period: 2026-01 + input: + people: + person1: + age: 19 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: true + +- name: SNC RCF_LEVEL_I age 20 is below the 21+ threshold. + period: 2026-01 + input: + people: + person1: + age: 20 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: false + +- name: SNC RCF_LEVEL_I age 21 exactly meets the threshold. + period: 2026-01 + input: + people: + person1: + age: 21 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: true + +- name: SNC RCF_LEVEL_II_OR_ALF age 21 meets the threshold. + period: 2026-01 + input: + people: + person1: + age: 21 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: true + +- name: SNC SNF_OR_ICF_NON_MEDICAID age 21 meets the threshold. + period: 2026-01 + input: + people: + person1: + age: 21 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_age_eligible: true + +- name: NONE arrangement at age 21 falls to the SNC 21+ threshold and passes. + period: 2026-01 + input: + people: + person1: + age: 21 + mo_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MO + output: + # Non-SAB arrangements use the SNC threshold; the category gate (tested + # separately in mo_ssp_category_eligible) rejects NONE downstream. + mo_ssp_age_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_category_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_category_eligible.yaml new file mode 100644 index 00000000000..169a10c2afc --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_category_eligible.yaml @@ -0,0 +1,132 @@ +- name: SAB pathway with is_blind=true is category-eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: SAB pathway with is_blind=false is not category-eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: false + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: false + +- name: SNC RCF_LEVEL_I aged resident is category-eligible without blindness. + period: 2026-01 + input: + people: + person1: + age: 70 + is_blind: false + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: SNC RCF_LEVEL_I resident who is not aged, blind, or disabled is not category-eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: false + is_disabled: false + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: false + +- name: SNC RCF_LEVEL_I disabled resident is category-eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_disabled: true + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: SNC RCF_LEVEL_II_OR_ALF is category-eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: SNC INTERMEDIATE_OR_SKILLED_NO_LOC is category-eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: SNC SNF_OR_ICF_NON_MEDICAID is category-eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: true + +- name: NONE arrangement is not category-eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + is_blind: true + mo_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_category_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml new file mode 100644 index 00000000000..d04cb868a23 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml @@ -0,0 +1,354 @@ +- name: Case 1, blind adult receiving SSI in MO with SAB arrangement is eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_eligible: true + +- name: Case 1b, blind adult with no SSI in MO with SAB arrangement is eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 0 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # SAB can be paid even when SSI is zero; the claimant must apply for or + # receive SSI, but that application requirement is not modeled. + mo_ssp_eligible: true + +- name: Case 2, SNC applicant with income above the facility base charge is not eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 0 + social_security: 18_000 # $1,500/month annualized + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_300 + households: + household: + members: [person1] + state_code: MO + output: + # SNC need test fails: $1,500 countable income exceeds $1,300 facility + # base charge, so no SNC grant is payable regardless of SSI receipt. + mo_ssp_eligible: false + +- name: Case 2b, SNC applicant with no SSI but income below the facility charge is eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 0 + social_security: 14_400 # $1,200/month annualized + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_300 + households: + household: + members: [person1] + state_code: MO + output: + # SNC need test passes: $1,200 countable income is below the $1,300 + # facility base charge. SSI receipt is not required — the facility-cost + # need test handles SNC income eligibility. + mo_ssp_eligible: true + +- name: Case 3, adult receiving SSI not in MO is not eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: KS + output: + # defined_for = StateCode.MO filters non-MO residents. + mo_ssp_eligible: false + +- name: Case 4, child age 17 receiving SSI in SAB arrangement is not eligible. + period: 2026-01 + input: + people: + person1: + age: 17 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # Below SAB age threshold of 18. + mo_ssp_eligible: false + +- name: Case 5, adult receiving SSI in NONE arrangement is not eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MO + output: + # NONE arrangement => not category-eligible. + mo_ssp_eligible: false + +- name: Case 6, adult age 19 with SAB arrangement is eligible. + period: 2026-01 + input: + people: + person1: + age: 19 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # SAB threshold is 18, so 19 qualifies. + mo_ssp_eligible: true + +- name: Case 7, adult age 20 in RCF Level I is not eligible due to SNC age threshold. + period: 2026-01 + input: + people: + person1: + age: 20 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # SNC categories require age 21+. + mo_ssp_eligible: false + +- name: Case 8, non-blind adult in SAB arrangement is not eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: false + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # SAB pathway requires is_blind. + mo_ssp_eligible: false + +- name: Case 9, disabled adult age 21 in SNF non-Medicaid is eligible. + period: 2026-01 + input: + people: + person1: + age: 21 + is_disabled: true + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Exactly meets SNC age threshold and the ABD category requirement. + mo_ssp_eligible: true + +- name: Case 10, adult exactly age 18 with SAB arrangement is eligible. + period: 2026-01 + input: + people: + person1: + age: 18 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # Exactly meets SAB age threshold of 18. + mo_ssp_eligible: true + +- name: Case 11, disabled adult exactly age 21 with RCF Level I is eligible. + period: 2026-01 + input: + people: + person1: + age: 21 + is_disabled: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Exactly meets SNC age threshold of 21 and the ABD category requirement. + mo_ssp_eligible: true + +- name: Case 12, adult age 20 in RCF Level I is not eligible. + period: 2026-01 + input: + people: + person1: + age: 20 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # SNC categories require age 21+; 20 is below threshold. + mo_ssp_eligible: false + +- name: Case 13, adult age 17 in SAB arrangement is not eligible. + period: 2026-01 + input: + people: + person1: + age: 17 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # SAB requires age 18+; 17 is below threshold. + mo_ssp_eligible: false + +- name: Case 14, adult with nominal SSI receipt is still eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 0.01 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # SAB eligibility is independent of SSI receipt; nominal SSI does not + # affect this — the SAB income test uses mo_sab_countable_income against + # the consolidated standard. + mo_ssp_eligible: true + +- name: Case 15, adult with valid eligibility outside Missouri is not eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: OK + output: + # defined_for = StateCode.MO filters non-MO residents to false. + mo_ssp_eligible: false + +- name: Case 16, SAB applicant with countable income above the monthly limit is not eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + employment_income: 30_000 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 SAB monthly income limit $1,073; mo_sab_countable_income for + # blind earner with $30,000 annual employment income exceeds the limit + # after SSI-style exclusions, so SAB pathway fails. + mo_ssp_eligible: false + +- name: Case 17, SAB applicant with non-SSI income at or below the monthly limit is eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + employment_income: 12_000 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 SAB monthly income limit $1,073; mo_sab_countable_income after + # SSI-style exclusions ($20 general + $65 earned + 50% remainder) on + # $1,000 monthly earned income is ~$458/month, within the limit, so SAB + # pathway passes. + mo_ssp_eligible: true + +- name: Case 18, SNC resident who is not aged, blind, or disabled is not eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: false + is_disabled: false + social_security: 600 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Facility cost exceeds income, but SNC still requires aged, blind, or + # disabled categorical eligibility. + mo_ssp_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.yaml new file mode 100644 index 00000000000..3b0c47d2c24 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.yaml @@ -0,0 +1,195 @@ +- name: Case 1, SNF non-Medicaid resident receiving SSI in MO gets PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_personal_needs_allowance: 50 + +- name: Case 2, RCF Level I resident gets PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Per Appendix J page 4, PNA is paid to all SNC participants. + mo_ssp_personal_needs_allowance: 50 + +- name: Case 3, RCF Level II or ALF resident gets PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Per Appendix J page 4, PNA is paid to all SNC participants. + mo_ssp_personal_needs_allowance: 50 + +- name: Case 4, ineligible person in SNF arrangement gets zero PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 0 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + households: + household: + members: [person1] + state_code: MO + output: + # defined_for = mo_ssp_eligible filters output to zero; SNC need test + # fails because the (defaulted-to-zero) facility base charge does not + # exceed the (zero) countable income. + mo_ssp_personal_needs_allowance: 0 + +- name: Case 5, SNF resident at PNA effective date gets PNA. + period: 2015-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # PNA effective 2015-01-01 at $50. + mo_ssp_personal_needs_allowance: 50 + +- name: Case 6, SNF resident before PNA effective date gets zero. + period: 2014-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # PNA effective 2015-01-01; pre-effective period returns 0. + mo_ssp_personal_needs_allowance: 0 + +- name: Case 7, SAB arrangement returns zero PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # PNA only paid for SNF/ICF non-Medicaid arrangements; SAB returns 0. + mo_ssp_personal_needs_allowance: 0 + +- name: Case 8, RCF Level I arrangement returns PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # Per Appendix J page 4, PNA is paid to all SNC participants. + mo_ssp_personal_needs_allowance: 50 + +- name: Case 9, SNF non-Medicaid arrangement returns PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + # PNA $50 paid for SNF/ICF non-Medicaid arrangement. + mo_ssp_personal_needs_allowance: 50 + +- name: Case 10, RCF II/ALF resident gets PNA. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_personal_needs_allowance: 50 + +- name: Case 11, INTERMEDIATE_OR_SKILLED_NO_LOC resident gets PNA. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + mo_snc_facility_base_charge: 1_000 + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_personal_needs_allowance: 50 diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_age_eligible.py b/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_age_eligible.py new file mode 100644 index 00000000000..ed389aedd2d --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_age_eligible.py @@ -0,0 +1,24 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mo.dss.ssp.mo_ssp_living_arrangement import ( + MOSSPLivingArrangement, +) + + +class mo_ssp_age_eligible(Variable): + value_type = bool + entity = Person + label = "Meets the age threshold for Missouri SSP" + definition_period = MONTH + defined_for = StateCode.MO + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.mo.dss.ssp.age_threshold + age = person("age", period.this_year) + living_arrangement = person("mo_ssp_living_arrangement", period) + is_sab = living_arrangement == MOSSPLivingArrangement.SAB + threshold = where(is_sab, p.sab, p.snc) + return age >= threshold diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_category_eligible.py b/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_category_eligible.py new file mode 100644 index 00000000000..4a0eed9580e --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_category_eligible.py @@ -0,0 +1,33 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mo.dss.ssp.mo_ssp_living_arrangement import ( + MOSSPLivingArrangement, +) + + +class mo_ssp_category_eligible(Variable): + value_type = bool + entity = Person + label = "In a Missouri SSP covered category" + definition_period = MONTH + defined_for = StateCode.MO + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", + ) + + def formula(person, period, parameters): + living_arrangement = person("mo_ssp_living_arrangement", period) + categories = living_arrangement.possible_values + in_snc_facility = ( + (living_arrangement == categories.RCF_LEVEL_I) + | (living_arrangement == categories.RCF_LEVEL_II_OR_ALF) + | (living_arrangement == categories.INTERMEDIATE_OR_SKILLED_NO_LOC) + | (living_arrangement == categories.SNF_OR_ICF_NON_MEDICAID) + ) + is_blind = person("is_blind", period.this_year) + is_aged_blind_disabled = person( + "is_ssi_aged_blind_disabled", period.this_year + ) + sab_pathway = (living_arrangement == categories.SAB) & is_blind + snc_pathway = in_snc_facility & is_aged_blind_disabled + return snc_pathway | sab_pathway diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_sab_countable_income.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_sab_countable_income.py new file mode 100644 index 00000000000..48f9b5d13bd --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_sab_countable_income.py @@ -0,0 +1,52 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.ssa.ssi.eligibility.income._apply_ssi_exclusions import ( + _apply_ssi_exclusions, +) + + +class mo_sab_countable_income(Variable): + value_type = float + entity = Person + label = "Missouri SAB countable income" + unit = USD + definition_period = YEAR + defined_for = StateCode.MO + reference = ( + "https://www.law.cornell.edu/regulations/missouri/13-CSR-40-2-120", + "https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2", + ) + + def formula(person, period, parameters): + # SAB applies SSI-methodology exclusions ($20 general, $65 earned, + # 50% of remainder) per 13 CSR 40-2.120(3), but the income test is + # applied to all blind applicants — including those who would fail + # federal SSI on resources or immigration. Using ssi_countable_income + # here would zero out for non-eligibles and silently bypass the SAB + # income test. Appendix K p.5 specifies the test is calculated + # individually, regardless of marital status, so we use individual + # (not marital) earned/unearned variables and do not apply the + # couple-computation divide-by-2. + earned = person("ssi_earned_income", period) + student_exclusion = person( + "ssi_blind_or_disabled_working_student_exclusion", period + ) + adjusted_earned = max_(earned - student_exclusion, 0) + # ISM (in-kind support and maintenance) is counted as unearned per + # 20 CFR § 416.1140 and is independent of SSI eligibility. + # Parent deeming (20 CFR § 416.1165) is not included because SAB is + # an 18+ program and the variable is gated on is_child. + unearned = person("ssi_unearned_income", period) + ism = person("ssi_in_kind_support_and_maintenance", period) + total_unearned = unearned + ism + own_countable = _apply_ssi_exclusions( + adjusted_earned, total_unearned, parameters, period + ) + # Spousal deeming from an SSI-ineligible spouse (20 CFR § 416.1163) + # applies for SAB applicants who are themselves SSI-eligible. The + # underlying variable is gated on is_ssi_eligible_individual, so SAB + # applicants who fail federal SSI on resources or immigration won't + # get spousal deeming captured here — a narrow modeling limitation. + spousal_deemed = person( + "ssi_income_deemed_from_ineligible_spouse", period + ) + return own_countable + spousal_deemed diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py new file mode 100644 index 00000000000..1fe256cb6fd --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class mo_snc_countable_income(Variable): + value_type = float + entity = Person + label = "Missouri SNC countable income" + unit = USD + definition_period = MONTH + defined_for = StateCode.MO + documentation = ( + "Approximates Supplemental Nursing Care countable income from modeled " + "cash income sources; Missouri-specific exclusions, overhead expenses, " + "and supported employment disregards are not modeled." + ) + reference = ( + "https://dssmanuals.mo.gov/supplemental-nursing-care/0610-000-00/0610-005-00/", + "https://dssmanuals.mo.gov/supplemental-nursing-care/0610-000-00/0610-025-00/", + ) + adds = ["market_income", "social_security", "ssi"] diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_facility_base_charge.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_facility_base_charge.py new file mode 100644 index 00000000000..f0ba9bade02 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_facility_base_charge.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class mo_snc_facility_base_charge(Variable): + value_type = float + entity = Person + label = "Missouri SNC facility base charge" + unit = USD + definition_period = MONTH + defined_for = StateCode.MO + reference = ( + "https://dssmanuals.mo.gov/supplemental-nursing-care/0610-000-00/0610-025-00/", + "https://dssmanuals.mo.gov/wp-content/uploads/2020/10/im-72.pdf", + ) diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py new file mode 100644 index 00000000000..787285916da --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py @@ -0,0 +1,56 @@ +from policyengine_us.model_api import * + + +class mo_ssp(Variable): + value_type = float + entity = Person + label = "Missouri State Supplementary Payment" + unit = USD + definition_period = MONTH + defined_for = "mo_ssp_eligible" + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://revisor.mo.gov/main/OneSection.aspx?section=209.040", + "https://www.law.cornell.edu/regulations/missouri/13-CSR-40-2-130", + "https://dssmanuals.mo.gov/supplemental-aid-to-the-blind/0415-000-00/0415-005-00/", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", + ) + + def formula(person, period, parameters): + # We don't model the earned-income-limit-based "most beneficial of two + # methods" Supplemental Aid to the Blind pathway from DSS Manual + # 0715.010.10 at the moment; it only applies to the closed + # 1973-conversion cohort. + p = parameters(period).gov.states.mo.dss.ssp + federal_ssi = person("ssi", period) + sab_remainder = max_(p.sab.maximum_payment - federal_ssi, 0) + sab_grant = where( + (sab_remainder > 0) & (sab_remainder < 1), + 1, + np.floor(sab_remainder + 0.5), + ) + living_arrangement = person("mo_ssp_living_arrangement", period) + categories = living_arrangement.possible_values + snc = p.snc.max_grant + snc_max_grant = select( + [ + living_arrangement == categories.RCF_LEVEL_I, + (living_arrangement == categories.RCF_LEVEL_II_OR_ALF) + | (living_arrangement == categories.INTERMEDIATE_OR_SKILLED_NO_LOC), + living_arrangement == categories.SNF_OR_ICF_NON_MEDICAID, + ], + [ + snc.rcf_level_i, + snc.rcf_level_ii_or_alf, + snc.snf_or_icf_non_medicaid, + ], + default=0, + ) + facility_base_charge = person("mo_snc_facility_base_charge", period) + countable_income = person("mo_snc_countable_income", period) + snc_grant = min_( + max_(facility_base_charge - countable_income, 0), + snc_max_grant, + ) + base = where(living_arrangement == categories.SAB, sab_grant, snc_grant) + return base + person("mo_ssp_personal_needs_allowance", period) diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py new file mode 100644 index 00000000000..3563ad1010a --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py @@ -0,0 +1,42 @@ +from policyengine_us.model_api import * + + +class mo_ssp_eligible(Variable): + value_type = bool + entity = Person + label = "Eligible for the Missouri State Supplementary Payment" + definition_period = MONTH + defined_for = StateCode.MO + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", + ) + + def formula(person, period, parameters): + # SAB applicants must apply for SSI but the grant is paid regardless + # of receipt; SNC eligibility turns on the facility-cost-vs-income + # need test, not SSI receipt. We don't track the SAB SSI-application + # requirement, Missouri-specific resource limits, the closed + # 1973-conversion State Pension cohort, or Supplemental Nursing Care + # physician medical-need tests. + in_category = person("mo_ssp_category_eligible", period) + age_eligible = person("mo_ssp_age_eligible", period) + living_arrangement = person("mo_ssp_living_arrangement", period) + categories = living_arrangement.possible_values + is_sab = living_arrangement == categories.SAB + is_snc = (living_arrangement != categories.SAB) & ( + living_arrangement != categories.NONE + ) + p = parameters(period).gov.states.mo.dss.ssp + # mo_sab_countable_income applies SSI-style exclusions without the + # eligibility zero-out in ssi_countable_income — so SAB applicants + # who fail federal SSI on resources or immigration are still + # income-tested against the MO consolidated standard. + countable_income = person("mo_sab_countable_income", period) + sab_income_eligible = ~is_sab | ( + countable_income <= p.sab.consolidated_standard + ) + snc_countable_income = person("mo_snc_countable_income", period) + facility_base_charge = person("mo_snc_facility_base_charge", period) + snc_need_eligible = ~is_snc | (snc_countable_income < facility_base_charge) + return in_category & age_eligible & sab_income_eligible & snc_need_eligible diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_living_arrangement.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_living_arrangement.py new file mode 100644 index 00000000000..95950943a06 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_living_arrangement.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class MOSSPLivingArrangement(Enum): + SAB = "Supplemental Aid to the Blind (own home or facility)" + RCF_LEVEL_I = "Residential Care Facility Level I" + RCF_LEVEL_II_OR_ALF = ( + "Residential Care Facility Level II or Assisted Living Facility" + ) + INTERMEDIATE_OR_SKILLED_NO_LOC = ( + "Intermediate or skilled care facility without level-of-care determination" + ) + SNF_OR_ICF_NON_MEDICAID = ( + "Skilled Nursing or Intermediate Care Facility, non-Medicaid" + ) + NONE = "None of the above" + + +class mo_ssp_living_arrangement(Variable): + value_type = Enum + entity = Person + label = "Missouri SSP living arrangement" + definition_period = MONTH + defined_for = StateCode.MO + possible_values = MOSSPLivingArrangement + default_value = MOSSPLivingArrangement.NONE + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4", + ) diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.py new file mode 100644 index 00000000000..c1c27d3fbe0 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.mo.dss.ssp.mo_ssp_living_arrangement import ( + MOSSPLivingArrangement, +) + + +class mo_ssp_personal_needs_allowance(Variable): + value_type = float + entity = Person + label = "Missouri SSP Personal Needs Allowance" + unit = USD + definition_period = MONTH + defined_for = "mo_ssp_eligible" + reference = ( + "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", + "https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4", + ) + + def formula(person, period, parameters): + # Per MO DSS MHABD Appendix J page 4, the PNA is paid to all SNC + # participants regardless of facility tier. RSMo 208.030.5 excludes the + # PNA when the recipient is already receiving a personal needs + # allowance from another state or federal program; we don't track that + # intake at the moment. + p = parameters(period).gov.states.mo.dss.ssp.snc + living_arrangement = person("mo_ssp_living_arrangement", period) + categories = MOSSPLivingArrangement + is_snc = (living_arrangement != categories.SAB) & ( + living_arrangement != categories.NONE + ) + return is_snc * p.personal_needs_allowance 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 4c7705cf267..cb65b512f5d 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 @@ -29,6 +29,7 @@ def formula(spm_unit, period, parameters): "wa_ssp", # Washington benefits "mi_ssp", # Michigan benefits "me_ssp", # Maine benefits + "mo_ssp", # Missouri benefits # California programs. "ca_cvrp", # California Clean Vehicle Rebate Project. # Colorado programs.