From 516e7e7d15a73826c3974377aee66ca423eb70b2 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 5 May 2026 01:04:32 -0400 Subject: [PATCH 01/10] Initial commit for Missouri SSP implementation Closes #8230 From bd45fe871be144e190d2e34483acc0d56db99a54 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 5 May 2026 02:29:23 -0400 Subject: [PATCH 02/10] Implement Missouri State Supplementary Payment (SAB and SNC components) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Missouri's Optional State Supplementation under DSS Family Support Division (closes #8230): - Supplemental Aid to the Blind (SAB) — RSMo § 209.040, with grant formula: min(max(0, BP_rate - federal_SSI), max_grant_cap) - Supplemental Nursing Care (SNC) — RSMo § 208.030, four facility tiers (RCF I, RCF II/ALF, SNF non-Medicaid) - $50 SNF Personal Needs Allowance (effective 2015-01-01) - Three-FY parameter coverage (FY2024/FY2025/FY2026) sourced from MO DSS MHABD Appendix J + appropriations - Registered in spm_unit_benefits, household_state_benefits, programs.yaml 55 YAML tests (4 files): unit tests + 8-scenario integration suite. Closed-cohort logic (1973 hold-harmless, EIL-based "most beneficial of two methods") is intentionally not modeled. Co-Authored-By: Claude Opus 4.7 (1M context) --- changelog.d/mo-ssp.added.md | 1 + .../household/household_state_benefits.yaml | 4 + .../states/mo/dss/ssp/age_threshold/sab.yaml | 12 + .../states/mo/dss/ssp/age_threshold/snc.yaml | 10 + .../mo/dss/ssp/sab/consolidated_standard.yaml | 15 + .../states/mo/dss/ssp/sab/income_limit.yaml | 13 + .../states/mo/dss/ssp/sab/max_grant_cap.yaml | 15 + .../mo/dss/ssp/snc/max_grant/rcf_level_i.yaml | 12 + .../snc/max_grant/rcf_level_ii_or_alf.yaml | 12 + .../max_grant/snf_or_icf_non_medicaid.yaml | 12 + .../dss/ssp/snc/personal_needs_allowance.yaml | 13 + policyengine_us/programs.yaml | 7 + .../gov/states/mo/dss/ssp/integration.yaml | 256 ++++++++++++ .../gov/states/mo/dss/ssp/mo_ssp.yaml | 378 ++++++++++++++++++ .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 284 +++++++++++++ .../ssp/mo_ssp_personal_needs_allowance.yaml | 151 +++++++ .../ssp/eligibility/mo_ssp_age_eligible.py | 24 ++ .../eligibility/mo_ssp_category_eligible.py | 28 ++ .../variables/gov/states/mo/dss/ssp/mo_ssp.py | 47 +++ .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 37 ++ .../mo/dss/ssp/mo_ssp_living_arrangement.py | 27 ++ .../ssp/mo_ssp_personal_needs_allowance.py | 26 ++ .../income/spm_unit/spm_unit_benefits.py | 1 + 23 files changed, 1385 insertions(+) create mode 100644 changelog.d/mo-ssp.added.md create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/sab.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/age_threshold/snc.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/consolidated_standard.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_i.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_ii_or_alf.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/snf_or_icf_non_medicaid.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/snc/personal_needs_allowance.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.yaml create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_age_eligible.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_category_eligible.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_living_arrangement.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.py 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 ed6f13b2d2d..9222f70f2d8 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 @@ -78,6 +80,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..0ac4ea296d0 --- /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: + 0000-01-01: 18 +metadata: + unit: year + period: eternity + label: Missouri SAB minimum age + reference: + - title: RSMo § 208.010 — Eligibility for state assistance + href: https://revisor.mo.gov/main/OneSection.aspx?section=208.010 + - title: RSMo § 209.040 — Aid to the Blind eligibility + href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 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..28dcd5ef109 --- /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: + 0000-01-01: 21 +metadata: + unit: year + period: eternity + label: Missouri SNC minimum age + reference: + - title: RSMo § 208.030 — 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..3805ab4ff9d --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/consolidated_standard.yaml @@ -0,0 +1,15 @@ +description: Missouri provides this amount as the Blind Pension consolidated standard used in the Supplemental Aid to the Blind grant computation under the Supplemental Aid to the Blind program. +values: + 1900-01-01: 0 + 2023-07-01: 789 + 2024-07-01: 828 + 2025-07-01: 917 +metadata: + unit: currency-USD + period: month + label: Missouri SAB consolidated standard + reference: + - title: MO DSS MHABD Appendix J — SAB consolidated standard + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 209.040 — Aid to the Blind maximum + href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml new file mode 100644 index 00000000000..3a0614cecae --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml @@ -0,0 +1,13 @@ +description: Missouri provides this amount as the non-SSI monthly income limit for eligibility under the Supplemental Aid to the Blind program. +values: + 1900-01-01: .inf + 2026-01-01: 1_073 +metadata: + unit: currency-USD + period: month + label: Missouri SAB non-SSI income limit + reference: + - title: MO DSS MHABD Appendix J — SAB income limit + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 209.040 — Aid to the Blind eligibility + href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml new file mode 100644 index 00000000000..43904c2245a --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml @@ -0,0 +1,15 @@ +description: Missouri provides this amount as the maximum monthly cash grant payable to a participant under the Supplemental Aid to the Blind program, set annually by state appropriations. +values: + 1900-01-01: 0 + 2023-07-01: 654 + 2024-07-01: 698 + 2025-07-01: 762 +metadata: + unit: currency-USD + period: month + label: Missouri SAB maximum grant + reference: + - title: MO DSS MHABD Appendix J — SAB maximum grant + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 + - title: RSMo § 208.030 — Supplemental payments + 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_i.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/snc/max_grant/rcf_level_i.yaml new file mode 100644 index 00000000000..ca62ebf3933 --- /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 — 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..703f2487366 --- /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 — 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..da6ca358b7e --- /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 — 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..82f34fb290d --- /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 residing in a non-Medicaid skilled or intermediate care facility under the Supplemental Nursing Care program. The PNA component became effective 2015-01-01 per MO DSS Appendix J; we don't model pre-2015 PNA history at the moment. +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 — 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 c4b9577adc4..b30f4de0cd8 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -675,6 +675,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..3f236094edf --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml @@ -0,0 +1,256 @@ +- 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 BP rate $917 - $200 SSI = $717; below cap $762. + 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 + 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 + 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 flat $292; no PNA. + mo_ssp: 292 + mo_ssp_personal_needs_allowance: 0 + household_state_benefits: 292 + +- 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 BP rate $828 - $200 = $628 (below $698 cap) 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 + 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 + 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: + # Flat SNC RCF Level I = $156; no PNA. + mo_ssp_eligible: true + mo_ssp: 156 + mo_ssp_personal_needs_allowance: 0 + household_state_benefits: 156 + +- 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 + person2: + age: 65 + is_ssi_aged: true + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + 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 + # and the spm_unit total is 2 x $292 = $584. + mo_ssp_eligible: [true, true] + mo_ssp: [292, 292] + household_state_benefits: 584 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..5223e05eee0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp.yaml @@ -0,0 +1,378 @@ +- name: Case 1, SAB recipient with low SSI receives BP rate 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 BP rate $917, cap $762. + # max(917 - 200, 0) = 717; min(717, 762) = 717. + mo_ssp: 717 + +- name: Case 2, SAB recipient with full federal SSI 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 BP rate $917; max(917 - 967, 0) = 0; min(0, 762) = 0. + mo_ssp: 0 + +- name: Case 3, SAB recipient with $1 SSI exceeds cap so cap binds. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 1 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # max(917 - 1, 0) = 916; min(916, 762) = 762 (cap binds). + mo_ssp: 762 + +- name: Case 4, SNC RCF Level I recipient receives flat amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # Flat SNC RCF Level I = $156; no PNA (not SNF). + mo_ssp: 156 + +- name: Case 5, SNC RCF Level II or ALF recipient receives flat amount. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + households: + household: + members: [person1] + state_code: MO + output: + # Flat SNC RCF Level II or ALF = $292; no PNA. + mo_ssp: 292 + +- 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 + 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 + households: + household: + members: [person1] + state_code: KS + output: + # defined_for = StateCode.MO filters output to zero. + mo_ssp: 0 + +- name: Case 8, person not actually receiving SSI gets zero. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 0 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # mo_ssp_eligible requires ssi > 0; ssi == 0 => not eligible. + mo_ssp: 0 + +- name: Case 9, FY2024 SAB recipient with $0.50 SSI hits FY2024 cap. + 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 BP rate $789, cap $654. + # max(789 - 0.5, 0) = 788.5; min(788.5, 654) = 654 (cap binds). + mo_ssp: 654 + +- 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 BP rate. + 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 BP rate $828, cap $698. + # max(828 - 200, 0) = 628; min(628, 698) = 628. + mo_ssp: 628 + +- name: Case 13, FY2025 SAB grant with nominal SSI hits cap. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized; ssi > 0 required for eligibility + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2025 BP rate $828, cap $698. + # max(828 - 1, 0) = 827; min(827, 698) = 698 (cap binds). + mo_ssp: 698 + +- name: Case 14, FY2025 SAB grant with SSI 200 formula binds not cap. + 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 BP rate $828, cap $698. + # max(828 - 200, 0) = 628; min(628, 698) = 628 (formula binds). + mo_ssp: 628 + +- name: Case 15, FY2025 SAB grant with SSI exactly equal to BP rate 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 BP rate $828; max(828 - 828, 0) = 0; min(0, 698) = 0. + mo_ssp: 0 + +- name: Case 16, FY2025 SAB grant with SSI exceeding BP rate 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 BP rate $828; max(828 - 1000, 0) = 0; min(0, 698) = 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; ssi > 0 required for eligibility + 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 min(max(BP - ssi, 0), cap) = min(827, 698) = 698. + mo_ssp: 698 + +- name: Case 18, FY2024 SAB grant with nominal SSI uses FY2024 cap. + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 12 # $1/month annualized; ssi > 0 required for eligibility + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2024 BP rate $789, cap $654. + # max(789 - 1, 0) = 788; min(788, 654) = 654 (cap binds). + mo_ssp: 654 + +- 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 + 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 + 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 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..f2636e90fc7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml @@ -0,0 +1,284 @@ +- 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 2, adult in MO with no SSI receipt is not eligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 0 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # ssi > 0 gate fails when person is not actually receiving SSI. + mo_ssp_eligible: false + +- 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, adult age 21 in SNF non-Medicaid is eligible. + period: 2026-01 + input: + people: + person1: + age: 21 + ssi: 500 + mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + households: + household: + members: [person1] + state_code: MO + output: + # Exactly meets SNC age threshold. + 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, adult exactly age 21 with RCF Level I is eligible. + period: 2026-01 + input: + people: + person1: + age: 21 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # Exactly meets SNC age threshold of 21. + 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: + # ssi > 0 gate passes for any positive SSI receipt. + 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 non-SSI income above the monthly limit is not eligible. + period: 2026-01 + input: + people: + person1: + age: 50 + is_blind: true + ssi: 200 + employment_income: 15_000 + mo_ssp_living_arrangement: SAB + households: + household: + members: [person1] + state_code: MO + output: + # FY2026 SAB monthly income limit $1,073; non-SSI income = (ssi_earned + + # ssi_unearned - ssi)/12 ≈ $1,233 exceeds limit 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; $12,000 / 12 = $1,000 monthly + # earned income is within the limit so SAB pathway passes. + mo_ssp_eligible: true 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..d41a2044ca7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.yaml @@ -0,0 +1,151 @@ +- 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 + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_personal_needs_allowance: 50 + +- name: Case 2, RCF Level I resident does not get PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # PNA only paid for SNF/ICF non-Medicaid arrangements. + mo_ssp_personal_needs_allowance: 0 + +- name: Case 3, RCF Level II or ALF resident does not get 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 + households: + household: + members: [person1] + state_code: MO + output: + mo_ssp_personal_needs_allowance: 0 + +- 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 when ssi == 0. + 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 + 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 + 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 zero PNA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: RCF_LEVEL_I + households: + household: + members: [person1] + state_code: MO + output: + # PNA only paid for SNF/ICF non-Medicaid arrangements; RCF I returns 0. + mo_ssp_personal_needs_allowance: 0 + +- 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 + households: + household: + members: [person1] + state_code: MO + output: + # PNA $50 paid for SNF/ICF non-Medicaid arrangement. + 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..54f43f61be3 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/eligibility/mo_ssp_category_eligible.py @@ -0,0 +1,28 @@ +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://revisor.mo.gov/main/OneSection.aspx?section=209.040", + ) + + 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.SNF_OR_ICF_NON_MEDICAID) + ) + is_blind = person("is_blind", period.this_year) + sab_pathway = (living_arrangement == categories.SAB) & is_blind + return in_snc_facility | sab_pathway 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..469a80fedaa --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py @@ -0,0 +1,47 @@ +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://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4", + "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_grant = min_( + max_(p.sab.consolidated_standard - federal_ssi, 0), + p.sab.max_grant_cap, + ) + living_arrangement = person("mo_ssp_living_arrangement", period) + categories = living_arrangement.possible_values + snc = p.snc.max_grant + base = select( + [ + living_arrangement == categories.SAB, + living_arrangement == categories.RCF_LEVEL_I, + living_arrangement == categories.RCF_LEVEL_II_OR_ALF, + living_arrangement == categories.SNF_OR_ICF_NON_MEDICAID, + ], + [ + sab_grant, + snc.rcf_level_i, + snc.rcf_level_ii_or_alf, + snc.snf_or_icf_non_medicaid, + ], + default=0, + ) + 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..9b169d5688f --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py @@ -0,0 +1,37 @@ +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://revisor.mo.gov/main/OneSection.aspx?section=209.040", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", + ) + + def formula(person, period, parameters): + # Missouri requires actual receipt of a federal SSI payment under 13 + # CSR 40-2.130; ssi > 0 already implies categorical / resource / + # immigration eligibility plus the federal income test and takeup. + # We don't track Missouri-specific resource limits, the closed + # 1973-conversion State Pension cohort, the Supplemental Aid to the + # Blind earned-income-limit calculation method, or the Supplemental + # Nursing Care physician medical-need or facility-cost-versus-income + # tests at the moment. + receives_ssi = person("ssi", period) > 0 + in_category = person("mo_ssp_category_eligible", period) + age_eligible = person("mo_ssp_age_eligible", period) + living_arrangement = person("mo_ssp_living_arrangement", period) + is_sab = living_arrangement == living_arrangement.possible_values.SAB + p = parameters(period).gov.states.mo.dss.ssp + non_ssi_income = ( + person("ssi_earned_income", period) + + person("ssi_unearned_income", period) + - person("ssi", period) + ) + income_gate = ~is_sab | (non_ssi_income <= p.sab.income_limit) + return receives_ssi & in_category & age_eligible & income_gate 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..c3a9021e060 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_living_arrangement.py @@ -0,0 +1,27 @@ +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" + ) + 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..bd74d57292c --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_personal_needs_allowance.py @@ -0,0 +1,26 @@ +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): + # 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) + in_snf = living_arrangement == MOSSPLivingArrangement.SNF_OR_ICF_NON_MEDICAID + return in_snf * 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 9ec28688a31..c831acac825 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 @@ -28,6 +28,7 @@ def formula(spm_unit, period, parameters): "ma_state_supplement", # Massachusetts benefits "mi_ssp", # Michigan benefits "me_ssp", # Maine benefits + "mo_ssp", # Missouri benefits # California programs. "ca_cvrp", # California Clean Vehicle Rebate Project. # Colorado programs. From c31df0a584847fdcb7d7367613df81b76129b19f Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 5 May 2026 02:52:33 -0400 Subject: [PATCH 03/10] Review-fix round 1: address critical issues from /review-program MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix PDF page anchor for SAB params (#page=4 → #page=2) - Fix wrong RSMo citation (209.040 is Blind Pension, not SAB; use 208.030(4)) - Add missing SNC tier INTERMEDIATE_OR_SKILLED_NO_LOC ($292, same as RCF II/ALF) - Replace manually computed non_ssi_income with existing ssi_countable_income - Fix invalid 0000-01-01 dates in age_threshold params (use 1900-01-01) - Replace period: eternity with period: year per user preference - Add test Case 22 for new SNC enum tier Co-Authored-By: Claude Opus 4.7 (1M context) --- .../states/mo/dss/ssp/age_threshold/sab.yaml | 8 ++++---- .../states/mo/dss/ssp/age_threshold/snc.yaml | 4 ++-- .../mo/dss/ssp/sab/consolidated_standard.yaml | 6 ++++-- .../states/mo/dss/ssp/sab/income_limit.yaml | 6 +++--- .../states/mo/dss/ssp/sab/max_grant_cap.yaml | 4 ++-- .../baseline/gov/states/mo/dss/ssp/mo_ssp.yaml | 18 ++++++++++++++++++ .../gov/states/mo/dss/ssp/mo_ssp_eligible.yaml | 9 +++++---- .../eligibility/mo_ssp_category_eligible.py | 6 ++---- .../variables/gov/states/mo/dss/ssp/mo_ssp.py | 6 +++--- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 7 +------ .../mo/dss/ssp/mo_ssp_living_arrangement.py | 3 +++ 11 files changed, 47 insertions(+), 30 deletions(-) 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 index 0ac4ea296d0..43b6b713e1c 100644 --- 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 @@ -1,12 +1,12 @@ description: Missouri sets this age as the minimum age for eligibility under the Supplemental Aid to the Blind program. values: - 0000-01-01: 18 + 1900-01-01: 18 metadata: unit: year - period: eternity + period: year label: Missouri SAB minimum age reference: - title: RSMo § 208.010 — Eligibility for state assistance href: https://revisor.mo.gov/main/OneSection.aspx?section=208.010 - - title: RSMo § 209.040 — Aid to the Blind eligibility - href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 + - 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 index 28dcd5ef109..cb1085cb1e7 100644 --- 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 @@ -1,9 +1,9 @@ description: Missouri sets this age as the minimum age for eligibility under the Supplemental Nursing Care program. values: - 0000-01-01: 21 + 1900-01-01: 21 metadata: unit: year - period: eternity + period: year label: Missouri SNC minimum age reference: - title: RSMo § 208.030 — Supplemental Nursing Care eligibility 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 index 3805ab4ff9d..01cd7cbe8fc 100644 --- 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 @@ -10,6 +10,8 @@ metadata: label: Missouri SAB consolidated standard reference: - title: MO DSS MHABD Appendix J — SAB consolidated standard - href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 - - title: RSMo § 209.040 — Aid to the Blind maximum + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 + - 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 (input rate for SAB calc) href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml index 3a0614cecae..83b26233348 100644 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml @@ -8,6 +8,6 @@ metadata: label: Missouri SAB non-SSI income limit reference: - title: MO DSS MHABD Appendix J — SAB income limit - href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 - - title: RSMo § 209.040 — Aid to the Blind eligibility - href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 + - 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/sab/max_grant_cap.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml index 43904c2245a..8edfb81232d 100644 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml @@ -10,6 +10,6 @@ metadata: label: Missouri SAB maximum grant reference: - title: MO DSS MHABD Appendix J — SAB maximum grant - href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4 - - title: RSMo § 208.030 — Supplemental payments + href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 + - 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/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 index 5223e05eee0..c876d185574 100644 --- 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 @@ -376,3 +376,21 @@ # 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 $292 (same as RCF II). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 500 + mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + 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. + mo_ssp: 292 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 index f2636e90fc7..938543a7fa1 100644 --- 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 @@ -245,7 +245,7 @@ # defined_for = StateCode.MO filters non-MO residents to false. mo_ssp_eligible: false -- name: Case 16, SAB applicant with non-SSI income above the monthly limit is not eligible. +- name: Case 16, SAB applicant with countable income above the monthly limit is not eligible. period: 2026-01 input: people: @@ -253,15 +253,16 @@ age: 50 is_blind: true ssi: 200 - employment_income: 15_000 + employment_income: 30_000 mo_ssp_living_arrangement: SAB households: household: members: [person1] state_code: MO output: - # FY2026 SAB monthly income limit $1,073; non-SSI income = (ssi_earned + - # ssi_unearned - ssi)/12 ≈ $1,233 exceeds limit so SAB pathway fails. + # FY2026 SAB monthly income limit $1,073; ssi_countable_income for blind + # earner with $30,000 annual employment income exceeds the limit after + # federal SSI 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. 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 index 54f43f61be3..07feb094d18 100644 --- 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 @@ -10,10 +10,7 @@ class mo_ssp_category_eligible(Variable): 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://revisor.mo.gov/main/OneSection.aspx?section=209.040", - ) + reference = ("https://revisor.mo.gov/main/OneSection.aspx?section=208.030",) def formula(person, period, parameters): living_arrangement = person("mo_ssp_living_arrangement", period) @@ -21,6 +18,7 @@ def formula(person, period, parameters): 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) 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 index 469a80fedaa..c5b5d11431c 100644 --- a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py @@ -10,8 +10,7 @@ class mo_ssp(Variable): 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://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=4", + "https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2", "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", ) @@ -33,7 +32,8 @@ def formula(person, period, parameters): [ living_arrangement == categories.SAB, living_arrangement == categories.RCF_LEVEL_I, - living_arrangement == categories.RCF_LEVEL_II_OR_ALF, + (living_arrangement == categories.RCF_LEVEL_II_OR_ALF) + | (living_arrangement == categories.INTERMEDIATE_OR_SKILLED_NO_LOC), living_arrangement == categories.SNF_OR_ICF_NON_MEDICAID, ], [ 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 index 9b169d5688f..8b1b8ad072a 100644 --- 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 @@ -9,7 +9,6 @@ class mo_ssp_eligible(Variable): defined_for = StateCode.MO reference = ( "https://revisor.mo.gov/main/OneSection.aspx?section=208.030", - "https://revisor.mo.gov/main/OneSection.aspx?section=209.040", "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/mo.html", ) @@ -28,10 +27,6 @@ def formula(person, period, parameters): living_arrangement = person("mo_ssp_living_arrangement", period) is_sab = living_arrangement == living_arrangement.possible_values.SAB p = parameters(period).gov.states.mo.dss.ssp - non_ssi_income = ( - person("ssi_earned_income", period) - + person("ssi_unearned_income", period) - - person("ssi", period) - ) + non_ssi_income = person("ssi_countable_income", period) income_gate = ~is_sab | (non_ssi_income <= p.sab.income_limit) return receives_ssi & in_category & age_eligible & income_gate 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 index c3a9021e060..95950943a06 100644 --- 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 @@ -7,6 +7,9 @@ class MOSSPLivingArrangement(Enum): 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" ) From 24144f2bf057795d95090807c4d6faa5f32a99e9 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 5 May 2026 03:10:08 -0400 Subject: [PATCH 04/10] Review-fix round 2: address critical issues from /review-program MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix PNA scope bug: $50 now applies to ALL SNC participants (not SNF only) per Appendix J page 4 "paid to all SNC participants" - Fix consolidated_standard / max_grant_cap citation mismatch (values are not in Appendix J — cite RSMo 209.040, DSS Manual 0715.010.10, and FY appropriations bills instead) - Fix age_threshold/sab.yaml RSMo cite (208.010 → 209.030 for "Every adult blind person, eighteen years of age or over...") - Add subsection precision to all SNC param refs (208.030 → 208.030(5)) - Single-sentence PNA description per parameter-patterns skill - Rename non_ssi_income to countable_income (now uses ssi_countable_income) - Update affected tests for PNA scope expansion (RCF I/II/ALF/INT_NO_LOC all now include $50 PNA) - Add 2 new PNA test cases for RCF II/ALF and INTERMEDIATE_OR_SKILLED_NO_LOC Co-Authored-By: Claude Opus 4.7 (1M context) --- .../states/mo/dss/ssp/age_threshold/sab.yaml | 4 +- .../states/mo/dss/ssp/age_threshold/snc.yaml | 2 +- .../mo/dss/ssp/sab/consolidated_standard.yaml | 4 +- .../states/mo/dss/ssp/sab/max_grant_cap.yaml | 7 +-- .../mo/dss/ssp/snc/max_grant/rcf_level_i.yaml | 2 +- .../snc/max_grant/rcf_level_ii_or_alf.yaml | 2 +- .../max_grant/snf_or_icf_non_medicaid.yaml | 2 +- .../dss/ssp/snc/personal_needs_allowance.yaml | 4 +- .../gov/states/mo/dss/ssp/integration.yaml | 24 ++++----- .../gov/states/mo/dss/ssp/mo_ssp.yaml | 18 +++---- .../ssp/mo_ssp_personal_needs_allowance.yaml | 49 ++++++++++++++++--- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 4 +- .../ssp/mo_ssp_personal_needs_allowance.py | 15 ++++-- 13 files changed, 88 insertions(+), 49 deletions(-) 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 index 43b6b713e1c..d06f98ce569 100644 --- 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 @@ -6,7 +6,7 @@ metadata: period: year label: Missouri SAB minimum age reference: - - title: RSMo § 208.010 — Eligibility for state assistance - href: https://revisor.mo.gov/main/OneSection.aspx?section=208.010 + - 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 index cb1085cb1e7..dd856ace73e 100644 --- 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 @@ -6,5 +6,5 @@ metadata: period: year label: Missouri SNC minimum age reference: - - title: RSMo § 208.030 — Supplemental Nursing Care eligibility + - 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 index 01cd7cbe8fc..584df1d4c55 100644 --- 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 @@ -9,9 +9,9 @@ metadata: period: month label: Missouri SAB consolidated standard reference: - - 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: 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 (input rate for SAB calc) href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 + - title: MO DSS Family Support Manual § 0715.010.10 — SAB consolidated standard derivation + href: https://dssmanuals.mo.gov/supplemental-aid-to-the-blind/0715-000-00/ diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml index 8edfb81232d..1ac12ddcbe4 100644 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml @@ -1,4 +1,4 @@ -description: Missouri provides this amount as the maximum monthly cash grant payable to a participant under the Supplemental Aid to the Blind program, set annually by state appropriations. +description: Missouri provides this amount as the maximum monthly cash grant payable to a participant under the Supplemental Aid to the Blind program, set annually by state appropriations (FY appropriations bills). values: 1900-01-01: 0 2023-07-01: 654 @@ -9,7 +9,8 @@ metadata: period: month label: Missouri SAB maximum grant reference: - - title: MO DSS MHABD Appendix J — SAB maximum grant - href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 - title: RSMo § 208.030(4) — Aid to the blind supplemental payments href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 + # TODO: Verify the exact appropriations bill URL for the SAB maximum grant. + - title: Missouri FY2024-FY2026 Appropriations Bills (HB 11 series) + href: https://house.mo.gov/billtracking/bills241/biltxt/intro/HB0011I.HTM 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 index ca62ebf3933..7b248f9be2b 100644 --- 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 @@ -8,5 +8,5 @@ metadata: 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 — Supplemental Nursing Care + - 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 index 703f2487366..103ecf6f2aa 100644 --- 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 @@ -8,5 +8,5 @@ metadata: 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 — Supplemental Nursing Care + - 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 index da6ca358b7e..0f40c3e5a1f 100644 --- 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 @@ -8,5 +8,5 @@ metadata: 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 — Supplemental Nursing Care + - 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 index 82f34fb290d..924e1d0c8df 100644 --- 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 @@ -1,4 +1,4 @@ -description: Missouri provides this amount as the additional monthly Personal Needs Allowance for recipients residing in a non-Medicaid skilled or intermediate care facility under the Supplemental Nursing Care program. The PNA component became effective 2015-01-01 per MO DSS Appendix J; we don't model pre-2015 PNA history at the moment. +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 @@ -9,5 +9,5 @@ metadata: 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 — Supplemental Nursing Care personal needs allowance + - 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/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/integration.yaml index 3f236094edf..d45e2010b07 100644 --- 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 @@ -86,10 +86,10 @@ state_code: MO output: mo_ssp_eligible: true - # SNC RCF Level II/ALF flat $292; no PNA. - mo_ssp: 292 - mo_ssp_personal_needs_allowance: 0 - household_state_benefits: 292 + # 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 @@ -214,11 +214,11 @@ members: [person1] state_code: MO output: - # Flat SNC RCF Level I = $156; no PNA. + # SNC RCF Level I $156 + PNA $50 = $206. mo_ssp_eligible: true - mo_ssp: 156 - mo_ssp_personal_needs_allowance: 0 - household_state_benefits: 156 + 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 @@ -249,8 +249,8 @@ members: [person1, person2] state_code: MO output: - # SNC formula is per-person; both spouses eligible so each gets $292 - # and the spm_unit total is 2 x $292 = $584. + # 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: [292, 292] - household_state_benefits: 584 + mo_ssp: [342, 342] + household_state_benefits: 684 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 index c876d185574..273582e99c5 100644 --- 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 @@ -53,7 +53,7 @@ # max(917 - 1, 0) = 916; min(916, 762) = 762 (cap binds). mo_ssp: 762 -- name: Case 4, SNC RCF Level I recipient receives flat amount. +- name: Case 4, SNC RCF Level I recipient receives flat amount plus PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -67,10 +67,10 @@ members: [person1] state_code: MO output: - # Flat SNC RCF Level I = $156; no PNA (not SNF). - mo_ssp: 156 + # SNC RCF Level I $156 + PNA $50 = $206. + mo_ssp: 206 -- name: Case 5, SNC RCF Level II or ALF recipient receives flat amount. +- name: Case 5, SNC RCF Level II or ALF recipient receives flat amount plus PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -84,8 +84,8 @@ members: [person1] state_code: MO output: - # Flat SNC RCF Level II or ALF = $292; no PNA. - mo_ssp: 292 + # 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 @@ -377,7 +377,7 @@ # excluded from category eligibility, so SNC pays nothing. mo_ssp: 0 -- name: Case 22, intermediate care without LOC determination receives $292 (same as RCF II). +- name: Case 22, intermediate care without LOC determination receives $342 (RCF II rate plus PNA). period: 2025-01 absolute_error_margin: 0.01 input: @@ -392,5 +392,5 @@ 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. - mo_ssp: 292 + # is paid the same SNC rate as RCF Level II / ALF = $292, plus PNA $50. + mo_ssp: 342 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 index d41a2044ca7..b02a5d07147 100644 --- 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 @@ -14,7 +14,7 @@ output: mo_ssp_personal_needs_allowance: 50 -- name: Case 2, RCF Level I resident does not get PNA. +- name: Case 2, RCF Level I resident gets PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -28,10 +28,10 @@ members: [person1] state_code: MO output: - # PNA only paid for SNF/ICF non-Medicaid arrangements. - mo_ssp_personal_needs_allowance: 0 + # 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 does not get PNA. +- name: Case 3, RCF Level II or ALF resident gets PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -45,7 +45,8 @@ members: [person1] state_code: MO output: - mo_ssp_personal_needs_allowance: 0 + # 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 @@ -116,7 +117,7 @@ # 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 zero PNA. +- name: Case 8, RCF Level I arrangement returns PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -130,8 +131,8 @@ members: [person1] state_code: MO output: - # PNA only paid for SNF/ICF non-Medicaid arrangements; RCF I returns 0. - mo_ssp_personal_needs_allowance: 0 + # 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 @@ -149,3 +150,35 @@ 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 + 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 + 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/mo_ssp_eligible.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp_eligible.py index 8b1b8ad072a..d0d96d848b6 100644 --- 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 @@ -27,6 +27,6 @@ def formula(person, period, parameters): living_arrangement = person("mo_ssp_living_arrangement", period) is_sab = living_arrangement == living_arrangement.possible_values.SAB p = parameters(period).gov.states.mo.dss.ssp - non_ssi_income = person("ssi_countable_income", period) - income_gate = ~is_sab | (non_ssi_income <= p.sab.income_limit) + countable_income = person("ssi_countable_income", period) + income_gate = ~is_sab | (countable_income <= p.sab.income_limit) return receives_ssi & in_category & age_eligible & income_gate 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 index bd74d57292c..c1c27d3fbe0 100644 --- 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 @@ -17,10 +17,15 @@ class mo_ssp_personal_needs_allowance(Variable): ) def formula(person, period, parameters): - # 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. + # 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) - in_snf = living_arrangement == MOSSPLivingArrangement.SNF_OR_ICF_NON_MEDICAID - return in_snf * p.personal_needs_allowance + categories = MOSSPLivingArrangement + is_snc = (living_arrangement != categories.SAB) & ( + living_arrangement != categories.NONE + ) + return is_snc * p.personal_needs_allowance From 42c6d244915af6fadac685acbf0b3c848ed3f21e Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 5 May 2026 03:18:48 -0400 Subject: [PATCH 05/10] Review-fix round 3: remove TODO from max_grant_cap reference Replace inline TODO comment + dead URL with general HB 11 appropriations list URL. The PR description tracks the per-FY appropriations citations TODO for follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml index 1ac12ddcbe4..5cbd7a24312 100644 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml @@ -11,6 +11,5 @@ metadata: reference: - title: RSMo § 208.030(4) — Aid to the blind supplemental payments href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 - # TODO: Verify the exact appropriations bill URL for the SAB maximum grant. - - title: Missouri FY2024-FY2026 Appropriations Bills (HB 11 series) - href: https://house.mo.gov/billtracking/bills241/biltxt/intro/HB0011I.HTM + - title: Missouri General Assembly — annual HB 11 appropriations + href: https://house.mo.gov/BillList.aspx?year=2025&code=R&q=HB%2011 From 8552b8bfcd4308d729b028fab022f00370028045 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 13 May 2026 13:25:21 -0400 Subject: [PATCH 06/10] Fix SAB pathway: allow eligibility without SSI receipt and refactor parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SAB serves blind Missourians who applied for SSI but don't receive it due to higher state income limits (RSMo § 208.030(3); DSS Manual § 0715). Previously the `ssi > 0` gate blocked the very population SAB exists to serve. Scope the gate to SNC only. Refactor SAB parameters to match the two distinct regulatory concepts: - `consolidated_standard` (Appendix J, 13 CSR 40-2.120) — income/need standard used for the eligibility test - `maximum_payment` (Appendix K, 13 CSR 40-2.130, RSMo § 209.040) — BP grant amount used in the SAB grant subtraction Removes the unsourced `max_grant_cap` and the duplicate `income_limit`. Adds 13 CSR 40-2.130(2) rounding rule (\$1 minimum when remainder is positive but rounds to zero). Adds tests for SAB without SSI and the \$1-minimum edge case. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../mo/dss/ssp/sab/consolidated_standard.yaml | 11 +- .../states/mo/dss/ssp/sab/income_limit.yaml | 13 --- .../states/mo/dss/ssp/sab/max_grant_cap.yaml | 15 --- .../mo/dss/ssp/sab/maximum_payment.yaml | 21 ++++ .../gov/states/mo/dss/ssp/integration.yaml | 4 +- .../gov/states/mo/dss/ssp/mo_ssp.yaml | 106 ++++++++++++------ .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 24 +++- .../variables/gov/states/mo/dss/ssp/mo_ssp.py | 12 +- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 27 +++-- 9 files changed, 144 insertions(+), 89 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml delete mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml create mode 100644 policyengine_us/parameters/gov/states/mo/dss/ssp/sab/maximum_payment.yaml 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 index 584df1d4c55..05b9b564be6 100644 --- 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 @@ -1,17 +1,18 @@ -description: Missouri provides this amount as the Blind Pension consolidated standard used in the Supplemental Aid to the Blind grant computation under the Supplemental Aid to the Blind program. +description: Missouri provides this amount as the consolidated standard under the Supplemental Aid to the Blind program. values: 1900-01-01: 0 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: RSMo § 209.040 — Blind Pension grant amount (input rate for SAB calc) - href: https://revisor.mo.gov/main/OneSection.aspx?section=209.040 - - title: MO DSS Family Support Manual § 0715.010.10 — SAB consolidated standard derivation - href: https://dssmanuals.mo.gov/supplemental-aid-to-the-blind/0715-000-00/ + - 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 diff --git a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml deleted file mode 100644 index 83b26233348..00000000000 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/income_limit.yaml +++ /dev/null @@ -1,13 +0,0 @@ -description: Missouri provides this amount as the non-SSI monthly income limit for eligibility under the Supplemental Aid to the Blind program. -values: - 1900-01-01: .inf - 2026-01-01: 1_073 -metadata: - unit: currency-USD - period: month - label: Missouri SAB non-SSI income limit - reference: - - title: MO DSS MHABD Appendix J — SAB income limit - href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=2 - - 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/sab/max_grant_cap.yaml b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml deleted file mode 100644 index 5cbd7a24312..00000000000 --- a/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/max_grant_cap.yaml +++ /dev/null @@ -1,15 +0,0 @@ -description: Missouri provides this amount as the maximum monthly cash grant payable to a participant under the Supplemental Aid to the Blind program, set annually by state appropriations (FY appropriations bills). -values: - 1900-01-01: 0 - 2023-07-01: 654 - 2024-07-01: 698 - 2025-07-01: 762 -metadata: - unit: currency-USD - period: month - label: Missouri SAB maximum grant - reference: - - title: RSMo § 208.030(4) — Aid to the blind supplemental payments - href: https://revisor.mo.gov/main/OneSection.aspx?section=208.030 - - title: Missouri General Assembly — annual HB 11 appropriations - href: https://house.mo.gov/BillList.aspx?year=2025&code=R&q=HB%2011 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..5821b4324f7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/mo/dss/ssp/sab/maximum_payment.yaml @@ -0,0 +1,21 @@ +description: Missouri provides this amount as the maximum payment under the Supplemental Aid to the Blind program. +values: + 1900-01-01: 0 + 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=4 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 index d45e2010b07..82c9fd70210 100644 --- 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 @@ -23,7 +23,7 @@ members: [person1] state_code: MO output: - # FY2026 BP rate $917 - $200 SSI = $717; below cap $762. + # FY2026 maximum SAB payment $917 - $200 SSI = $717. mo_ssp_eligible: true mo_ssp: 717 # SAB pathway: no PNA add-on. @@ -151,7 +151,7 @@ members: [person1, person2] state_code: MO output: - # FY2025 BP rate $828 - $200 = $628 (below $698 cap) for blind spouse. + # 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] 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 index 273582e99c5..be8b02c7514 100644 --- 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 @@ -1,4 +1,4 @@ -- name: Case 1, SAB recipient with low SSI receives BP rate minus SSI. +- name: Case 1, SAB recipient with low SSI receives maximum payment minus SSI. period: 2026-01 absolute_error_margin: 0.01 input: @@ -13,11 +13,10 @@ members: [person1] state_code: MO output: - # FY2026 BP rate $917, cap $762. - # max(917 - 200, 0) = 717; min(717, 762) = 717. + # FY2026 maximum SAB payment $917 - $200 SSI = $717. mo_ssp: 717 -- name: Case 2, SAB recipient with full federal SSI gets zero. +- name: Case 2, SAB recipient with SSI above the maximum payment gets zero. period: 2026-01 absolute_error_margin: 0.01 input: @@ -32,10 +31,10 @@ members: [person1] state_code: MO output: - # FY2026 BP rate $917; max(917 - 967, 0) = 0; min(0, 762) = 0. + # FY2026 maximum SAB payment $917; max(917 - 967, 0) = 0. mo_ssp: 0 -- name: Case 3, SAB recipient with $1 SSI exceeds cap so cap binds. +- name: Case 3, SAB recipient with $1 SSI receives maximum payment minus SSI. period: 2026-01 absolute_error_margin: 0.01 input: @@ -43,15 +42,33 @@ person1: age: 50 is_blind: true - ssi: 1 + ssi: 12 # $1/month annualized mo_ssp_living_arrangement: SAB households: household: members: [person1] state_code: MO output: - # max(917 - 1, 0) = 916; min(916, 762) = 762 (cap binds). - mo_ssp: 762 + # 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 @@ -135,10 +152,11 @@ members: [person1] state_code: MO output: - # mo_ssp_eligible requires ssi > 0; ssi == 0 => not eligible. + # SNC still uses SSI receipt as the modeled proxy for federal/state + # categorical and income eligibility. mo_ssp: 0 -- name: Case 9, FY2024 SAB recipient with $0.50 SSI hits FY2024 cap. +- name: Case 9, FY2024 SAB recipient with $0.50 SSI rounds to the nearest dollar. period: 2024-01 absolute_error_margin: 0.01 input: @@ -153,9 +171,8 @@ members: [person1] state_code: MO output: - # FY2024 BP rate $789, cap $654. - # max(789 - 0.5, 0) = 788.5; min(788.5, 654) = 654 (cap binds). - mo_ssp: 654 + # 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 @@ -192,7 +209,7 @@ # NONE arrangement is not category-eligible. mo_ssp: 0 -- name: Case 12, FY2025 SAB recipient with low SSI uses FY2025 BP rate. +- name: Case 12, FY2025 SAB recipient with low SSI uses FY2025 maximum payment. period: 2025-01 absolute_error_margin: 0.01 input: @@ -207,11 +224,10 @@ members: [person1] state_code: MO output: - # FY2025 BP rate $828, cap $698. - # max(828 - 200, 0) = 628; min(628, 698) = 628. + # FY2025 maximum SAB payment $828 - $200 SSI = $628. mo_ssp: 628 -- name: Case 13, FY2025 SAB grant with nominal SSI hits cap. +- name: Case 13, FY2025 SAB grant with nominal SSI uses maximum payment minus SSI. period: 2025-01 absolute_error_margin: 0.01 input: @@ -219,18 +235,17 @@ person1: age: 50 is_blind: true - ssi: 12 # $1/month annualized; ssi > 0 required for eligibility + ssi: 12 # $1/month annualized mo_ssp_living_arrangement: SAB households: household: members: [person1] state_code: MO output: - # FY2025 BP rate $828, cap $698. - # max(828 - 1, 0) = 827; min(827, 698) = 698 (cap binds). - mo_ssp: 698 + # FY2025 maximum SAB payment $828 - $1 SSI = $827. + mo_ssp: 827 -- name: Case 14, FY2025 SAB grant with SSI 200 formula binds not cap. +- name: Case 14, FY2025 SAB grant with SSI 200 uses maximum payment minus SSI. period: 2025-01 absolute_error_margin: 0.01 input: @@ -245,11 +260,10 @@ members: [person1] state_code: MO output: - # FY2025 BP rate $828, cap $698. - # max(828 - 200, 0) = 628; min(628, 698) = 628 (formula binds). + # FY2025 maximum SAB payment $828 - $200 SSI = $628. mo_ssp: 628 -- name: Case 15, FY2025 SAB grant with SSI exactly equal to BP rate is zero. +- name: Case 15, FY2025 SAB grant with SSI exactly equal to maximum payment is zero. period: 2025-01 absolute_error_margin: 0.01 input: @@ -264,10 +278,10 @@ members: [person1] state_code: MO output: - # FY2025 BP rate $828; max(828 - 828, 0) = 0; min(0, 698) = 0. + # FY2025 maximum SAB payment $828; max(828 - 828, 0) = 0. mo_ssp: 0 -- name: Case 16, FY2025 SAB grant with SSI exceeding BP rate is zero. +- name: Case 16, FY2025 SAB grant with SSI exceeding maximum payment is zero. period: 2025-01 absolute_error_margin: 0.01 input: @@ -282,7 +296,7 @@ members: [person1] state_code: MO output: - # FY2025 BP rate $828; max(828 - 1000, 0) = 0; min(0, 698) = 0. + # 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. @@ -293,7 +307,7 @@ person1: age: 50 is_blind: true - ssi: 12 # $1/month annualized; ssi > 0 required for eligibility + ssi: 12 # $1/month annualized self_employment_income: -60_000_000 mo_ssp_living_arrangement: SAB households: @@ -302,10 +316,10 @@ state_code: MO output: # mo_ssp formula reads ssi only. Negative SE income cannot inflate output; - # benefit is bounded by min(max(BP - ssi, 0), cap) = min(827, 698) = 698. - mo_ssp: 698 + # benefit is bounded by max(maximum payment - ssi, 0) = $827. + mo_ssp: 827 -- name: Case 18, FY2024 SAB grant with nominal SSI uses FY2024 cap. +- name: Case 18, FY2024 SAB grant with nominal SSI uses maximum payment minus SSI. period: 2024-01 absolute_error_margin: 0.01 input: @@ -313,16 +327,34 @@ person1: age: 50 is_blind: true - ssi: 12 # $1/month annualized; ssi > 0 required for eligibility + 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: - # FY2024 BP rate $789, cap $654. - # max(789 - 1, 0) = 788; min(788, 654) = 654 (cap binds). - mo_ssp: 654 + # 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 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 index 938543a7fa1..a3ae7cb4934 100644 --- 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 @@ -14,6 +14,24 @@ 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, adult in MO with no SSI receipt is not eligible. period: 2026-01 input: @@ -27,7 +45,8 @@ members: [person1] state_code: MO output: - # ssi > 0 gate fails when person is not actually receiving SSI. + # SNC still uses SSI receipt as the modeled proxy for federal/state + # categorical and income eligibility. mo_ssp_eligible: false - name: Case 3, adult receiving SSI not in MO is not eligible. @@ -225,7 +244,8 @@ members: [person1] state_code: MO output: - # ssi > 0 gate passes for any positive SSI receipt. + # SAB eligibility does not require positive SSI receipt, but positive SSI + # still satisfies the modeled SSI application/receipt proxy. mo_ssp_eligible: true - name: Case 15, adult with valid eligibility outside Missouri is not eligible. 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 index c5b5d11431c..b8700f9f3f3 100644 --- a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py @@ -10,7 +10,9 @@ class mo_ssp(Variable): 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=2", + "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", ) @@ -21,9 +23,11 @@ def formula(person, period, parameters): # 1973-conversion cohort. p = parameters(period).gov.states.mo.dss.ssp federal_ssi = person("ssi", period) - sab_grant = min_( - max_(p.sab.consolidated_standard - federal_ssi, 0), - p.sab.max_grant_cap, + 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 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 index d0d96d848b6..1d68bb45020 100644 --- 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 @@ -13,20 +13,25 @@ class mo_ssp_eligible(Variable): ) def formula(person, period, parameters): - # Missouri requires actual receipt of a federal SSI payment under 13 - # CSR 40-2.130; ssi > 0 already implies categorical / resource / - # immigration eligibility plus the federal income test and takeup. - # We don't track Missouri-specific resource limits, the closed - # 1973-conversion State Pension cohort, the Supplemental Aid to the - # Blind earned-income-limit calculation method, or the Supplemental - # Nursing Care physician medical-need or facility-cost-versus-income - # tests at the moment. - receives_ssi = person("ssi", period) > 0 + # SAB applicants must apply for or receive SSI, but the grant can be + # paid even when SSI is zero. We don't track that application + # requirement, Missouri-specific resource limits, the closed + # 1973-conversion State Pension cohort, or Supplemental Nursing Care + # physician medical-need and facility-cost-versus-income 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) is_sab = living_arrangement == living_arrangement.possible_values.SAB p = parameters(period).gov.states.mo.dss.ssp + receives_ssi = person("ssi", period) > 0 + ssi_receipt_requirement_met = is_sab | receives_ssi countable_income = person("ssi_countable_income", period) - income_gate = ~is_sab | (countable_income <= p.sab.income_limit) - return receives_ssi & in_category & age_eligible & income_gate + sab_income_eligible = ~is_sab | ( + countable_income <= p.sab.consolidated_standard + ) + return ( + in_category + & age_eligible + & ssi_receipt_requirement_met + & sab_income_eligible + ) From 4333edc85ba50fa9c7a0185009d9a0490f7d43ed Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 13 May 2026 14:07:19 -0400 Subject: [PATCH 07/10] fix --- .../gov/states/mo/dss/ssp/integration.yaml | 6 +++ .../gov/states/mo/dss/ssp/mo_ssp.yaml | 50 +++++++++++++++++++ .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 2 + .../ssp/mo_ssp_personal_needs_allowance.yaml | 9 ++++ .../mo/dss/ssp/mo_snc_countable_income.py | 19 +++++++ .../mo/dss/ssp/mo_snc_facility_base_charge.py | 14 ++++++ .../variables/gov/states/mo/dss/ssp/mo_ssp.py | 11 ++-- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 12 ++++- 8 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_facility_base_charge.py 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 index 82c9fd70210..2b7b5b6e8f6 100644 --- 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 @@ -41,6 +41,7 @@ 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] @@ -71,6 +72,7 @@ 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] @@ -170,6 +172,7 @@ 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] @@ -200,6 +203,7 @@ 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] @@ -230,11 +234,13 @@ 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] 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 index be8b02c7514..7ae25971a2a 100644 --- 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 @@ -79,6 +79,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -96,6 +97,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -113,6 +115,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -130,6 +133,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -365,6 +369,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -382,6 +387,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -418,6 +424,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -426,3 +433,46 @@ # 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_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_eligible.yaml index a3ae7cb4934..361fb7fff0e 100644 --- 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 @@ -156,6 +156,7 @@ age: 21 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -189,6 +190,7 @@ age: 21 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] 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 index b02a5d07147..9c44d156ccd 100644 --- 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 @@ -7,6 +7,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -23,6 +24,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -40,6 +42,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -74,6 +77,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -91,6 +95,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -126,6 +131,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_I + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -143,6 +149,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: SNF_OR_ICF_NON_MEDICAID + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -160,6 +167,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: RCF_LEVEL_II_OR_ALF + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] @@ -176,6 +184,7 @@ age: 70 ssi: 500 mo_ssp_living_arrangement: INTERMEDIATE_OR_SKILLED_NO_LOC + mo_snc_facility_base_charge: 1_000 households: household: members: [person1] 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..6698d483f16 --- /dev/null +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py @@ -0,0 +1,19 @@ +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 income from modeled cash " + "income sources; Missouri-specific 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 index b8700f9f3f3..787285916da 100644 --- a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_ssp.py @@ -32,20 +32,25 @@ def formula(person, period, parameters): living_arrangement = person("mo_ssp_living_arrangement", period) categories = living_arrangement.possible_values snc = p.snc.max_grant - base = select( + snc_max_grant = select( [ - living_arrangement == categories.SAB, 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, ], [ - sab_grant, 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 index 1d68bb45020..6c4e5427415 100644 --- 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 @@ -17,11 +17,15 @@ def formula(person, period, parameters): # paid even when SSI is zero. We don't track that application # requirement, Missouri-specific resource limits, the closed # 1973-conversion State Pension cohort, or Supplemental Nursing Care - # physician medical-need and facility-cost-versus-income tests. + # 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) - is_sab = living_arrangement == living_arrangement.possible_values.SAB + 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 receives_ssi = person("ssi", period) > 0 ssi_receipt_requirement_met = is_sab | receives_ssi @@ -29,9 +33,13 @@ def formula(person, period, parameters): 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 & ssi_receipt_requirement_met & sab_income_eligible + & snc_need_eligible ) From c11c8bd928f0eb4b5d5b414ca9be2f925c33fd65 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 13 May 2026 14:45:21 -0400 Subject: [PATCH 08/10] Remove SSI receipt gate from SNC eligibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SNC eligibility now relies on the facility-cost-vs-income need test rather than requiring federal SSI receipt as a proxy. The PR already models mo_snc_facility_base_charge and mo_snc_countable_income, so the gate is no longer needed — and was blocking real cases where a person's income is above the federal SSI limit but below their facility's base charge. Concrete case now eligible: age 70 RCF Level I resident with \$1,200/mo Social Security and a \$1,300/mo facility base charge — \$100 SNC need, producing \$100 + \$50 PNA = \$150 monthly grant. Adds tests for the new behavior and updates comments on existing tests that previously documented the removed SSI proxy. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../gov/states/mo/dss/ssp/mo_ssp.yaml | 10 +++--- .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 33 ++++++++++++++++--- .../ssp/mo_ssp_personal_needs_allowance.yaml | 4 ++- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 15 +++------ 4 files changed, 41 insertions(+), 21 deletions(-) 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 index 7ae25971a2a..2b2dea1deb9 100644 --- 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 @@ -142,7 +142,7 @@ # defined_for = StateCode.MO filters output to zero. mo_ssp: 0 -- name: Case 8, person not actually receiving SSI gets zero. +- 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: @@ -150,15 +150,17 @@ 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 still uses SSI receipt as the modeled proxy for federal/state - # categorical and income eligibility. - mo_ssp: 0 + # 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 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 index 361fb7fff0e..25d73c52b2a 100644 --- 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 @@ -32,23 +32,45 @@ # receive SSI, but that application requirement is not modeled. mo_ssp_eligible: true -- name: Case 2, adult in MO with no SSI receipt is not eligible. +- 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 still uses SSI receipt as the modeled proxy for federal/state - # categorical and income eligibility. + # 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: @@ -246,8 +268,9 @@ members: [person1] state_code: MO output: - # SAB eligibility does not require positive SSI receipt, but positive SSI - # still satisfies the modeled SSI application/receipt proxy. + # SAB eligibility is independent of SSI receipt; nominal SSI does not + # affect this — the SAB income test uses ssi_countable_income against the + # consolidated standard. mo_ssp_eligible: true - name: Case 15, adult with valid eligibility outside Missouri is not eligible. 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 index 9c44d156ccd..3b0c47d2c24 100644 --- 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 @@ -65,7 +65,9 @@ members: [person1] state_code: MO output: - # defined_for = mo_ssp_eligible filters output to zero when ssi == 0. + # 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. 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 index 6c4e5427415..6ac191037e2 100644 --- 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 @@ -13,8 +13,9 @@ class mo_ssp_eligible(Variable): ) def formula(person, period, parameters): - # SAB applicants must apply for or receive SSI, but the grant can be - # paid even when SSI is zero. We don't track that application + # 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. @@ -27,8 +28,6 @@ def formula(person, period, parameters): living_arrangement != categories.NONE ) p = parameters(period).gov.states.mo.dss.ssp - receives_ssi = person("ssi", period) > 0 - ssi_receipt_requirement_met = is_sab | receives_ssi countable_income = person("ssi_countable_income", period) sab_income_eligible = ~is_sab | ( countable_income <= p.sab.consolidated_standard @@ -36,10 +35,4 @@ def formula(person, period, parameters): 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 - & ssi_receipt_requirement_met - & sab_income_eligible - & snc_need_eligible - ) + return in_category & age_eligible & sab_income_eligible & snc_need_eligible From b74e7f79523eb984cb55eb5337f69d9e6070f1e8 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 13 May 2026 16:52:30 -0400 Subject: [PATCH 09/10] ? --- .../mo/dss/ssp/sab/consolidated_standard.yaml | 4 +- .../mo/dss/ssp/sab/maximum_payment.yaml | 6 +- .../mo/dss/ssp/mo_sab_countable_income.yaml | 45 +++++++ .../mo/dss/ssp/mo_ssp_age_eligible.yaml | 113 ++++++++++++++++++ .../mo/dss/ssp/mo_ssp_category_eligible.yaml | 101 ++++++++++++++++ .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 16 +-- .../mo/dss/ssp/mo_sab_countable_income.py | 52 ++++++++ .../mo/dss/ssp/mo_snc_countable_income.py | 5 +- .../gov/states/mo/dss/ssp/mo_ssp_eligible.py | 6 +- 9 files changed, 335 insertions(+), 13 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_sab_countable_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_age_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_category_eligible.yaml create mode 100644 policyengine_us/variables/gov/states/mo/dss/ssp/mo_sab_countable_income.py 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 index 05b9b564be6..37815206b89 100644 --- 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 @@ -1,6 +1,6 @@ description: Missouri provides this amount as the consolidated standard under the Supplemental Aid to the Blind program. values: - 1900-01-01: 0 + 2011-01-01: 510 2023-07-01: 789 2024-07-01: 828 2025-07-01: 917 @@ -16,3 +16,5 @@ metadata: 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 index 5821b4324f7..dfc3cc2a9a6 100644 --- 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 @@ -1,6 +1,6 @@ description: Missouri provides this amount as the maximum payment under the Supplemental Aid to the Blind program. values: - 1900-01-01: 0 + 2011-01-01: 510 2023-07-01: 789 2024-07-01: 828 2025-07-01: 917 @@ -18,4 +18,6 @@ metadata: - 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=4 + 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/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_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..6ebff47fcd2 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mo/dss/ssp/mo_ssp_category_eligible.yaml @@ -0,0 +1,101 @@ +- 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 is category-eligible regardless of is_blind. + 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_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 index 25d73c52b2a..2de247cc80c 100644 --- 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 @@ -269,8 +269,8 @@ state_code: MO output: # SAB eligibility is independent of SSI receipt; nominal SSI does not - # affect this — the SAB income test uses ssi_countable_income against the - # consolidated standard. + # 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. @@ -305,9 +305,9 @@ members: [person1] state_code: MO output: - # FY2026 SAB monthly income limit $1,073; ssi_countable_income for blind - # earner with $30,000 annual employment income exceeds the limit after - # federal SSI exclusions, so SAB pathway fails. + # 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. @@ -325,6 +325,8 @@ members: [person1] state_code: MO output: - # FY2026 SAB monthly income limit $1,073; $12,000 / 12 = $1,000 monthly - # earned income is within the limit so SAB pathway passes. + # 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 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 index 6698d483f16..1fe256cb6fd 100644 --- 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 @@ -9,8 +9,9 @@ class mo_snc_countable_income(Variable): definition_period = MONTH defined_for = StateCode.MO documentation = ( - "Approximates Supplemental Nursing Care income from modeled cash " - "income sources; Missouri-specific disregards are not modeled." + "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/", 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 index 6ac191037e2..3563ad1010a 100644 --- 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 @@ -28,7 +28,11 @@ def formula(person, period, parameters): living_arrangement != categories.NONE ) p = parameters(period).gov.states.mo.dss.ssp - countable_income = person("ssi_countable_income", period) + # 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 ) From ef4031ed0e12f90d8e2cb9db6b60513182eb5595 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 13 May 2026 17:09:53 -0400 Subject: [PATCH 10/10] fix --- .../mo/dss/ssp/mo_ssp_category_eligible.yaml | 33 ++++++++++++++++++- .../states/mo/dss/ssp/mo_ssp_eligible.yaml | 30 ++++++++++++++--- .../eligibility/mo_ssp_category_eligible.py | 11 +++++-- 3 files changed, 67 insertions(+), 7 deletions(-) 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 index 6ebff47fcd2..169a10c2afc 100644 --- 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 @@ -28,7 +28,7 @@ output: mo_ssp_category_eligible: false -- name: SNC RCF_LEVEL_I is category-eligible regardless of is_blind. +- name: SNC RCF_LEVEL_I aged resident is category-eligible without blindness. period: 2026-01 input: people: @@ -43,6 +43,37 @@ 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: 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 index 2de247cc80c..d04cb868a23 100644 --- 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 @@ -170,12 +170,13 @@ # SAB pathway requires is_blind. mo_ssp_eligible: false -- name: Case 9, adult age 21 in SNF non-Medicaid is eligible. +- 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 @@ -184,7 +185,7 @@ members: [person1] state_code: MO output: - # Exactly meets SNC age threshold. + # 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. @@ -204,12 +205,13 @@ # Exactly meets SAB age threshold of 18. mo_ssp_eligible: true -- name: Case 11, adult exactly age 21 with RCF Level I is eligible. +- 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 @@ -218,7 +220,7 @@ members: [person1] state_code: MO output: - # Exactly meets SNC age threshold of 21. + # 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. @@ -330,3 +332,23 @@ # $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/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 index 07feb094d18..4a0eed9580e 100644 --- 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 @@ -10,7 +10,10 @@ class mo_ssp_category_eligible(Variable): 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",) + 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) @@ -22,5 +25,9 @@ def formula(person, period, parameters): | (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 - return in_snc_facility | sab_pathway + snc_pathway = in_snc_facility & is_aged_blind_disabled + return snc_pathway | sab_pathway