From eb9bae665a73156df63925d6746c2c47a5c5080f Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 28 Apr 2026 17:08:45 -0400 Subject: [PATCH 01/17] Add changelog fragment for MD SSP --- changelog.d/md-ssp.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/md-ssp.added.md diff --git a/changelog.d/md-ssp.added.md b/changelog.d/md-ssp.added.md new file mode 100644 index 00000000000..ca6f5ee7cb7 --- /dev/null +++ b/changelog.d/md-ssp.added.md @@ -0,0 +1 @@ +Added Maryland State Supplementary Payment (SSP). From a1d8c08995d02ed2bf9703bc606e64c4ab471652 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 28 Apr 2026 18:12:59 -0400 Subject: [PATCH 02/17] Implement Maryland Public Assistance to Adults (state SSP) Co-Authored-By: Claude Opus 4.7 (1M context) --- changelog.d/md-ssp.added.md | 2 +- .../household/household_state_benefits.yaml | 4 + .../earned_income_disregard/initial.yaml | 14 + .../income/earned_income_disregard/rate.yaml | 14 + .../paa/income/unearned_income_disregard.yaml | 14 + .../dhs/fia/paa/personal_needs_allowance.yaml | 21 ++ .../states/md/dhs/fia/paa/provider_rate.yaml | 30 ++ policyengine_us/programs.yaml | 5 + .../states/md/dhs/fia/paa/integration.yaml | 311 ++++++++++++++++++ .../gov/states/md/dhs/fia/paa/md_paa.yaml | 117 +++++++ .../paa/md_paa_countable_earned_income.yaml | 112 +++++++ .../paa/md_paa_countable_unearned_income.yaml | 95 ++++++ .../md/dhs/fia/paa/md_paa_eligible.yaml | 63 ++++ .../paa/md_paa_personal_needs_allowance.yaml | 86 +++++ .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 140 ++++++++ .../gov/states/md/dhs/fia/paa/md_paa.py | 19 ++ .../fia/paa/md_paa_countable_earned_income.py | 27 ++ .../md/dhs/fia/paa/md_paa_countable_income.py | 18 + .../paa/md_paa_countable_unearned_income.py | 26 ++ .../states/md/dhs/fia/paa/md_paa_eligible.py | 20 ++ .../dhs/fia/paa/md_paa_living_arrangement.py | 26 ++ .../paa/md_paa_personal_needs_allowance.py | 17 + .../md/dhs/fia/paa/md_paa_provider_rate.py | 20 ++ .../dhs/fia/paa/md_paa_total_cost_of_care.py | 12 + .../income/spm_unit/spm_unit_benefits.py | 1 + 25 files changed, 1213 insertions(+), 1 deletion(-) create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py diff --git a/changelog.d/md-ssp.added.md b/changelog.d/md-ssp.added.md index ca6f5ee7cb7..34373543d7e 100644 --- a/changelog.d/md-ssp.added.md +++ b/changelog.d/md-ssp.added.md @@ -1 +1 @@ -Added Maryland State Supplementary Payment (SSP). +Add Maryland Public Assistance to Adults (state SSP). diff --git a/policyengine_us/parameters/gov/household/household_state_benefits.yaml b/policyengine_us/parameters/gov/household/household_state_benefits.yaml index 619bf682302..15e83d4a45e 100644 --- a/policyengine_us/parameters/gov/household/household_state_benefits.yaml +++ b/policyengine_us/parameters/gov/household/household_state_benefits.yaml @@ -12,6 +12,8 @@ values: - ga_ssp # Massachusetts benefits - ma_state_supplement + # Maryland benefits + - md_paa # Colorado benefits - co_state_supplement - co_oap @@ -67,6 +69,8 @@ values: - ga_ssp # Massachusetts benefits - ma_state_supplement + # Maryland benefits + - md_paa # Colorado benefits - co_state_supplement - co_oap diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml new file mode 100644 index 00000000000..f77cb9286cc --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml @@ -0,0 +1,14 @@ +description: Maryland excludes this initial amount of earned income from countable income under the Public Assistance to Adults program. + +values: + 1974-07-01: 85 + +metadata: + unit: currency-USD + period: month + label: Maryland PAA earned income initial disregard + reference: + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx + - title: COMAR 07.03.07.09 Amount of Grant and Payee + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml new file mode 100644 index 00000000000..7c6947dda66 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml @@ -0,0 +1,14 @@ +description: Maryland excludes this share of remaining earned income from countable income under the Public Assistance to Adults program. + +values: + 1974-07-01: 0.5 + +metadata: + unit: /1 + period: year + label: Maryland PAA earned income disregard rate + reference: + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx + - title: COMAR 07.03.07.09 Amount of Grant and Payee + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml new file mode 100644 index 00000000000..47e9de9dddc --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml @@ -0,0 +1,14 @@ +description: Maryland excludes this amount of unearned income from countable income under the Public Assistance to Adults program. + +values: + 1974-07-01: 20 + +metadata: + unit: currency-USD + period: month + label: Maryland PAA unearned income disregard + reference: + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx + - title: COMAR 07.03.07.09 Amount of Grant and Payee + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml new file mode 100644 index 00000000000..ddd320dae92 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -0,0 +1,21 @@ +description: Maryland provides this amount as the personal needs allowance per recipient under the Public Assistance to Adults program. + +values: + 2023-01-01: 93 + # Effective date unverified: confirmed only by IM 26-04 stating PNA "increased + # from $102 to $106" effective Jul 1, 2025. The transmittal that raised PNA + # from $93 to $102 has not been located; 2024-07-01 is a placeholder. + 2024-07-01: 102 + 2025-07-01: 106 + +metadata: + unit: currency-USD + period: month + label: Maryland PAA personal needs allowance + reference: + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 + - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2 + - title: COMAR 07.03.07.04 Allowable Needs + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml new file mode 100644 index 00000000000..11ba9cc8ce0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -0,0 +1,30 @@ +description: Maryland provides this amount as the monthly provider rate by living arrangement under the Public Assistance to Adults program. + +metadata: + unit: currency-USD + period: month + label: Maryland PAA provider rate + breakdown: + - md_paa_living_arrangement + reference: + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, Master PAA Rates and Per Diems Chart + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 + - title: COMAR 07.03.07.04 Allowable Needs + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 + - title: COMAR 07.03.07.09 Amount of Grant and Payee + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + +CARE_HOME_LEVEL_A: + 2021-01-01: 776 +CARE_HOME_LEVEL_B: + 2021-01-01: 875 +CARE_HOME_LEVEL_C: + 2021-01-01: 1_173 +CARE_HOME_LEVEL_D: + 2021-01-01: 1_376 +ASSISTED_LIVING: + 2021-01-01: 894 +REHAB_RESIDENCE: + 2021-01-01: 0 +NONE: + 2021-01-01: 0 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 801129254f1..8e90cb3cd89 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -651,6 +651,11 @@ programs: name: Massachusetts SSP full_name: Massachusetts State Supplementary Payment variable: ma_state_supplement + - state: MD + status: complete + name: Maryland PAA + full_name: Maryland Public Assistance to Adults + variable: md_paa - state: KS status: complete name: Kansas SSPP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml new file mode 100644 index 00000000000..a96bd0f5d86 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -0,0 +1,311 @@ +# Integration tests for Maryland Public Assistance to Adults (PAA). +# Verifies the full federal SSI → state PAA chain using real income +# variables (social_security, employment_income), not pre-set ssi values. +# All scenarios use 2026-01 (PNA = $106 after 2025-07-01 effective date). + +- name: Case 1, single SSI recipient in CARE_HOME_LEVEL_B with no other income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # --- Federal SSI --- + # Aged, no other income → full FBR ($994/mo in 2026). + is_ssi_eligible: [true] + ssi: [994] + + # --- PAA eligibility --- + md_paa_eligible: [true] + + # --- PAA income (PAA excludes SSI from countable income) --- + md_paa_countable_earned_income: [0] + md_paa_countable_unearned_income: [0] + md_paa_countable_income: [0] + + # --- PAA benefit --- + # provider_rate(875) + PNA(106) = 981 total cost of care. + # PAA = max(981 - 0, 0) = 981. + md_paa_provider_rate: [875] + md_paa_personal_needs_allowance: [106] + md_paa_total_cost_of_care: [981] + md_paa: [981] + +- name: Case 2, SSI recipient with $200/mo earned income in CARE_HOME_LEVEL_C. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + employment_income: 2_400 # $200/mo + md_paa_living_arrangement: CARE_HOME_LEVEL_C + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # --- Federal SSI --- + # Federal countable = max(0, 200 - 65 - 20) * 0.5 = 115 * 0.5 = 57.50. + # SSI = 994 - 57.50 = 936.50/mo. + is_ssi_eligible: [true] + ssi: [936.50] + + # --- PAA eligibility --- + md_paa_eligible: [true] + + # --- PAA income (uses $85 earned disregard, NOT $65) --- + # countable_earned = max(200 - 85, 0) * 0.5 = 115 * 0.5 = 57.50. + # countable_unearned = 0 (PAA excludes SSI). + md_paa_countable_earned_income: [57.50] + md_paa_countable_unearned_income: [0] + md_paa_countable_income: [57.50] + + # --- PAA benefit --- + # provider_rate(1173) + PNA(106) = 1279 total cost of care. + # PAA = max(1279 - 57.50, 0) = 1221.50. + md_paa_provider_rate: [1_173] + md_paa_total_cost_of_care: [1_279] + md_paa: [1_221.50] + +- name: Case 3, SSI recipient with $400/mo other unearned income in ASSISTED_LIVING. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 800 + social_security: 4_800 # $400/mo non-SSI unearned + employment_income: 0 + md_paa_living_arrangement: ASSISTED_LIVING + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # --- Federal SSI --- + # Federal countable unearned = 400 - 20 = 380/mo. + # SSI = 994 - 380 = 614/mo. + is_ssi_eligible: [true] + ssi: [614] + + # --- PAA eligibility --- + md_paa_eligible: [true] + + # --- PAA income --- + # countable_unearned = max(400 - 20, 0) = 380 (PAA excludes SSI itself). + md_paa_countable_earned_income: [0] + md_paa_countable_unearned_income: [380] + md_paa_countable_income: [380] + + # --- PAA benefit --- + # provider_rate(894) + PNA(106) = 1000 total cost of care. + # PAA = max(1000 - 380, 0) = 620. + md_paa_provider_rate: [894] + md_paa_total_cost_of_care: [1_000] + md_paa: [620] + +- name: Case 4, REHAB_RESIDENCE recipient receives PNA only. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + employment_income: 0 + md_paa_living_arrangement: REHAB_RESIDENCE + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # --- Federal SSI --- + is_ssi_eligible: [true] + ssi: [994] + + # --- PAA benefit --- + # MDH pays provider cost; PAA pays only the PNA. + # provider_rate(0) + PNA(106) = 106. countable = 0. PAA = 106. + md_paa_eligible: [true] + md_paa_provider_rate: [0] + md_paa_personal_needs_allowance: [106] + md_paa: [106] + +- name: Case 5, married couple both eligible in CARE_HOME_LEVEL_A treated as two individuals. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_A + person2: + age: 68 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_A + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MD + output: + # Per SSA 2011 (Maryland): couples treated as two individuals. + # No couple-rate reduction — each spouse gets the individual provider rate. + md_paa_eligible: [true, true] + md_paa_provider_rate: [776, 776] + md_paa_personal_needs_allowance: [106, 106] + # Each: provider(776) + PNA(106) - countable(0) = 882. + md_paa: [882, 882] + +- name: Case 6, person not in PAA facility (NONE) is ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + employment_income: 0 + md_paa_living_arrangement: NONE + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # SSI eligible but not in PAA facility → PAA ineligible. + is_ssi_eligible: [true] + ssi: [994] + md_paa_eligible: [false] + md_paa: [0] + +- name: Case 7, countable income equals total cost of care yields zero benefit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + # $1001/mo unearned: countable = 1001 - 20 = 981 = total cost of care + # for CARE_HOME_LEVEL_B (875 + 106 = 981). + social_security: 12_012 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # --- Federal SSI --- + # Countable unearned = 1001 - 20 = 981 > 994 FBR? No: 981 < 994 → SSI = 13. + is_ssi_eligible: [true] + ssi: [13] + + # --- PAA --- + # countable_unearned = max(1001 - 20, 0) = 981. + # total_cost_of_care = 875 + 106 = 981. + # PAA = max(981 - 981, 0) = 0 (exactly at threshold). + md_paa_eligible: [true] + md_paa_countable_unearned_income: [981] + md_paa_total_cost_of_care: [981] + md_paa: [0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml new file mode 100644 index 00000000000..9e2e3ba4e53 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -0,0 +1,117 @@ +# Tests for md_paa main benefit variable. +# Formula: max(provider_rate + PNA - countable_income, 0) when eligible. +# Period: 2026-01 used so PNA = $106 (after 2025-07-01 effective date). + +- name: Case 1, eligible CARE_HOME_LEVEL_B with no other income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi_earned_income: 0 + ssi_unearned_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + # provider_rate(875) + PNA(106) - countable(0) = 981 + md_paa: 981 + +- name: Case 2, eligible REHAB_RESIDENCE receives PNA only (MDH covers cost). + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi_earned_income: 0 + ssi_unearned_income: 0 + md_paa_living_arrangement: REHAB_RESIDENCE + households: + household: + members: [person1] + state_code: MD + output: + # provider_rate(0) + PNA(106) - countable(0) = 106 + md_paa: 106 + +- name: Case 3, ineligible (NONE living arrangement). + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + md_paa_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MD + output: + md_paa: 0 + +- name: Case 4, eligible CARE_HOME_LEVEL_A with $300/mo unearned income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi_earned_income: 0 + # $300/mo * 12 = $3,600/yr. + ssi_unearned_income: 3_600 + md_paa_living_arrangement: CARE_HOME_LEVEL_A + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 3600/12 = 300. + # countable_unearned: max(300 - 20, 0) = 280. + # provider_rate(776) + PNA(106) - countable(280) = 602. + md_paa: 602 + +- name: Case 5, negative countable income does not inflate benefit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + # Self-employment loss should not reduce countable income below zero. + self_employment_income: -60_000_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + # Negative earned income is floored at 0 by max_(earned - 85, 0) * 0.5. + # provider_rate(875) + PNA(106) - 0 = 981. + md_paa: 981 + +# Out-of-state SSI recipient in a PAA-style facility is filtered out by +# md_paa_eligible's `defined_for = StateCode.MD`. +- name: Case 6, out-of-state recipient is ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: CA + output: + md_paa: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml new file mode 100644 index 00000000000..39c86baa603 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml @@ -0,0 +1,112 @@ +# Tests for md_paa_countable_earned_income variable. +# Formula: max(earned - $85, 0) * 0.5 (per PAA Manual §500.3, AT 23-02). +# Note: PAA earned disregard is $85 (NOT $65 like federal SSI). +# ssi_earned_income is YEAR-defined; inputs are annual (= 12 * monthly). + +- name: Case 1, zero earned income. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi_earned_income: 0 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 0/12 = 0. + # max(0 - 85, 0) * 0.5 = 0 + md_paa_countable_earned_income: 0 + +- name: Case 2, earned exactly at $85/mo disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + # $85/mo * 12 = $1,020/yr. + ssi_earned_income: 1_020 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 1020/12 = 85. + # max(85 - 85, 0) * 0.5 = 0 + md_paa_countable_earned_income: 0 + +- name: Case 3, earned $185/mo (above disregard). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $185/mo * 12 = $2,220/yr. + ssi_earned_income: 2_220 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 2220/12 = 185. + # max(185 - 85, 0) * 0.5 = 100 * 0.5 = 50 + md_paa_countable_earned_income: 50 + +- name: Case 4, earned $50/mo (below initial disregard). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + # $50/mo * 12 = $600/yr. + ssi_earned_income: 600 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 600/12 = 50. + # max(50 - 85, 0) * 0.5 = 0 (floored at 0) + md_paa_countable_earned_income: 0 + +# Boundary cases around the $85 initial disregard. +- name: Case 5, earned $84/mo just below initial disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $84/mo * 12 = $1,008/yr. + ssi_earned_income: 1_008 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 1008/12 = 84. + # max(84 - 85, 0) * 0.5 = 0 (max_ floors at 0) + md_paa_countable_earned_income: 0 + +- name: Case 6, earned $86/mo just above initial disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $86/mo * 12 = $1,032/yr. + ssi_earned_income: 1_032 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly earned = 1032/12 = 86. + # max(86 - 85, 0) * 0.5 = 1 * 0.5 = 0.5 + md_paa_countable_earned_income: 0.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml new file mode 100644 index 00000000000..42f6707f786 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml @@ -0,0 +1,95 @@ +# Tests for md_paa_countable_unearned_income variable. +# Formula: max(unearned - $20, 0) (per PAA Manual §500.3). +# $20 disregard matches federal SSI universal disregard. +# ssi_unearned_income is YEAR-defined; inputs are annual (= 12 * monthly). + +- name: Case 1, zero unearned income. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi_unearned_income: 0 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 0/12 = 0. + # max(0 - 20, 0) = 0 + md_paa_countable_unearned_income: 0 + +- name: Case 2, unearned exactly at $20/mo disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + # $20/mo * 12 = $240/yr. + ssi_unearned_income: 240 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 240/12 = 20. + # max(20 - 20, 0) = 0 + md_paa_countable_unearned_income: 0 + +- name: Case 3, unearned $300/mo (above disregard). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $300/mo * 12 = $3,600/yr. + ssi_unearned_income: 3_600 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 3600/12 = 300. + # max(300 - 20, 0) = 280 + md_paa_countable_unearned_income: 280 + +# Boundary cases around the $20 unearned disregard. +- name: Case 4, unearned $19/mo just below disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $19/mo * 12 = $228/yr. + ssi_unearned_income: 228 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 228/12 = 19. + # max(19 - 20, 0) = 0 + md_paa_countable_unearned_income: 0 + +- name: Case 5, unearned $21/mo just above disregard. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $21/mo * 12 = $252/yr. + ssi_unearned_income: 252 + households: + household: + members: [person1] + state_code: MD + output: + # Monthly unearned = 252/12 = 21. + # max(21 - 20, 0) = 1 + md_paa_countable_unearned_income: 1 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml new file mode 100644 index 00000000000..a24380c65b2 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -0,0 +1,63 @@ +# Tests for md_paa_eligible variable. +# Eligibility requires: ssi > 0 AND md_paa_living_arrangement != NONE +# AND state_code = MD (defined_for filter). + +- name: Case 1, SSI recipient in CARE_HOME_LEVEL_B in Maryland is eligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +- name: Case 2, no living arrangement (NONE) is ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + md_paa_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +- name: Case 3, no SSI receipt is ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +- name: Case 4, out-of-state resident is ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: CA + output: + md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml new file mode 100644 index 00000000000..a46439697fb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -0,0 +1,86 @@ +# Tests for md_paa_personal_needs_allowance variable. +# Tests parameter values across the timeline: +# 2023-01-01: $93 +# 2024-07-01: $102 (effective date unverified) +# 2025-07-01: $106 + +- name: Case 1, 2023 PNA. + period: 2023-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 93 + +- name: Case 2, 2025 January PNA (after 2024-07-01 effective date). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 102 + +- name: Case 3, 2026 January PNA (after 2025-07-01 effective date). + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 106 + +# Boundary just before the 2024-07-01 transition: January 2024 should still +# return $93 (the 2023-01-01 value) because the PNA increased only mid-year. +# Test framework only supports YYYY-MM at the first month, so January is +# the closest pre-transition checkpoint within 2024. +- name: Case 4, January 2024 PNA still 93 (before 2024-07-01 transition). + period: 2024-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 93 + +# Pre-2023 dates use forward-fill: parameter starts 2023-01-01 with $93, +# so any earlier period also returns $93. +- name: Case 5, pre-2023 PNA uses forward-filled value. + period: 2022-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 93 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml new file mode 100644 index 00000000000..e655f2e5deb --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -0,0 +1,140 @@ +# Tests for md_paa_provider_rate variable. +# Rate is determined by md_paa_living_arrangement enum. +# Each tier has at least one test (per testing-patterns: every dimension value). +# Rates effective 2021-01-01 onwards (per AT 23-02). + +- name: Case 1, CARE_HOME_LEVEL_A. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_A + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 776 + +- name: Case 2, CARE_HOME_LEVEL_B. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 875 + +- name: Case 3, CARE_HOME_LEVEL_C. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 1_173 + +- name: Case 4, CARE_HOME_LEVEL_D. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_D + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 1_376 + +- name: Case 5, ASSISTED_LIVING. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: ASSISTED_LIVING + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 894 + +- name: Case 6, REHAB_RESIDENCE pays no provider rate (MDH covers cost). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + md_paa_living_arrangement: REHAB_RESIDENCE + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 0 + +- name: Case 7, NONE living arrangement returns zero. + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + md_paa_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 0 + +# Pre-2021 dates use forward-fill: rates were first set 2021-01-01 in the +# parameter file, so PolicyEngine returns the 2021 value for any earlier +# period (rates have been frozen since 2021). +- name: Case 8, pre-2021 period uses forward-filled CARE_HOME_LEVEL_C rate. + period: 2020-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 1_173 + +- name: Case 9, pre-2021 period uses forward-filled ASSISTED_LIVING rate. + period: 2018-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: ASSISTED_LIVING + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 894 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py new file mode 100644 index 00000000000..2e6c5206f8e --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class md_paa(Variable): + value_type = float + entity = Person + label = "Maryland Public Assistance to Adults" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + ) + + def formula(person, period, parameters): + total_cost_of_care = person("md_paa_total_cost_of_care", period) + countable_income = person("md_paa_countable_income", period) + return max_(total_cost_of_care - countable_income, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py new file mode 100644 index 00000000000..1f9b52c42b1 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * + + +class md_paa_countable_earned_income(Variable): + value_type = float + entity = Person + label = "Maryland PAA countable earned income" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.md.dhs.fia.paa.income.earned_income_disregard + earned = add( + person, + period, + [ + "ssi_earned_income", + "ssi_earned_income_deemed_from_ineligible_spouse", + ], + ) + after_initial = max_(earned - p.initial, 0) + return after_initial * (1 - p.rate) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py new file mode 100644 index 00000000000..531a554eb51 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class md_paa_countable_income(Variable): + value_type = float + entity = Person + label = "Maryland PAA countable income" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + ) + adds = [ + "md_paa_countable_earned_income", + "md_paa_countable_unearned_income", + ] diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py new file mode 100644 index 00000000000..9bbb3c34832 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class md_paa_countable_unearned_income(Variable): + value_type = float + entity = Person + label = "Maryland PAA countable unearned income" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.md.dhs.fia.paa.income + unearned = add( + person, + period, + [ + "ssi_unearned_income", + "ssi_unearned_income_deemed_from_ineligible_spouse", + ], + ) + return max_(unearned - p.unearned_income_disregard, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py new file mode 100644 index 00000000000..f6e498c8096 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class md_paa_eligible(Variable): + value_type = bool + entity = Person + label = "Maryland PAA eligible" + definition_period = MONTH + defined_for = StateCode.MD + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-03", + ) + + def formula(person, period, parameters): + # PAA Manual §300.2 requires actual SSI receipt, not just SSI eligibility. + receives_ssi = person("ssi", period) > 0 + living_arrangement = person("md_paa_living_arrangement", period) + in_facility = living_arrangement != living_arrangement.possible_values.NONE + return receives_ssi & in_facility diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py new file mode 100644 index 00000000000..cbb3b30c266 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class MDPAALivingArrangement(Enum): + CARE_HOME_LEVEL_A = "CARE Home Level A" + CARE_HOME_LEVEL_B = "CARE Home Level B" + CARE_HOME_LEVEL_C = "CARE Home Level C" + CARE_HOME_LEVEL_D = "CARE Home Level D" + ASSISTED_LIVING = "Licensed Assisted Living" + REHAB_RESIDENCE = "MDH Rehabilitative Residence" + NONE = "None" + + +class md_paa_living_arrangement(Variable): + value_type = Enum + entity = Person + label = "Maryland PAA living arrangement" + definition_period = MONTH + defined_for = StateCode.MD + possible_values = MDPAALivingArrangement + default_value = MDPAALivingArrangement.NONE + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20400%20Allowable%20Needs%20rev%2011.22.docx", + "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", + ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py new file mode 100644 index 00000000000..7803b3df3f7 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class md_paa_personal_needs_allowance(Variable): + value_type = float + entity = Person + label = "Maryland PAA personal needs allowance" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2", + ) + + def formula(person, period, parameters): + return parameters(period).gov.states.md.dhs.fia.paa.personal_needs_allowance diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py new file mode 100644 index 00000000000..efdb34e3783 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class md_paa_provider_rate(Variable): + value_type = float + entity = Person + label = "Maryland PAA provider rate" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + ) + + def formula(person, period, parameters): + living_arrangement = person("md_paa_living_arrangement", period) + return parameters(period).gov.states.md.dhs.fia.paa.provider_rate[ + living_arrangement + ] diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py new file mode 100644 index 00000000000..2256e2f4a59 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class md_paa_total_cost_of_care(Variable): + value_type = float + entity = Person + label = "Maryland PAA total cost of care" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx" + adds = ["md_paa_provider_rate", "md_paa_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 9fdb27b3401..81a847cb55f 100644 --- a/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py +++ b/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py @@ -24,6 +24,7 @@ def formula(spm_unit, period, parameters): "ks_sspp", # Kansas benefits "hi_oss", "ma_state_supplement", # Massachusetts benefits + "md_paa", # Maryland benefits # California programs. "ca_cvrp", # California Clean Vehicle Rebate Project. # Colorado programs. From b36ff02ad1979ad88abeb30422cb1d6e8aa58d99 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 28 Apr 2026 20:19:01 -0400 Subject: [PATCH 03/17] Review-fix round 1: address 5 critical regulatory issues - Fix earned-income disregard cascade per COMAR 07.03.07.08(A): apply $20 to unearned first, leftover (up to $20) cascades into earned; earned floor is $65 + leftover (matches federal SSI pattern) - Fix REHAB_RESIDENCE provider rate: cap at $54/day per AT 23-02 page 3 ($1,642.50/month) instead of $0 - Apply cost-of-care disregard for REHAB residents per COMAR 07.03.07.08(B) - Update COMAR citations from .09 to .08 for income disregards Co-Authored-By: Claude Opus 4.7 (1M context) --- .../earned_income_disregard/initial.yaml | 6 +- .../income/earned_income_disregard/rate.yaml | 4 +- .../paa/income/unearned_income_disregard.yaml | 4 +- .../paa/provider_rate_rehab_daily_cap.yaml | 14 +++ .../states/md/dhs/fia/paa/integration.yaml | 98 +++++++++++++++++-- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 9 +- .../paa/md_paa_countable_earned_income.yaml | 94 +++++++++++++++--- .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 7 +- .../fia/paa/md_paa_countable_earned_income.py | 20 +++- .../md/dhs/fia/paa/md_paa_countable_income.py | 18 +++- .../paa/md_paa_countable_unearned_income.py | 2 +- .../md/dhs/fia/paa/md_paa_provider_rate.py | 10 +- 12 files changed, 239 insertions(+), 47 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml index f77cb9286cc..92517e85e0e 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml @@ -1,7 +1,7 @@ description: Maryland excludes this initial amount of earned income from countable income under the Public Assistance to Adults program. values: - 1974-07-01: 85 + 1974-07-01: 65 metadata: unit: currency-USD @@ -10,5 +10,5 @@ metadata: reference: - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.09 Amount of Grant and Payee - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + - title: COMAR 07.03.07.08(A)(3) Income Disregards + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml index 7c6947dda66..2b5263328cc 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml @@ -10,5 +10,5 @@ metadata: reference: - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.09 Amount of Grant and Payee - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + - title: COMAR 07.03.07.08(A)(1) Income Disregards + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml index 47e9de9dddc..462c19804ab 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml @@ -10,5 +10,5 @@ metadata: reference: - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.09 Amount of Grant and Payee - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + - title: COMAR 07.03.07.08(A)(2) Income Disregards + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml new file mode 100644 index 00000000000..e1831a4194f --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml @@ -0,0 +1,14 @@ +description: Maryland caps the daily provider rate for MDH Rehabilitative Residence cases at this amount under the Public Assistance to Adults program. + +values: + 2023-01-01: 54 + +metadata: + unit: currency-USD + period: day + label: Maryland PAA Rehabilitative Residence daily provider rate cap + reference: + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 + - title: COMAR 07.03.07.04 Allowable Needs + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index a96bd0f5d86..dcfd7d7b2d8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -87,7 +87,7 @@ # --- PAA eligibility --- md_paa_eligible: [true] - # --- PAA income (uses $85 earned disregard, NOT $65) --- + # --- PAA income (COMAR 07.03.07.08(A)(1): no unearned → $85 effective disregard) --- # countable_earned = max(200 - 85, 0) * 0.5 = 115 * 0.5 = 57.50. # countable_unearned = 0 (PAA excludes SSI). md_paa_countable_earned_income: [57.50] @@ -151,7 +151,7 @@ md_paa_total_cost_of_care: [1_000] md_paa: [620] -- name: Case 4, REHAB_RESIDENCE recipient receives PNA only. +- name: Case 4, REHAB_RESIDENCE recipient receives cost of care plus PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -183,12 +183,15 @@ ssi: [994] # --- PAA benefit --- - # MDH pays provider cost; PAA pays only the PNA. - # provider_rate(0) + PNA(106) = 106. countable = 0. PAA = 106. + # Per AT 23-02 page 3, REHAB allowable need = cost of rehab residence + # (capped at $54/day = $1,642.50/mo) + PNA ($106). + # Per COMAR 07.03.07.08(B), a cost-of-care disregard reduces income. + # countable_income = max(0 - 1642.50, 0) = 0. + # PAA = max(1642.50 + 106 - 0, 0) = 1,748.50. md_paa_eligible: [true] - md_paa_provider_rate: [0] + md_paa_provider_rate: [1_642.50] md_paa_personal_needs_allowance: [106] - md_paa: [106] + md_paa: [1_748.50] - name: Case 5, married couple both eligible in CARE_HOME_LEVEL_A treated as two individuals. period: 2026-01 @@ -309,3 +312,86 @@ md_paa_countable_unearned_income: [981] md_paa_total_cost_of_care: [981] md_paa: [0] + +- name: Case 8, COMAR 07.03.07.08(A)(3) cascade with both earned and unearned income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + # $185/mo earned ($2,220/yr) and $300/mo unearned ($3,600/yr). + ssi_earned_income: 2_220 + ssi_unearned_income: 3_600 + ssi: 9_600 # gates md_paa_eligible + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: [true] + # Per COMAR 07.03.07.08(A)(3): unearned >= $20 → leftover_general = 0, + # effective earned disregard = $65 (NOT $85). + # countable_earned = max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60. + # countable_unearned = max(300 - 20, 0) = 280. + md_paa_countable_earned_income: [60] + md_paa_countable_unearned_income: [280] + md_paa_countable_income: [340] + # provider_rate(875) + PNA(106) = 981 total cost of care. + # PAA = max(981 - 340, 0) = 641. + md_paa: [641] + +- name: Case 9, COMAR 07.03.07.08(A)(1) earned-only with no unearned ($85 disregard). + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 500 + # $185/mo earned with zero unearned income. + ssi_earned_income: 2_220 + ssi_unearned_income: 0 + ssi: 9_600 # gates md_paa_eligible + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: [true] + # Per COMAR 07.03.07.08(A)(1): no unearned → leftover_general = $20, + # effective earned disregard = $85. + # countable_earned = max(185 - 85, 0) * 0.5 = 100 * 0.5 = 50. + md_paa_countable_earned_income: [50] + md_paa_countable_unearned_income: [0] + md_paa_countable_income: [50] + # provider_rate(875) + PNA(106) = 981 total cost of care. + # PAA = max(981 - 50, 0) = 931. + md_paa: [931] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index 9e2e3ba4e53..5444dd5785b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -21,7 +21,7 @@ # provider_rate(875) + PNA(106) - countable(0) = 981 md_paa: 981 -- name: Case 2, eligible REHAB_RESIDENCE receives PNA only (MDH covers cost). +- name: Case 2, eligible REHAB_RESIDENCE receives cost of care up to $54/day plus PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -37,8 +37,11 @@ members: [person1] state_code: MD output: - # provider_rate(0) + PNA(106) - countable(0) = 106 - md_paa: 106 + # Per AT 23-02 page 3, REHAB allowable need = cost of rehab residence + # (capped at $54/day = 54 * 365 / 12 = $1,642.50/mo) + PNA ($106). + # countable_income = max(0 - 1642.50, 0) = 0 (cost-of-care disregard). + # provider_rate(1642.50) + PNA(106) - countable(0) = 1,748.50. + md_paa: 1_748.50 - name: Case 3, ineligible (NONE living arrangement). period: 2026-01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml index 39c86baa603..a0477556aef 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml @@ -1,7 +1,12 @@ # Tests for md_paa_countable_earned_income variable. -# Formula: max(earned - $85, 0) * 0.5 (per PAA Manual §500.3, AT 23-02). -# Note: PAA earned disregard is $85 (NOT $65 like federal SSI). -# ssi_earned_income is YEAR-defined; inputs are annual (= 12 * monthly). +# Implements the COMAR 07.03.07.08(A) cascade: +# leftover_general = max($20 - unearned, 0) +# effective_earned_disregard = $65 + leftover_general +# earned_after_disregard = max(earned - effective_earned_disregard, 0) +# countable_earned = earned_after_disregard * 0.5 +# When unearned = 0: effective disregard = $85 (matches (A)(1)) +# When unearned >= $20: effective disregard = $65 (matches (A)(3)) +# ssi_earned_income / ssi_unearned_income are YEAR-defined; inputs are annual. - name: Case 1, zero earned income. period: 2025-01 @@ -15,11 +20,9 @@ members: [person1] state_code: MD output: - # Monthly earned = 0/12 = 0. - # max(0 - 85, 0) * 0.5 = 0 md_paa_countable_earned_income: 0 -- name: Case 2, earned exactly at $85/mo disregard. +- name: Case 2, earned exactly at $85/mo disregard with no unearned. period: 2025-01 absolute_error_margin: 0.01 input: @@ -32,11 +35,11 @@ members: [person1] state_code: MD output: - # Monthly earned = 1020/12 = 85. + # Monthly earned = 85, unearned = 0 → effective disregard = $85. # max(85 - 85, 0) * 0.5 = 0 md_paa_countable_earned_income: 0 -- name: Case 3, earned $185/mo (above disregard). +- name: Case 3, earned $185/mo with no unearned (matches COMAR 07.03.07.08(A)(1)). period: 2025-01 absolute_error_margin: 0.01 input: @@ -51,7 +54,7 @@ members: [person1] state_code: MD output: - # Monthly earned = 2220/12 = 185. + # No unearned → leftover_general = $20, effective disregard = $85. # max(185 - 85, 0) * 0.5 = 100 * 0.5 = 50 md_paa_countable_earned_income: 50 @@ -68,11 +71,10 @@ members: [person1] state_code: MD output: - # Monthly earned = 600/12 = 50. - # max(50 - 85, 0) * 0.5 = 0 (floored at 0) + # No unearned → effective disregard = $85. + # max(50 - 85, 0) * 0.5 = 0 md_paa_countable_earned_income: 0 -# Boundary cases around the $85 initial disregard. - name: Case 5, earned $84/mo just below initial disregard. period: 2025-01 absolute_error_margin: 0.01 @@ -88,8 +90,6 @@ members: [person1] state_code: MD output: - # Monthly earned = 1008/12 = 84. - # max(84 - 85, 0) * 0.5 = 0 (max_ floors at 0) md_paa_countable_earned_income: 0 - name: Case 6, earned $86/mo just above initial disregard. @@ -107,6 +107,68 @@ members: [person1] state_code: MD output: - # Monthly earned = 1032/12 = 86. - # max(86 - 85, 0) * 0.5 = 1 * 0.5 = 0.5 + # max(86 - 85, 0) * 0.5 = 0.5 md_paa_countable_earned_income: 0.5 + +- name: Case 7, earned $185/mo with $300/mo unearned (matches COMAR 07.03.07.08(A)(3)). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $185/mo * 12 = $2,220/yr. + ssi_earned_income: 2_220 + # $300/mo * 12 = $3,600/yr. + ssi_unearned_income: 3_600 + households: + household: + members: [person1] + state_code: MD + output: + # Unearned >= $20 → leftover_general = 0, effective disregard = $65. + # max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60 + md_paa_countable_earned_income: 60 + +- name: Case 8, earned $185/mo with partial $10/mo unearned (gradual transition). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $185/mo * 12 = $2,220/yr. + ssi_earned_income: 2_220 + # $10/mo * 12 = $120/yr. + ssi_unearned_income: 120 + households: + household: + members: [person1] + state_code: MD + output: + # Unearned = $10 → leftover_general = $10, effective disregard = $75. + # max(185 - 75, 0) * 0.5 = 110 * 0.5 = 55 + md_paa_countable_earned_income: 55 + +- name: Case 9, earned $185/mo with unearned exactly at $20 (cascade boundary). + period: 2025-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_B + # $185/mo * 12 = $2,220/yr. + ssi_earned_income: 2_220 + # $20/mo * 12 = $240/yr. + ssi_unearned_income: 240 + households: + household: + members: [person1] + state_code: MD + output: + # Unearned = $20 → leftover_general = 0, effective disregard = $65. + # max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60 + md_paa_countable_earned_income: 60 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index e655f2e5deb..b7b53fe03a8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -78,19 +78,22 @@ output: md_paa_provider_rate: 894 -- name: Case 6, REHAB_RESIDENCE pays no provider rate (MDH covers cost). +- name: Case 6, REHAB_RESIDENCE provider rate is monthly equivalent of $54/day cap. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) md_paa_living_arrangement: REHAB_RESIDENCE households: household: members: [person1] state_code: MD output: - md_paa_provider_rate: 0 + # Per AT 23-02 page 3, REHAB allowable need is the cost of care, not to + # exceed $54/day. Monthly equivalent = 54 * 365 / 12 = 1642.50. + md_paa_provider_rate: 1_642.50 - name: Case 7, NONE living arrangement returns zero. period: 2025-01 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py index 1f9b52c42b1..2272738f162 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py @@ -10,11 +10,11 @@ class md_paa_countable_earned_income(Variable): defined_for = "md_paa_eligible" reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", ) def formula(person, period, parameters): - p = parameters(period).gov.states.md.dhs.fia.paa.income.earned_income_disregard + p = parameters(period).gov.states.md.dhs.fia.paa.income earned = add( person, period, @@ -23,5 +23,17 @@ def formula(person, period, parameters): "ssi_earned_income_deemed_from_ineligible_spouse", ], ) - after_initial = max_(earned - p.initial, 0) - return after_initial * (1 - p.rate) + unearned = add( + person, + period, + [ + "ssi_unearned_income", + "ssi_unearned_income_deemed_from_ineligible_spouse", + ], + ) + leftover_general = max_(p.unearned_income_disregard - unearned, 0) + effective_earned_disregard = ( + p.earned_income_disregard.initial + leftover_general + ) + earned_after_disregard = max_(earned - effective_earned_disregard, 0) + return earned_after_disregard * (1 - p.earned_income_disregard.rate) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py index 531a554eb51..f93a8f0297a 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py @@ -10,9 +10,17 @@ class md_paa_countable_income(Variable): defined_for = "md_paa_eligible" reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", ) - adds = [ - "md_paa_countable_earned_income", - "md_paa_countable_unearned_income", - ] + + def formula(person, period, parameters): + living_arrangement = person("md_paa_living_arrangement", period) + is_rehab = ( + living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE + ) + countable_earned = person("md_paa_countable_earned_income", period) + countable_unearned = person("md_paa_countable_unearned_income", period) + gross_countable = countable_earned + countable_unearned + cost_of_care = person("md_paa_provider_rate", period) + rehab_disregard = where(is_rehab, cost_of_care, 0) + return max_(gross_countable - rehab_disregard, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py index 9bbb3c34832..c78b5d7cf49 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py @@ -10,7 +10,7 @@ class md_paa_countable_unearned_income(Variable): defined_for = "md_paa_eligible" reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py index efdb34e3783..8457221fa6c 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py @@ -14,7 +14,11 @@ class md_paa_provider_rate(Variable): ) def formula(person, period, parameters): + p = parameters(period).gov.states.md.dhs.fia.paa living_arrangement = person("md_paa_living_arrangement", period) - return parameters(period).gov.states.md.dhs.fia.paa.provider_rate[ - living_arrangement - ] + is_rehab = ( + living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE + ) + standard_rate = p.provider_rate[living_arrangement] + monthly_rehab_cap = p.provider_rate_rehab_daily_cap * 365 / MONTHS_IN_YEAR + return standard_rate + where(is_rehab, monthly_rehab_cap, 0) From 0960da45a96f59e376836ed0dcdcaa7760a3226d Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 28 Apr 2026 21:08:15 -0400 Subject: [PATCH 04/17] Review-fix round 2: revert REHAB_RESIDENCE over-correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 1 incorrectly added a $54/day provider rate cap for REHAB residents, overpaying by $1,642.50/month. Per PAA Manual §400 and §900.3, MDH pays the cost of care directly for Rehabilitative Residence cases — PAA contributes only the personal needs allowance. - Delete provider_rate_rehab_daily_cap.yaml (parameter not needed) - Revert md_paa_provider_rate.py to simple parameter lookup (REHAB=$0) - Simplify md_paa_countable_income.py: per COMAR 07.03.07.08(B), REHAB residents get a full cost-of-care disregard, so countable income is fully absorbed (PAA = PNA always) - Update REHAB tests to expect PNA-only benefit ($106 in 2026) Round 1's earned-disregard cascade fix and COMAR .09→.08 citation fix remain correct and are kept. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fia/paa/provider_rate_rehab_daily_cap.yaml | 14 -------------- .../gov/states/md/dhs/fia/paa/integration.yaml | 16 ++++++++-------- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 13 +++++++------ .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 9 +++++---- .../md/dhs/fia/paa/md_paa_countable_income.py | 9 +++++---- .../md/dhs/fia/paa/md_paa_provider_rate.py | 10 +++------- 6 files changed, 28 insertions(+), 43 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml deleted file mode 100644 index e1831a4194f..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate_rehab_daily_cap.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Maryland caps the daily provider rate for MDH Rehabilitative Residence cases at this amount under the Public Assistance to Adults program. - -values: - 2023-01-01: 54 - -metadata: - unit: currency-USD - period: day - label: Maryland PAA Rehabilitative Residence daily provider rate cap - reference: - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems - href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - - title: COMAR 07.03.07.04 Allowable Needs - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index dcfd7d7b2d8..0ab91c8e99b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -151,7 +151,7 @@ md_paa_total_cost_of_care: [1_000] md_paa: [620] -- name: Case 4, REHAB_RESIDENCE recipient receives cost of care plus PNA. +- name: Case 4, REHAB_RESIDENCE recipient receives only the PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -183,15 +183,15 @@ ssi: [994] # --- PAA benefit --- - # Per AT 23-02 page 3, REHAB allowable need = cost of rehab residence - # (capped at $54/day = $1,642.50/mo) + PNA ($106). - # Per COMAR 07.03.07.08(B), a cost-of-care disregard reduces income. - # countable_income = max(0 - 1642.50, 0) = 0. - # PAA = max(1642.50 + 106 - 0, 0) = 1,748.50. + # Per PAA Manual §400 and §900.3, MDH Rehabilitative Residence cases + # receive only the personal needs allowance from PAA — MDH pays the + # provider directly. Per COMAR 07.03.07.08(B), a cost-of-care disregard + # absorbs countable income. + # PAA = provider_rate(0) + PNA(106) - countable(0) = 106. md_paa_eligible: [true] - md_paa_provider_rate: [1_642.50] + md_paa_provider_rate: [0] md_paa_personal_needs_allowance: [106] - md_paa: [1_748.50] + md_paa: [106] - name: Case 5, married couple both eligible in CARE_HOME_LEVEL_A treated as two individuals. period: 2026-01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index 5444dd5785b..e0b19b17404 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -21,7 +21,7 @@ # provider_rate(875) + PNA(106) - countable(0) = 981 md_paa: 981 -- name: Case 2, eligible REHAB_RESIDENCE receives cost of care up to $54/day plus PNA. +- name: Case 2, eligible REHAB_RESIDENCE receives only the PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -37,11 +37,12 @@ members: [person1] state_code: MD output: - # Per AT 23-02 page 3, REHAB allowable need = cost of rehab residence - # (capped at $54/day = 54 * 365 / 12 = $1,642.50/mo) + PNA ($106). - # countable_income = max(0 - 1642.50, 0) = 0 (cost-of-care disregard). - # provider_rate(1642.50) + PNA(106) - countable(0) = 1,748.50. - md_paa: 1_748.50 + # Per PAA Manual §400 and §900.3, MDH Rehabilitative Residence cases + # receive only the personal needs allowance from PAA — MDH pays the + # cost of care from other sources. Per COMAR 07.03.07.08(B), a + # cost-of-care disregard absorbs any countable income. + # provider_rate(0) + PNA(106) - countable(0) = 106. + md_paa: 106 - name: Case 3, ineligible (NONE living arrangement). period: 2026-01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index b7b53fe03a8..2a99b4814b9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -78,7 +78,7 @@ output: md_paa_provider_rate: 894 -- name: Case 6, REHAB_RESIDENCE provider rate is monthly equivalent of $54/day cap. +- name: Case 6, REHAB_RESIDENCE provider rate is zero. period: 2025-01 absolute_error_margin: 0.01 input: @@ -91,9 +91,10 @@ members: [person1] state_code: MD output: - # Per AT 23-02 page 3, REHAB allowable need is the cost of care, not to - # exceed $54/day. Monthly equivalent = 54 * 365 / 12 = 1642.50. - md_paa_provider_rate: 1_642.50 + # Per PAA Manual §400 and §900.3, MDH pays the provider directly for + # Rehabilitative Residence cases, so PAA's provider rate is zero. PAA + # contributes only the personal needs allowance. + md_paa_provider_rate: 0 - name: Case 7, NONE living arrangement returns zero. period: 2025-01 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py index f93a8f0297a..ffd181859a6 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py @@ -14,13 +14,14 @@ class md_paa_countable_income(Variable): ) def formula(person, period, parameters): + # Per COMAR 07.03.07.08(B), residents of MDH Rehabilitative Residences + # have a cost-of-care disregard. MDH pays the cost of care directly, so + # PAA contributes only the personal needs allowance — countable income + # is fully disregarded. living_arrangement = person("md_paa_living_arrangement", period) is_rehab = ( living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE ) countable_earned = person("md_paa_countable_earned_income", period) countable_unearned = person("md_paa_countable_unearned_income", period) - gross_countable = countable_earned + countable_unearned - cost_of_care = person("md_paa_provider_rate", period) - rehab_disregard = where(is_rehab, cost_of_care, 0) - return max_(gross_countable - rehab_disregard, 0) + return where(is_rehab, 0, countable_earned + countable_unearned) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py index 8457221fa6c..efdb34e3783 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py @@ -14,11 +14,7 @@ class md_paa_provider_rate(Variable): ) def formula(person, period, parameters): - p = parameters(period).gov.states.md.dhs.fia.paa living_arrangement = person("md_paa_living_arrangement", period) - is_rehab = ( - living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE - ) - standard_rate = p.provider_rate[living_arrangement] - monthly_rehab_cap = p.provider_rate_rehab_daily_cap * 365 / MONTHS_IN_YEAR - return standard_rate + where(is_rehab, monthly_rehab_cap, 0) + return parameters(period).gov.states.md.dhs.fia.paa.provider_rate[ + living_arrangement + ] From 92b6b7f08bb3cd6d8140f1ecc4f0219ea86065b2 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 1 May 2026 10:49:21 -0400 Subject: [PATCH 05/17] Review-fix round 3: address regulatory and modeling gaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eligibility: accept SSI, SSDI, or other OASI/survivor receipt with aged/blind/disabled gate on the OASI path; add $2,000 resource check; fix §300.2 → §300.1.B citation. - Countable income: include SSI cash payment in unearned per §500.8 / AT 23-02; document residual gaps (lump sums §500.7, §500.8 contributions, §500.9 exclusions, "actually available" first-month treatment). - Earned disregard: gate on living arrangement — no disregard for Assisted Living per §500.11.B.1. - Rehab Residence: replace return-zero shortcut with §900.3 cost-of-need ceiling test ($54/day × 30) so high-income residents no longer automatically receive the full PNA. - PNA: collapse and re-place placeholder at 2025-01-01 = $102 with research-based comment (DLS FY26 budget still cites $93; IM 26-04 is the first source mentioning $102). - References: fix §500.3 → §500.11 in three income disregard YAMLs; remove broken §400 URL from md_paa_living_arrangement.py. - Tests: recompute expected values for SSI inclusion; add cases for SSDI-only path, $2,000 resource limit, asymmetric married couple, high-income Rehab Residence, CARE_HOME_LEVEL_D, and Assisted Living no-disregard path. All 52 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../earned_income_disregard/initial.yaml | 4 +- .../income/earned_income_disregard/rate.yaml | 6 +- .../paa/income/unearned_income_disregard.yaml | 4 +- .../dhs/fia/paa/personal_needs_allowance.yaml | 16 +- .../cost_of_need_per_diem.yaml | 16 ++ .../states/md/dhs/fia/paa/integration.yaml | 198 ++++++++---------- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 96 +++++++-- .../paa/md_paa_countable_earned_income.yaml | 108 +++++----- .../paa/md_paa_countable_unearned_income.yaml | 61 +++--- .../md/dhs/fia/paa/md_paa_eligible.yaml | 90 +++++++- .../paa/md_paa_personal_needs_allowance.yaml | 32 +-- .../fia/paa/md_paa_countable_earned_income.py | 20 +- .../md/dhs/fia/paa/md_paa_countable_income.py | 23 +- .../paa/md_paa_countable_unearned_income.py | 13 ++ .../states/md/dhs/fia/paa/md_paa_eligible.py | 30 ++- .../dhs/fia/paa/md_paa_living_arrangement.py | 1 - 16 files changed, 467 insertions(+), 251 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml index 92517e85e0e..7def6392ad2 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml @@ -8,7 +8,7 @@ metadata: period: month label: Maryland PAA earned income initial disregard reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(3) Income Disregards + - title: COMAR 07.03.07.08(A)(1) and (A)(3) Income Disregards href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml index 2b5263328cc..30bc8d2ab92 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml @@ -5,10 +5,10 @@ values: metadata: unit: /1 - period: year + period: eternity label: Maryland PAA earned income disregard rate reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(1) Income Disregards + - title: COMAR 07.03.07.08(A)(1) and (A)(3) Income Disregards href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml index 462c19804ab..caecacedfe8 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml @@ -8,7 +8,7 @@ metadata: period: month label: Maryland PAA unearned income disregard reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.3 Financial Eligibility + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(2) Income Disregards + - title: COMAR 07.03.07.08(A)(2) and (A)(3) Income Disregards href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index ddd320dae92..78d150114cb 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -1,11 +1,17 @@ -description: Maryland provides this amount as the personal needs allowance per recipient under the Public Assistance to Adults program. +description: Maryland provides this amount as the personal needs allowance under the Public Assistance to Adults program. values: 2023-01-01: 93 - # Effective date unverified: confirmed only by IM 26-04 stating PNA "increased - # from $102 to $106" effective Jul 1, 2025. The transmittal that raised PNA - # from $93 to $102 has not been located; 2024-07-01 is a placeholder. - 2024-07-01: 102 + # 2025-01-01: 102 is a researched placeholder. The DHS FY24 and FY25 + # AT/IM archives contain no PAA/PNA transmittal, and the DLS FY26 DHS-FIA + # budget analysis (published in early 2025) still cites $93. IM 26-04 + # (Oct 2025) is the first public document mentioning $102 and only + # references "Updates: AT#23-02"; the originating transmittal could not + # be located. The 2025-01-01 effective date matches Maryland's pattern + # of January COLA-aligned PNA updates and brackets the period directly + # preceding the verified 2025-07-01 → $106 transition. Reach out to + # fia.policy@maryland.gov to confirm. + 2025-01-01: 102 2025-07-01: 106 metadata: diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml new file mode 100644 index 00000000000..6eca0a6cb00 --- /dev/null +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml @@ -0,0 +1,16 @@ +description: Maryland caps the daily cost of need for MDH Rehabilitative Residence customers at this amount under the Public Assistance to Adults program. + +values: + 2023-01-01: 54 + +metadata: + unit: currency-USD + period: day + label: Maryland PAA Rehabilitative Residence cost of need per diem + reference: + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 + - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 900.3 Calculation of Benefits + href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx + - title: COMAR 07.03.07.08(B) Cost of Care Disregard + href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 0ab91c8e99b..d0dcb59efb3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -1,7 +1,8 @@ # Integration tests for Maryland Public Assistance to Adults (PAA). -# Verifies the full federal SSI → state PAA chain using real income -# variables (social_security, employment_income), not pre-set ssi values. -# All scenarios use 2026-01 (PNA = $106 after 2025-07-01 effective date). +# Verifies the full federal SSI → state PAA chain. Per PAA Manual §500.8 +# and AT 23-02, the SSI cash payment counts as unearned income for PAA +# (with the $20 disregard). All scenarios use 2026-01 (PNA = $106). +# 2026 SSI individual FBR = $994/mo. - name: Case 1, single SSI recipient in CARE_HOME_LEVEL_B with no other income. period: 2026-01 @@ -31,25 +32,21 @@ state_code: MD output: # --- Federal SSI --- - # Aged, no other income → full FBR ($994/mo in 2026). is_ssi_eligible: [true] ssi: [994] - # --- PAA eligibility --- + # --- PAA --- md_paa_eligible: [true] - - # --- PAA income (PAA excludes SSI from countable income) --- + # countable_unearned = max(994 - 20, 0) = 974 (SSI counts as income). + md_paa_countable_unearned_income: [974] md_paa_countable_earned_income: [0] - md_paa_countable_unearned_income: [0] - md_paa_countable_income: [0] - - # --- PAA benefit --- + md_paa_countable_income: [974] # provider_rate(875) + PNA(106) = 981 total cost of care. - # PAA = max(981 - 0, 0) = 981. + # PAA = max(981 - 974, 0) = 7. md_paa_provider_rate: [875] md_paa_personal_needs_allowance: [106] md_paa_total_cost_of_care: [981] - md_paa: [981] + md_paa: [7] - name: Case 2, SSI recipient with $200/mo earned income in CARE_HOME_LEVEL_C. period: 2026-01 @@ -84,24 +81,21 @@ is_ssi_eligible: [true] ssi: [936.50] - # --- PAA eligibility --- + # --- PAA --- md_paa_eligible: [true] - - # --- PAA income (COMAR 07.03.07.08(A)(1): no unearned → $85 effective disregard) --- - # countable_earned = max(200 - 85, 0) * 0.5 = 115 * 0.5 = 57.50. - # countable_unearned = 0 (PAA excludes SSI). - md_paa_countable_earned_income: [57.50] - md_paa_countable_unearned_income: [0] - md_paa_countable_income: [57.50] - - # --- PAA benefit --- - # provider_rate(1173) + PNA(106) = 1279 total cost of care. - # PAA = max(1279 - 57.50, 0) = 1221.50. + # unearned = 936.50 (SSI) → countable_unearned = max(936.50 - 20, 0) = 916.50. + # leftover_general = 0. effective earned disregard = $65. + # countable_earned = max(200 - 65, 0) * 0.5 = 67.50. + md_paa_countable_unearned_income: [916.50] + md_paa_countable_earned_income: [67.50] + md_paa_countable_income: [984] + # provider_rate(1173) + PNA(106) = 1279. + # PAA = max(1279 - 984, 0) = 295. md_paa_provider_rate: [1_173] md_paa_total_cost_of_care: [1_279] - md_paa: [1_221.50] + md_paa: [295] -- name: Case 3, SSI recipient with $400/mo other unearned income in ASSISTED_LIVING. +- name: Case 3, SSI recipient with $400/mo non-SSI unearned income in ASSISTED_LIVING. period: 2026-01 absolute_error_margin: 0.01 input: @@ -112,7 +106,7 @@ is_disabled: false immigration_status: CITIZEN ssi_countable_resources: 800 - social_security: 4_800 # $400/mo non-SSI unearned + social_security: 4_800 # $400/mo OASI employment_income: 0 md_paa_living_arrangement: ASSISTED_LIVING marital_units: @@ -131,27 +125,25 @@ output: # --- Federal SSI --- # Federal countable unearned = 400 - 20 = 380/mo. - # SSI = 994 - 380 = 614/mo. + # SSI = max(994 - 380, 0) = 614/mo. is_ssi_eligible: [true] ssi: [614] - # --- PAA eligibility --- + # --- PAA --- md_paa_eligible: [true] - - # --- PAA income --- - # countable_unearned = max(400 - 20, 0) = 380 (PAA excludes SSI itself). + # unearned for PAA = ssi(614) + ssi_unearned_income(400 from social_security) = 1014. + # countable_unearned = max(1014 - 20, 0) = 994. + # Assisted Living → no earned disregard, but earned = 0 here. + md_paa_countable_unearned_income: [994] md_paa_countable_earned_income: [0] - md_paa_countable_unearned_income: [380] - md_paa_countable_income: [380] - - # --- PAA benefit --- - # provider_rate(894) + PNA(106) = 1000 total cost of care. - # PAA = max(1000 - 380, 0) = 620. + md_paa_countable_income: [994] + # provider_rate(894) + PNA(106) = 1000. + # PAA = max(1000 - 994, 0) = 6. md_paa_provider_rate: [894] md_paa_total_cost_of_care: [1_000] - md_paa: [620] + md_paa: [6] -- name: Case 4, REHAB_RESIDENCE recipient receives only the PNA. +- name: Case 4, REHAB_RESIDENCE recipient with only SSI receives only the PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -182,18 +174,18 @@ is_ssi_eligible: [true] ssi: [994] - # --- PAA benefit --- - # Per PAA Manual §400 and §900.3, MDH Rehabilitative Residence cases - # receive only the personal needs allowance from PAA — MDH pays the - # provider directly. Per COMAR 07.03.07.08(B), a cost-of-care disregard - # absorbs countable income. + # --- PAA --- + # post_disregard_income = max(994 - 20, 0) = 974. + # cost_of_need_ceiling = $54 * 30 = 1620. + # rehab_countable = max(974 - 1620, 0) = 0. # PAA = provider_rate(0) + PNA(106) - countable(0) = 106. md_paa_eligible: [true] + md_paa_countable_income: [0] md_paa_provider_rate: [0] md_paa_personal_needs_allowance: [106] md_paa: [106] -- name: Case 5, married couple both eligible in CARE_HOME_LEVEL_A treated as two individuals. +- name: Case 5, married couple both SSI-eligible in CARE_HOME_LEVEL_A treated as two individuals. period: 2026-01 absolute_error_margin: 0.01 input: @@ -203,6 +195,7 @@ is_blind: false is_disabled: false immigration_status: CITIZEN + ssi: 8_946 # $745.50/mo (couple FBR $1,491 / 2) as input ssi_countable_resources: 1_000 employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_A @@ -211,6 +204,7 @@ is_blind: false is_disabled: false immigration_status: CITIZEN + ssi: 8_946 ssi_countable_resources: 1_000 employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_A @@ -228,13 +222,14 @@ members: [person1, person2] state_code: MD output: - # Per SSA 2011 (Maryland): couples treated as two individuals. - # No couple-rate reduction — each spouse gets the individual provider rate. + # Per SSA 2011: Maryland treats couples as two individuals. + # Each spouse: countable_unearned = max(745.50 - 20, 0) = 725.50. + # PAA per spouse = max(776 + 106 - 725.50, 0) = 156.50. md_paa_eligible: [true, true] md_paa_provider_rate: [776, 776] md_paa_personal_needs_allowance: [106, 106] - # Each: provider(776) + PNA(106) - countable(0) = 882. - md_paa: [882, 882] + md_paa_countable_unearned_income: [725.50, 725.50] + md_paa: [156.50, 156.50] - name: Case 6, person not in PAA facility (NONE) is ineligible. period: 2026-01 @@ -263,13 +258,12 @@ members: [person1] state_code: MD output: - # SSI eligible but not in PAA facility → PAA ineligible. is_ssi_eligible: [true] ssi: [994] md_paa_eligible: [false] md_paa: [0] -- name: Case 7, countable income equals total cost of care yields zero benefit. +- name: Case 7, applicant with only earned income (no SSA receipt) is ineligible. period: 2026-01 absolute_error_margin: 0.01 input: @@ -280,10 +274,10 @@ is_disabled: false immigration_status: CITIZEN ssi_countable_resources: 500 - # $1001/mo unearned: countable = 1001 - 20 = 981 = total cost of care - # for CARE_HOME_LEVEL_B (875 + 106 = 981). - social_security: 12_012 - employment_income: 0 + ssi: 0 + social_security: 0 + social_security_disability: 0 + employment_income: 36_000 # $3,000/mo md_paa_living_arrangement: CARE_HOME_LEVEL_B marital_units: marital_unit: @@ -299,35 +293,25 @@ members: [person1] state_code: MD output: - # --- Federal SSI --- - # Countable unearned = 1001 - 20 = 981 > 994 FBR? No: 981 < 994 → SSI = 13. - is_ssi_eligible: [true] - ssi: [13] - - # --- PAA --- - # countable_unearned = max(1001 - 20, 0) = 981. - # total_cost_of_care = 875 + 106 = 981. - # PAA = max(981 - 981, 0) = 0 (exactly at threshold). - md_paa_eligible: [true] - md_paa_countable_unearned_income: [981] - md_paa_total_cost_of_care: [981] + # No federal cash benefit (SSI / SSDI / OASI) → PAA-ineligible per + # PAA Manual §300.1.B. + md_paa_eligible: [false] md_paa: [0] -- name: Case 8, COMAR 07.03.07.08(A)(3) cascade with both earned and unearned income. +- name: Case 8, SSDI-only recipient in CARE_HOME_LEVEL_B is PAA-eligible. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: - age: 70 + age: 60 is_blind: false - is_disabled: false + is_disabled: true immigration_status: CITIZEN + ssi: 0 # Set explicitly to disable federal SSI calc and isolate SSDI path. ssi_countable_resources: 500 - # $185/mo earned ($2,220/yr) and $300/mo unearned ($3,600/yr). - ssi_earned_income: 2_220 - ssi_unearned_income: 3_600 - ssi: 9_600 # gates md_paa_eligible + social_security_disability: 12_000 # $1,000/mo SSDI + employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_B marital_units: marital_unit: @@ -344,18 +328,13 @@ state_code: MD output: md_paa_eligible: [true] - # Per COMAR 07.03.07.08(A)(3): unearned >= $20 → leftover_general = 0, - # effective earned disregard = $65 (NOT $85). - # countable_earned = max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60. - # countable_unearned = max(300 - 20, 0) = 280. - md_paa_countable_earned_income: [60] - md_paa_countable_unearned_income: [280] - md_paa_countable_income: [340] - # provider_rate(875) + PNA(106) = 981 total cost of care. - # PAA = max(981 - 340, 0) = 641. - md_paa: [641] + # ssi=0; unearned for PAA = 0 + ssi_unearned_income(1000 from SSDI) = 1000. + # countable_unearned = max(1000 - 20, 0) = 980. + md_paa_countable_unearned_income: [980] + # provider_rate(875) + PNA(106) - 980 = 1. + md_paa: [1] -- name: Case 9, COMAR 07.03.07.08(A)(1) earned-only with no unearned ($85 disregard). +- name: Case 9, asymmetric married couple — only one spouse PAA-eligible. period: 2026-01 absolute_error_margin: 0.01 input: @@ -365,33 +344,40 @@ is_blind: false is_disabled: false immigration_status: CITIZEN - ssi_countable_resources: 500 - # $185/mo earned with zero unearned income. - ssi_earned_income: 2_220 - ssi_unearned_income: 0 - ssi: 9_600 # gates md_paa_eligible + ssi: 8_400 # $700/mo SSI input + ssi_countable_resources: 1_000 + employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_B + person2: + age: 60 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + ssi_countable_resources: 0 + employment_income: 0 + md_paa_living_arrangement: NONE marital_units: marital_unit: - members: [person1] + members: [person1, person2] tax_units: tax_unit: - members: [person1] + members: [person1, person2] spm_units: spm_unit: - members: [person1] + members: [person1, person2] households: household: - members: [person1] + members: [person1, person2] state_code: MD output: - md_paa_eligible: [true] - # Per COMAR 07.03.07.08(A)(1): no unearned → leftover_general = $20, - # effective earned disregard = $85. - # countable_earned = max(185 - 85, 0) * 0.5 = 100 * 0.5 = 50. - md_paa_countable_earned_income: [50] - md_paa_countable_unearned_income: [0] - md_paa_countable_income: [50] - # provider_rate(875) + PNA(106) = 981 total cost of care. - # PAA = max(981 - 50, 0) = 931. - md_paa: [931] + # Only person1 is PAA-eligible (in CARE_HOME and receiving SSI). person2 + # is not in a PAA facility and does not receive SSI/SSDI. Verifies the + # asymmetric-eligibility code path through md_paa_eligible. + md_paa_eligible: [true, false] + md_paa_personal_needs_allowance: [106, 0] + md_paa_provider_rate: [875, 0] + # person1: countable_unearned = max(700 - 20, 0) = 680. + # PAA = max(875 + 106 - 680, 0) = 301. + # person2's PAA fields are zero (defined_for filters out). + md_paa: [301, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index e0b19b17404..75bf939b7a2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -1,8 +1,10 @@ # Tests for md_paa main benefit variable. # Formula: max(provider_rate + PNA - countable_income, 0) when eligible. -# Period: 2026-01 used so PNA = $106 (after 2025-07-01 effective date). +# Per PAA Manual §500.8 and AT 23-02, the SSI cash payment counts as +# unearned income for PAA. Period: 2026-01 used so PNA = $106 (after +# 2025-07-01 effective date). -- name: Case 1, eligible CARE_HOME_LEVEL_B with no other income. +- name: Case 1, eligible CARE_HOME_LEVEL_B with only SSI income. period: 2026-01 absolute_error_margin: 0.01 input: @@ -12,35 +14,36 @@ ssi: 9_600 # $800/mo SSI as input (YEAR-defined) ssi_earned_income: 0 ssi_unearned_income: 0 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # provider_rate(875) + PNA(106) - countable(0) = 981 - md_paa: 981 + # countable_unearned = max(800 - 20, 0) = 780. + # provider_rate(875) + PNA(106) - countable(780) = 201. + md_paa: 201 -- name: Case 2, eligible REHAB_RESIDENCE receives only the PNA. +- name: Case 2, eligible REHAB_RESIDENCE with only SSI receives only the PNA. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 ssi_earned_income: 0 ssi_unearned_income: 0 + ssi_countable_resources: 1_000 md_paa_living_arrangement: REHAB_RESIDENCE households: household: members: [person1] state_code: MD output: - # Per PAA Manual §400 and §900.3, MDH Rehabilitative Residence cases - # receive only the personal needs allowance from PAA — MDH pays the - # cost of care from other sources. Per COMAR 07.03.07.08(B), a - # cost-of-care disregard absorbs any countable income. + # post_disregard_income = 780 < cost_of_need_ceiling ($54 * 30 = 1620) + # → rehab_countable = 0. # provider_rate(0) + PNA(106) - countable(0) = 106. md_paa: 106 @@ -51,7 +54,8 @@ people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: NONE households: household: @@ -60,47 +64,48 @@ output: md_paa: 0 -- name: Case 4, eligible CARE_HOME_LEVEL_A with $300/mo unearned income. +- name: Case 4, eligible CARE_HOME_LEVEL_A with $300/mo additional unearned income. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 ssi_earned_income: 0 # $300/mo * 12 = $3,600/yr. ssi_unearned_income: 3_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_A households: household: members: [person1] state_code: MD output: - # Monthly unearned = 3600/12 = 300. - # countable_unearned: max(300 - 20, 0) = 280. - # provider_rate(776) + PNA(106) - countable(280) = 602. - md_paa: 602 + # unearned = 800 + 300 = 1100. countable_unearned = max(1100 - 20, 0) = 1080. + # provider_rate(776) + PNA(106) - countable(1080) = max(-198, 0) = 0. + md_paa: 0 -- name: Case 5, negative countable income does not inflate benefit. +- name: Case 5, negative earned income does not inflate benefit. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 # Self-employment loss should not reduce countable income below zero. self_employment_income: -60_000_000 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Negative earned income is floored at 0 by max_(earned - 85, 0) * 0.5. - # provider_rate(875) + PNA(106) - 0 = 981. - md_paa: 981 + # countable_earned floored at 0 by max_(earned - 65, 0) * 0.5. + # countable_unearned = 780. PAA = 981 - 780 = 201. + md_paa: 201 # Out-of-state SSI recipient in a PAA-style facility is filtered out by # md_paa_eligible's `defined_for = StateCode.MD`. @@ -111,7 +116,8 @@ people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -119,3 +125,47 @@ state_code: CA output: md_paa: 0 + +# High-income REHAB_RESIDENCE recipient — income above $54/day cost-of-need +# ceiling reduces the PAA benefit (corrects prior "always pay PNA" behavior). +- name: Case 7, high-income REHAB_RESIDENCE recipient gets reduced PAA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 + # $1,000/mo additional unearned * 12 = $12,000/yr. + ssi_unearned_income: 12_000 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: REHAB_RESIDENCE + households: + household: + members: [person1] + state_code: MD + output: + # unearned = 800 + 1000 = 1800. countable_unearned = max(1800 - 20, 0) = 1780. + # post_disregard_income = 1780. cost_of_need_ceiling = $54 * 30 = 1620. + # rehab_countable = max(1780 - 1620, 0) = 160. + # provider_rate(0) + PNA(106) - 160 = max(-54, 0) = 0. + md_paa: 0 + +# CARE_HOME_LEVEL_D missing from earlier coverage; verify highest tier. +- name: Case 8, eligible CARE_HOME_LEVEL_D recipient. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_D + households: + household: + members: [person1] + state_code: MD + output: + # provider_rate(1376) + PNA(106) - countable(780) = 702. + md_paa: 702 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml index a0477556aef..6c00c25dcc7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml @@ -2,11 +2,11 @@ # Implements the COMAR 07.03.07.08(A) cascade: # leftover_general = max($20 - unearned, 0) # effective_earned_disregard = $65 + leftover_general -# earned_after_disregard = max(earned - effective_earned_disregard, 0) -# countable_earned = earned_after_disregard * 0.5 -# When unearned = 0: effective disregard = $85 (matches (A)(1)) -# When unearned >= $20: effective disregard = $65 (matches (A)(3)) -# ssi_earned_income / ssi_unearned_income are YEAR-defined; inputs are annual. +# countable_earned = max(earned - effective_earned_disregard, 0) * 0.5 +# Per PAA Manual §500.11.B.1, the earned disregard does NOT apply to +# Assisted Living residents — earned income counts in full for them. +# All cases use ssi = $9,600/yr ($800/mo), so unearned > $20 → leftover = 0 +# → effective disregard = $65, except where ssi_unearned_income overrides. - name: Case 1, zero earned income. period: 2025-01 @@ -14,7 +14,9 @@ input: people: person1: + ssi: 9_600 ssi_earned_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] @@ -22,41 +24,42 @@ output: md_paa_countable_earned_income: 0 -- name: Case 2, earned exactly at $85/mo disregard with no unearned. +- name: Case 2, earned exactly at $65/mo disregard with SSI unearned. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - # $85/mo * 12 = $1,020/yr. - ssi_earned_income: 1_020 + ssi: 9_600 + # $65/mo * 12 = $780/yr. + ssi_earned_income: 780 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly earned = 85, unearned = 0 → effective disregard = $85. - # max(85 - 85, 0) * 0.5 = 0 + # SSI ($800) >= $20 → leftover = 0 → effective disregard = $65. + # max(65 - 65, 0) * 0.5 = 0. md_paa_countable_earned_income: 0 -- name: Case 3, earned $185/mo with no unearned (matches COMAR 07.03.07.08(A)(1)). +- name: Case 3, earned $185/mo with SSI unearned (effective disregard $65). period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B + ssi: 9_600 # $185/mo * 12 = $2,220/yr. ssi_earned_income: 2_220 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # No unearned → leftover_general = $20, effective disregard = $85. - # max(185 - 85, 0) * 0.5 = 100 * 0.5 = 50 - md_paa_countable_earned_income: 50 + # max(185 - 65, 0) * 0.5 = 60. + md_paa_countable_earned_income: 60 - name: Case 4, earned $50/mo (below initial disregard). period: 2025-01 @@ -64,111 +67,116 @@ input: people: person1: + ssi: 9_600 # $50/mo * 12 = $600/yr. ssi_earned_income: 600 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # No unearned → effective disregard = $85. - # max(50 - 85, 0) * 0.5 = 0 + # max(50 - 65, 0) * 0.5 = 0. md_paa_countable_earned_income: 0 -- name: Case 5, earned $84/mo just below initial disregard. +- name: Case 5, earned $64/mo just below $65 disregard. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + # $64/mo * 12 = $768/yr. + ssi_earned_income: 768 md_paa_living_arrangement: CARE_HOME_LEVEL_B - # $84/mo * 12 = $1,008/yr. - ssi_earned_income: 1_008 households: household: members: [person1] state_code: MD output: + # max(64 - 65, 0) * 0.5 = 0. md_paa_countable_earned_income: 0 -- name: Case 6, earned $86/mo just above initial disregard. +- name: Case 6, earned $66/mo just above $65 disregard. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + # $66/mo * 12 = $792/yr. + ssi_earned_income: 792 md_paa_living_arrangement: CARE_HOME_LEVEL_B - # $86/mo * 12 = $1,032/yr. - ssi_earned_income: 1_032 households: household: members: [person1] state_code: MD output: - # max(86 - 85, 0) * 0.5 = 0.5 + # max(66 - 65, 0) * 0.5 = 0.5. md_paa_countable_earned_income: 0.5 -- name: Case 7, earned $185/mo with $300/mo unearned (matches COMAR 07.03.07.08(A)(3)). +- name: Case 7, earned $185/mo with $300/mo additional unearned. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B - # $185/mo * 12 = $2,220/yr. + ssi: 9_600 ssi_earned_income: 2_220 # $300/mo * 12 = $3,600/yr. ssi_unearned_income: 3_600 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Unearned >= $20 → leftover_general = 0, effective disregard = $65. - # max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60 + # Total unearned = 800 + 300 = 1100 > $20 → leftover = 0 → disregard = $65. + # max(185 - 65, 0) * 0.5 = 60. md_paa_countable_earned_income: 60 -- name: Case 8, earned $185/mo with partial $10/mo unearned (gradual transition). +# Per §500.11.B.1, Assisted Living residents do NOT receive the earned +# disregard (the manual presumes they are not gainfully employed). +- name: Case 8, Assisted Living resident has full earned income counted. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B + ssi: 9_600 # $185/mo * 12 = $2,220/yr. ssi_earned_income: 2_220 - # $10/mo * 12 = $120/yr. - ssi_unearned_income: 120 + md_paa_living_arrangement: ASSISTED_LIVING households: household: members: [person1] state_code: MD output: - # Unearned = $10 → leftover_general = $10, effective disregard = $75. - # max(185 - 75, 0) * 0.5 = 110 * 0.5 = 55 - md_paa_countable_earned_income: 55 + # No disregard for Assisted Living → countable = full $185 earned. + md_paa_countable_earned_income: 185 -- name: Case 9, earned $185/mo with unearned exactly at $20 (cascade boundary). +# Verify the leftover-spillover mechanic when unearned is below $20. SSDI-only +# path keeps total unearned modest ($1/mo) so the spillover into the earned +# disregard is exercised: leftover = $19, effective disregard = $84. +- name: Case 9, SSDI-only resident with low unearned exercises leftover spillover. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 0 + # $1/mo SSDI = $12/yr. + social_security_disability: 12 + # $185/mo earned * 12 = $2,220/yr. + employment_income: 2_220 + ssi_countable_resources: 500 md_paa_living_arrangement: CARE_HOME_LEVEL_B - # $185/mo * 12 = $2,220/yr. - ssi_earned_income: 2_220 - # $20/mo * 12 = $240/yr. - ssi_unearned_income: 240 households: household: members: [person1] state_code: MD output: - # Unearned = $20 → leftover_general = 0, effective disregard = $65. - # max(185 - 65, 0) * 0.5 = 120 * 0.5 = 60 - md_paa_countable_earned_income: 60 + # Total unearned = $1 (SSDI flows through ssi_unearned_income source list). + # leftover_general = max(20 - 1, 0) = 19. effective disregard = 65 + 19 = 84. + # max(185 - 84, 0) * 0.5 = 50.5. + md_paa_countable_earned_income: 50.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml index 42f6707f786..b05a07e2ab8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml @@ -1,95 +1,96 @@ # Tests for md_paa_countable_unearned_income variable. -# Formula: max(unearned - $20, 0) (per PAA Manual §500.3). -# $20 disregard matches federal SSI universal disregard. -# ssi_unearned_income is YEAR-defined; inputs are annual (= 12 * monthly). +# Formula: max(ssi + ssi_unearned_income + deemed - $20, 0). +# Per PAA Manual §500.8 and AT 23-02, the SSI cash payment counts as +# unearned income for PAA. ssi is YEAR-defined so input is annual. +# All cases use ssi = $9,600/yr ($800/mo) to gate eligibility. -- name: Case 1, zero unearned income. +- name: Case 1, no other unearned income beyond SSI. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: + ssi: 9_600 # $800/mo SSI is the only unearned income ssi_unearned_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly unearned = 0/12 = 0. - # max(0 - 20, 0) = 0 - md_paa_countable_unearned_income: 0 + # unearned = $800 (SSI). countable = max(800 - 20, 0) = 780. + md_paa_countable_unearned_income: 780 -- name: Case 2, unearned exactly at $20/mo disregard. +- name: Case 2, additional $20/mo unearned (boundary at unearned disregard). period: 2025-01 absolute_error_margin: 0.01 input: people: person1: + ssi: 9_600 # $20/mo * 12 = $240/yr. ssi_unearned_income: 240 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly unearned = 240/12 = 20. - # max(20 - 20, 0) = 0 - md_paa_countable_unearned_income: 0 + # unearned = 800 + 20 = 820. countable = max(820 - 20, 0) = 800. + md_paa_countable_unearned_income: 800 -- name: Case 3, unearned $300/mo (above disregard). +- name: Case 3, additional $300/mo unearned. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B + ssi: 9_600 # $300/mo * 12 = $3,600/yr. ssi_unearned_income: 3_600 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly unearned = 3600/12 = 300. - # max(300 - 20, 0) = 280 - md_paa_countable_unearned_income: 280 + # unearned = 800 + 300 = 1100. countable = max(1100 - 20, 0) = 1080. + md_paa_countable_unearned_income: 1_080 -# Boundary cases around the $20 unearned disregard. -- name: Case 4, unearned $19/mo just below disregard. +# Boundary cases around the $20 unearned disregard. The $1 difference +# between Case 4 and Case 5 verifies the disregard is applied at the margin. +- name: Case 4, $19/mo non-SSI unearned just below $20 increment. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B + ssi: 9_600 # $19/mo * 12 = $228/yr. ssi_unearned_income: 228 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly unearned = 228/12 = 19. - # max(19 - 20, 0) = 0 - md_paa_countable_unearned_income: 0 + # unearned = 800 + 19 = 819. countable = max(819 - 20, 0) = 799. + md_paa_countable_unearned_income: 799 -- name: Case 5, unearned $21/mo just above disregard. +- name: Case 5, $21/mo non-SSI unearned just above $20 increment. period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) - md_paa_living_arrangement: CARE_HOME_LEVEL_B + ssi: 9_600 # $21/mo * 12 = $252/yr. ssi_unearned_income: 252 + md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: - # Monthly unearned = 252/12 = 21. - # max(21 - 20, 0) = 1 - md_paa_countable_unearned_income: 1 + # unearned = 800 + 21 = 821. countable = max(821 - 20, 0) = 801. + md_paa_countable_unearned_income: 801 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index a24380c65b2..93660dbf327 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -1,6 +1,9 @@ # Tests for md_paa_eligible variable. -# Eligibility requires: ssi > 0 AND md_paa_living_arrangement != NONE -# AND state_code = MD (defined_for filter). +# Eligibility requires: +# - state_code = MD (defined_for filter) +# - ssi > 0 OR social_security_disability > 0 (federal cash benefit per §300.1.B) +# - ssi_countable_resources <= $2,000 (parity with federal SSI per §500.2.B) +# - md_paa_living_arrangement != NONE (§300.3, §300.4) - name: Case 1, SSI recipient in CARE_HOME_LEVEL_B in Maryland is eligible. period: 2025-01 @@ -9,6 +12,7 @@ person1: age: 70 ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -23,7 +27,8 @@ people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: NONE households: household: @@ -32,13 +37,15 @@ output: md_paa_eligible: false -- name: Case 3, no SSI receipt is ineligible. +- name: Case 3, no SSI or SSDI receipt is ineligible. period: 2025-01 input: people: person1: age: 70 ssi: 0 + social_security_disability: 0 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -53,7 +60,8 @@ people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -61,3 +69,75 @@ state_code: CA output: md_paa_eligible: false + +# Per PAA Manual §300.1.B, applicants who receive SSDI (without SSI) on the +# basis of disability are also PAA-eligible. +- name: Case 5, SSDI-only recipient is eligible. + period: 2025-01 + input: + people: + person1: + age: 60 + ssi: 0 + social_security_disability: 12_000 # $1,000/mo SSDI + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +# Per PAA Manual §500.2.B, the $2,000 SSI resource limit applies. Verify +# the resource gate is enforced independently of SSI receipt. +- name: Case 6, resources above $2,000 limit are ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 3_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +# A person with only earned income (no SSI, SSDI, or OASI receipt) cannot +# qualify for PAA, even if otherwise in a PAA facility — PAA requires +# receipt of a federal cash benefit on the basis of age/blindness/ +# disability per §300.1.B. +- name: Case 7, no federal cash benefit receipt is PAA-ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + employment_income: 36_000 # $3,000/mo earned income + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index a46439697fb..28af21ee4e7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -1,8 +1,10 @@ # Tests for md_paa_personal_needs_allowance variable. # Tests parameter values across the timeline: -# 2023-01-01: $93 -# 2024-07-01: $102 (effective date unverified) -# 2025-07-01: $106 +# 2023-01-01: $93 (AT 23-02) +# 2025-07-01: $106 (IM 26-04) +# The interim $102 value is omitted from the YAML pending verification of +# its issuing transmittal — the manifest's IM 26-04 confirms $102 was the +# prior PNA but does not name the originating transmittal. - name: Case 1, 2023 PNA. period: 2023-01 @@ -10,7 +12,8 @@ input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 # gates md_paa_eligible + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -19,19 +22,21 @@ output: md_paa_personal_needs_allowance: 93 -- name: Case 2, 2025 January PNA (after 2024-07-01 effective date). +- name: Case 2, 2025 January PNA reflects $102 (placeholder effective date). period: 2025-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: members: [person1] state_code: MD output: + # 2025-01-01: 102 is a placeholder effective date — see parameter file. md_paa_personal_needs_allowance: 102 - name: Case 3, 2026 January PNA (after 2025-07-01 effective date). @@ -40,7 +45,8 @@ input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -49,17 +55,14 @@ output: md_paa_personal_needs_allowance: 106 -# Boundary just before the 2024-07-01 transition: January 2024 should still -# return $93 (the 2023-01-01 value) because the PNA increased only mid-year. -# Test framework only supports YYYY-MM at the first month, so January is -# the closest pre-transition checkpoint within 2024. -- name: Case 4, January 2024 PNA still 93 (before 2024-07-01 transition). +- name: Case 4, January 2024 PNA still 93. period: 2024-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: @@ -76,7 +79,8 @@ input: people: person1: - ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + ssi: 9_600 + ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: household: diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py index 2272738f162..5cf77cd26b1 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py @@ -14,6 +14,10 @@ class md_paa_countable_earned_income(Variable): ) def formula(person, period, parameters): + # PAA Manual §500.11.B.1: the earned-income disregard applies only + # to CARE Home and MDH Rehabilitative Residence customers — Assisted + # Living residents are presumed not gainfully employed, so any + # earned income is fully countable for them. p = parameters(period).gov.states.md.dhs.fia.paa.income earned = add( person, @@ -27,13 +31,27 @@ def formula(person, period, parameters): person, period, [ + "ssi", "ssi_unearned_income", "ssi_unearned_income_deemed_from_ineligible_spouse", ], ) + # When unearned < $20, the unused portion of the $20 general + # disregard rolls into the earned disregard ($65 → up to $85). leftover_general = max_(p.unearned_income_disregard - unearned, 0) effective_earned_disregard = ( p.earned_income_disregard.initial + leftover_general ) earned_after_disregard = max_(earned - effective_earned_disregard, 0) - return earned_after_disregard * (1 - p.earned_income_disregard.rate) + countable_earned_with_disregard = earned_after_disregard * ( + 1 - p.earned_income_disregard.rate + ) + living_arrangement = person("md_paa_living_arrangement", period) + is_assisted_living = ( + living_arrangement == living_arrangement.possible_values.ASSISTED_LIVING + ) + return where( + is_assisted_living, + earned, + countable_earned_with_disregard, + ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py index ffd181859a6..de5617ef644 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py @@ -1,5 +1,7 @@ from policyengine_us.model_api import * +DAYS_IN_MONTH = 30 + class md_paa_countable_income(Variable): value_type = float @@ -14,14 +16,21 @@ class md_paa_countable_income(Variable): ) def formula(person, period, parameters): - # Per COMAR 07.03.07.08(B), residents of MDH Rehabilitative Residences - # have a cost-of-care disregard. MDH pays the cost of care directly, so - # PAA contributes only the personal needs allowance — countable income - # is fully disregarded. + # PAA Manual §900.3 / COMAR 07.03.07.08(B): MDH Rehabilitative + # Residence customers receive a cost-of-care disregard up to the + # per-diem ceiling ($54/day × ~30 days). Post-disregard income above + # that ceiling counts against the personal needs allowance; income + # below the ceiling produces zero countable income. + countable_earned = person("md_paa_countable_earned_income", period) + countable_unearned = person("md_paa_countable_unearned_income", period) + post_disregard_income = countable_earned + countable_unearned living_arrangement = person("md_paa_living_arrangement", period) is_rehab = ( living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE ) - countable_earned = person("md_paa_countable_earned_income", period) - countable_unearned = person("md_paa_countable_unearned_income", period) - return where(is_rehab, 0, countable_earned + countable_unearned) + per_diem = parameters( + period + ).gov.states.md.dhs.fia.paa.rehab_residence.cost_of_need_per_diem + cost_of_need_ceiling = per_diem * DAYS_IN_MONTH + rehab_countable = max_(post_disregard_income - cost_of_need_ceiling, 0) + return where(is_rehab, rehab_countable, post_disregard_income) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py index c78b5d7cf49..fbcf520f7e0 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py @@ -14,11 +14,24 @@ class md_paa_countable_unearned_income(Variable): ) def formula(person, period, parameters): + # PAA Manual §500.8 / §500.11 / AT 23-02: federally funded + # assistance, Social Security, and SSI/RSDI-type benefits all count + # as unearned income for PAA, with a $20 disregard. We add `ssi` + # explicitly because the federal `ssi_unearned_income` source list + # does not include the SSI cash payment itself. The following PAA + # countable-income refinements are not tracked at the moment and + # should be treated as residual modeling gaps: + # - Lump sums (§500.7) and infrequent / irregular income (§500.9). + # - Parent / adult-child financial contributions (§500.8). + # - "Actually available" first-month income treatment (§500.8.A.5). + # - State-funded assistance categories outside the federal SSI + # source list. p = parameters(period).gov.states.md.dhs.fia.paa.income unearned = add( person, period, [ + "ssi", "ssi_unearned_income", "ssi_unearned_income_deemed_from_ineligible_spouse", ], diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index f6e498c8096..f9829dd3ff5 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -13,8 +13,34 @@ class md_paa_eligible(Variable): ) def formula(person, period, parameters): - # PAA Manual §300.2 requires actual SSI receipt, not just SSI eligibility. + # PAA Manual §300.1.B / COMAR 07.03.07.03: applicants must be aged, + # blind, or disabled and receive SSI, SSDI, or other federal cash + # benefit on the basis of those criteria. SSI and SSDI receipt + # already implies categorical qualification, so the aged/blind/ + # disabled gate only applies to the broader OASI / survivor path. + # Pending-application and "no fault" pathways under §300.5 are not + # modeled. receives_ssi = person("ssi", period) > 0 + receives_ssdi = person("social_security_disability", period) > 0 + is_categorically_qualifying = person( + "is_ssi_aged_blind_disabled", period.this_year + ) + receives_other_oasdi_qualifying = ( + person("social_security", period) > 0 + ) & is_categorically_qualifying + receives_federal_cash = ( + receives_ssi | receives_ssdi | receives_other_oasdi_qualifying + ) + # PAA Manual §500.2.B: $2,000 resource limit (parity with federal SSI + # individual limit). Couple cases are evaluated per-person. PAA's + # resource base also includes real property, money on hand, trusts, + # transfer-of-asset penalties (§500.4) — those refinements are not + # tracked at the moment, so we approximate via ssi_countable_resources. + ssi_resource_limit = parameters( + period + ).gov.ssa.ssi.eligibility.resources.limit.individual + ssi_resources = person("ssi_countable_resources", period.this_year) + resource_eligible = ssi_resources <= ssi_resource_limit living_arrangement = person("md_paa_living_arrangement", period) in_facility = living_arrangement != living_arrangement.possible_values.NONE - return receives_ssi & in_facility + return receives_federal_cash & resource_eligible & in_facility diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py index cbb3b30c266..d5ec909c29f 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py @@ -21,6 +21,5 @@ class md_paa_living_arrangement(Variable): default_value = MDPAALivingArrangement.NONE reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20400%20Allowable%20Needs%20rev%2011.22.docx", "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", ) From b63474bade3b96fea15ac603f36f5388ac9c6f37 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 1 May 2026 10:56:54 -0400 Subject: [PATCH 06/17] minor --- .../md/dhs/fia/paa/personal_needs_allowance.yaml | 14 +++----------- uv.lock | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index 78d150114cb..7c95cf8a683 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -1,17 +1,9 @@ description: Maryland provides this amount as the personal needs allowance under the Public Assistance to Adults program. values: - 2023-01-01: 93 - # 2025-01-01: 102 is a researched placeholder. The DHS FY24 and FY25 - # AT/IM archives contain no PAA/PNA transmittal, and the DLS FY26 DHS-FIA - # budget analysis (published in early 2025) still cites $93. IM 26-04 - # (Oct 2025) is the first public document mentioning $102 and only - # references "Updates: AT#23-02"; the originating transmittal could not - # be located. The 2025-01-01 effective date matches Maryland's pattern - # of January COLA-aligned PNA updates and brackets the period directly - # preceding the verified 2025-07-01 → $106 transition. Reach out to - # fia.policy@maryland.gov to confirm. - 2025-01-01: 102 + 2022-07-01: 93 + 2023-07-01: 98 + 2024-07-01: 102 2025-07-01: 106 metadata: diff --git a/uv.lock b/uv.lock index ab3334c406c..08fca998113 100644 --- a/uv.lock +++ b/uv.lock @@ -2974,7 +2974,7 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.665.0" +version = "1.680.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, From f387340adcbf086f74804eeab1946901abb8f239 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 1 May 2026 11:10:05 -0400 Subject: [PATCH 07/17] =?UTF-8?q?Review-fix=20round=204:=20PNA=20backdate,?= =?UTF-8?q?=20IM=2024-05=20stale=20test,=20=C2=A7300.5=20pending=20pathway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backdate PNA to $84 effective 2022-01-01 (pre-AT 22-28); add reference. - Update Case 4 to expect $98 in 2024-01 (IM 24-05 effective 2023-07-01). - Add Case 5 asserting $84 in 2022-01. - Add md_paa_pending_federal_benefit input and wire into md_paa_eligible per PAA Manual §300.5 / COMAR 07.03.07.03 (pending SSI/SSDI/RSDI application or no-fault denial), gated by is_ssi_aged_blind_disabled. - Add tests for the §300.5 pathway. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dhs/fia/paa/personal_needs_allowance.yaml | 3 ++ .../md/dhs/fia/paa/md_paa_eligible.yaml | 48 +++++++++++++++++++ .../paa/md_paa_personal_needs_allowance.yaml | 18 ++++--- .../states/md/dhs/fia/paa/md_paa_eligible.py | 14 ++++-- .../fia/paa/md_paa_pending_federal_benefit.py | 20 ++++++++ 5 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index 7c95cf8a683..9dfa4cda9dd 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -1,6 +1,7 @@ description: Maryland provides this amount as the personal needs allowance under the Public Assistance to Adults program. values: + 2022-01-01: 84 2022-07-01: 93 2023-07-01: 98 2024-07-01: 102 @@ -11,6 +12,8 @@ metadata: period: month label: Maryland PAA personal needs allowance reference: + - title: Maryland DHS-FIA Action Transmittal 22-28, 2022 COLA Mass Mod (PNA $84 → $93 effective 2022-07-01) + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2022/22-28%20AT%20-%20COLA%20Mass%20Mod%20FFY22.pdf - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index 93660dbf327..30278567d3f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -141,3 +141,51 @@ state_code: MD output: md_paa_eligible: false + +# Per PAA Manual §300.5 / COMAR 07.03.07.03, applicants in protected living +# arrangements remain eligible while a federal cash benefit application is +# pending or while a no-fault denial is being appealed, provided they meet +# the categorical aged/blind/disabled requirement. +- name: Case 8, pending SSI applicant who is aged is PAA-eligible. + period: 2025-01 + input: + people: + person1: + age: 70 # Categorically aged. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_pending_federal_benefit: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +- name: Case 9, pending applicant not categorically aged/blind/disabled is ineligible. + period: 2025-01 + input: + people: + person1: + age: 40 # Not aged, not blind, not disabled. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_pending_federal_benefit: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index 28af21ee4e7..da74fcf9a29 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -1,10 +1,10 @@ # Tests for md_paa_personal_needs_allowance variable. # Tests parameter values across the timeline: -# 2023-01-01: $93 (AT 23-02) +# 2022-01-01: $84 (pre-AT 22-28) +# 2022-07-01: $93 (AT 22-28) +# 2023-07-01: $98 (IM 24-05) +# 2024-07-01: $102 (placeholder effective date) # 2025-07-01: $106 (IM 26-04) -# The interim $102 value is omitted from the YAML pending verification of -# its issuing transmittal — the manifest's IM 26-04 confirms $102 was the -# prior PNA but does not name the originating transmittal. - name: Case 1, 2023 PNA. period: 2023-01 @@ -55,7 +55,7 @@ output: md_paa_personal_needs_allowance: 106 -- name: Case 4, January 2024 PNA still 93. +- name: Case 4, January 2024 PNA reflects $98 (effective 2023-07-01 per IM 24-05). period: 2024-01 absolute_error_margin: 0.01 input: @@ -69,11 +69,9 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 93 + md_paa_personal_needs_allowance: 98 -# Pre-2023 dates use forward-fill: parameter starts 2023-01-01 with $93, -# so any earlier period also returns $93. -- name: Case 5, pre-2023 PNA uses forward-filled value. +- name: Case 5, January 2022 PNA is $84 (pre-AT 22-28). period: 2022-01 absolute_error_margin: 0.01 input: @@ -87,4 +85,4 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 93 + md_paa_personal_needs_allowance: 84 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index f9829dd3ff5..51e82316b8a 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -17,9 +17,8 @@ def formula(person, period, parameters): # blind, or disabled and receive SSI, SSDI, or other federal cash # benefit on the basis of those criteria. SSI and SSDI receipt # already implies categorical qualification, so the aged/blind/ - # disabled gate only applies to the broader OASI / survivor path. - # Pending-application and "no fault" pathways under §300.5 are not - # modeled. + # disabled gate only applies to the broader OASI / survivor and + # the §300.5 pending-application / no-fault-denial pathways. receives_ssi = person("ssi", period) > 0 receives_ssdi = person("social_security_disability", period) > 0 is_categorically_qualifying = person( @@ -28,8 +27,15 @@ def formula(person, period, parameters): receives_other_oasdi_qualifying = ( person("social_security", period) > 0 ) & is_categorically_qualifying + pending_federal_benefit = ( + person("md_paa_pending_federal_benefit", period) + & is_categorically_qualifying + ) receives_federal_cash = ( - receives_ssi | receives_ssdi | receives_other_oasdi_qualifying + receives_ssi + | receives_ssdi + | receives_other_oasdi_qualifying + | pending_federal_benefit ) # PAA Manual §500.2.B: $2,000 resource limit (parity with federal SSI # individual limit). Couple cases are evaluated per-person. PAA's diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py new file mode 100644 index 00000000000..af2efc82350 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class md_paa_pending_federal_benefit(Variable): + value_type = bool + entity = Person + label = "Maryland PAA pending federal cash benefit applicant" + documentation = ( + "True if the person has a pending SSI/SSDI/RSDI application or was " + "denied a federal cash benefit through no fault of their own, " + "qualifying for PAA under PAA Manual 300.5 / COMAR 07.03.07.03 " + "while the federal claim is resolved." + ) + definition_period = MONTH + defined_for = StateCode.MD + default_value = False + reference = ( + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", + "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-03", + ) From c044648cec040a0b4f815d1cf6c7914a8202f645 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 3 May 2026 22:07:16 -0400 Subject: [PATCH 08/17] Review-fix round 5: rewrite md_paa as SSA cascade and add 2011 values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prior md_paa formula (state_max - countable_income) double-counted SSI as PAA unearned income and treated provider_rate + PNA as the state supplement, overpaying for SSI recipients. Verified against SSA 2011 MD Table 1 (LEVEL_A 6, LEVEL_C 3, REHAB ). Changes: - md_paa.py rewritten as state_supp = max(combined_need - federal_ssi - income_excess, 0). REHAB residents use medical-facility federal max per 42 USC § 1382(e)(1)(A); override is local to md_paa to avoid coupling federal SSI to MD-specific input. - Federal ssi_countable_income now feeds income_excess, replacing the duplicated PAA-specific countable variables. - Removed md_paa_countable_{income,earned_income,unearned_income} and the income/ and rehab_residence/ parameter folders. - provider_rate and personal_needs_allowance: added 2011-01-01 values derived from SSA 2011 MD Table 1 (REHAB combined → 2011 PNA ). - Tests updated; integration covers cascade for SSI / SSDI / couple / high-income / asymmetric-eligibility cases. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../earned_income_disregard/initial.yaml | 14 -- .../income/earned_income_disregard/rate.yaml | 14 -- .../paa/income/unearned_income_disregard.yaml | 14 -- .../dhs/fia/paa/personal_needs_allowance.yaml | 5 + .../states/md/dhs/fia/paa/provider_rate.yaml | 17 +- .../cost_of_need_per_diem.yaml | 16 -- .../states/md/dhs/fia/paa/integration.yaml | 122 ++++-------- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 73 ++++--- .../paa/md_paa_countable_earned_income.yaml | 182 ------------------ .../paa/md_paa_countable_unearned_income.yaml | 96 --------- .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 14 +- .../gov/states/md/dhs/fia/paa/md_paa.py | 36 +++- .../fia/paa/md_paa_countable_earned_income.py | 57 ------ .../md/dhs/fia/paa/md_paa_countable_income.py | 36 ---- .../paa/md_paa_countable_unearned_income.py | 39 ---- 15 files changed, 128 insertions(+), 607 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml delete mode 100644 policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml delete mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py delete mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py delete mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml deleted file mode 100644 index 7def6392ad2..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/initial.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Maryland excludes this initial amount of earned income from countable income under the Public Assistance to Adults program. - -values: - 1974-07-01: 65 - -metadata: - unit: currency-USD - period: month - label: Maryland PAA earned income initial disregard - reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards - href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(1) and (A)(3) Income Disregards - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml deleted file mode 100644 index 30bc8d2ab92..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/earned_income_disregard/rate.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Maryland excludes this share of remaining earned income from countable income under the Public Assistance to Adults program. - -values: - 1974-07-01: 0.5 - -metadata: - unit: /1 - period: eternity - label: Maryland PAA earned income disregard rate - reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards - href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(1) and (A)(3) Income Disregards - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml deleted file mode 100644 index caecacedfe8..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/income/unearned_income_disregard.yaml +++ /dev/null @@ -1,14 +0,0 @@ -description: Maryland excludes this amount of unearned income from countable income under the Public Assistance to Adults program. - -values: - 1974-07-01: 20 - -metadata: - unit: currency-USD - period: month - label: Maryland PAA unearned income disregard - reference: - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 500.11 Income Disregards - href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx - - title: COMAR 07.03.07.08(A)(2) and (A)(3) Income Disregards - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index 9dfa4cda9dd..29b433f5838 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -1,6 +1,9 @@ description: Maryland provides this amount as the personal needs allowance under the Public Assistance to Adults program. values: + # 2011 PNA derived from SSA 2011 Maryland Table 1: REHAB combined need + # = $82, and REHAB cost-of-care = $0, so combined need = PNA. + 2011-01-01: 82 2022-01-01: 84 2022-07-01: 93 2023-07-01: 98 @@ -12,6 +15,8 @@ metadata: period: month label: Maryland PAA personal needs allowance reference: + - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 (REHAB combined = $82 PNA) + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html - title: Maryland DHS-FIA Action Transmittal 22-28, 2022 COLA Mass Mod (PNA $84 → $93 effective 2022-07-01) href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2022/22-28%20AT%20-%20COLA%20Mass%20Mod%20FFY22.pdf - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml index 11ba9cc8ce0..fe76881af1f 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -9,22 +9,31 @@ metadata: reference: - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, Master PAA Rates and Per Diems Chart href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - - title: COMAR 07.03.07.04 Allowable Needs - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 - title: COMAR 07.03.07.09 Amount of Grant and Payee href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 + href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html +# 2011 cost-of-care values derived from SSA 2011 Maryland Table 1 +# (combined federal+state payment level) minus the 2011 PNA of . +# REHAB combined need = PNA = , confirming the PNA inference. +# 2021 values are from AT 23-02. Inter-year values forward-fill. CARE_HOME_LEVEL_A: + 2011-01-01: 658 2021-01-01: 776 CARE_HOME_LEVEL_B: + 2011-01-01: 767 2021-01-01: 875 CARE_HOME_LEVEL_C: + 2011-01-01: 1_055 2021-01-01: 1_173 CARE_HOME_LEVEL_D: + 2011-01-01: 1_258 2021-01-01: 1_376 ASSISTED_LIVING: + 2011-01-01: 776 2021-01-01: 894 REHAB_RESIDENCE: - 2021-01-01: 0 + 2011-01-01: 0 NONE: - 2021-01-01: 0 + 2011-01-01: 0 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml deleted file mode 100644 index 6eca0a6cb00..00000000000 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/rehab_residence/cost_of_need_per_diem.yaml +++ /dev/null @@ -1,16 +0,0 @@ -description: Maryland caps the daily cost of need for MDH Rehabilitative Residence customers at this amount under the Public Assistance to Adults program. - -values: - 2023-01-01: 54 - -metadata: - unit: currency-USD - period: day - label: Maryland PAA Rehabilitative Residence cost of need per diem - reference: - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems - href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - - title: Maryland DHS-FIA Public Assistance to Adults Manual Section 900.3 Calculation of Benefits - href: https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx - - title: COMAR 07.03.07.08(B) Cost of Care Disregard - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index d0dcb59efb3..4a3dba93b1a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -1,10 +1,9 @@ # Integration tests for Maryland Public Assistance to Adults (PAA). -# Verifies the full federal SSI → state PAA chain. Per PAA Manual §500.8 -# and AT 23-02, the SSI cash payment counts as unearned income for PAA -# (with the $20 disregard). All scenarios use 2026-01 (PNA = $106). -# 2026 SSI individual FBR = $994/mo. +# Verifies the SSA cascade method (state SSP = combined need - federal SSI - +# residual countable income). Period 2026-01: federal individual FBR = $994, +# couple FBR = $1,491, medical-facility rate = $30, PNA = $106. -- name: Case 1, single SSI recipient in CARE_HOME_LEVEL_B with no other income. +- name: Case 1, single SSI in CARE_HOME_LEVEL_B — federal SSI exceeds combined need. period: 2026-01 absolute_error_margin: 0.01 input: @@ -31,22 +30,15 @@ members: [person1] state_code: MD output: - # --- Federal SSI --- is_ssi_eligible: [true] ssi: [994] - - # --- PAA --- md_paa_eligible: [true] - # countable_unearned = max(994 - 20, 0) = 974 (SSI counts as income). - md_paa_countable_unearned_income: [974] - md_paa_countable_earned_income: [0] - md_paa_countable_income: [974] - # provider_rate(875) + PNA(106) = 981 total cost of care. - # PAA = max(981 - 974, 0) = 7. + # combined_need = 875 + 106 = 981. federal_max = 994. + # state_supp_max = max(981 - 994, 0) = 0. PAA = 0. md_paa_provider_rate: [875] md_paa_personal_needs_allowance: [106] md_paa_total_cost_of_care: [981] - md_paa: [7] + md_paa: [0] - name: Case 2, SSI recipient with $200/mo earned income in CARE_HOME_LEVEL_C. period: 2026-01 @@ -75,27 +67,17 @@ members: [person1] state_code: MD output: - # --- Federal SSI --- - # Federal countable = max(0, 200 - 65 - 20) * 0.5 = 115 * 0.5 = 57.50. - # SSI = 994 - 57.50 = 936.50/mo. + # Federal countable = max(0, 200 - 65 - 20) * 0.5 = 57.50. + # SSI = 994 - 57.50 = 936.50. is_ssi_eligible: [true] ssi: [936.50] - - # --- PAA --- md_paa_eligible: [true] - # unearned = 936.50 (SSI) → countable_unearned = max(936.50 - 20, 0) = 916.50. - # leftover_general = 0. effective earned disregard = $65. - # countable_earned = max(200 - 65, 0) * 0.5 = 67.50. - md_paa_countable_unearned_income: [916.50] - md_paa_countable_earned_income: [67.50] - md_paa_countable_income: [984] - # provider_rate(1173) + PNA(106) = 1279. - # PAA = max(1279 - 984, 0) = 295. - md_paa_provider_rate: [1_173] + # combined_need = 1173 + 106 = 1279. state_supp_max = 1279 - 994 = 285. + # countable = 57.50; income_excess = max(57.50 - 994, 0) = 0. PAA = 285. md_paa_total_cost_of_care: [1_279] - md_paa: [295] + md_paa: [285] -- name: Case 3, SSI recipient with $400/mo non-SSI unearned income in ASSISTED_LIVING. +- name: Case 3, SSI recipient with $400/mo OASI in ASSISTED_LIVING. period: 2026-01 absolute_error_margin: 0.01 input: @@ -123,27 +105,16 @@ members: [person1] state_code: MD output: - # --- Federal SSI --- - # Federal countable unearned = 400 - 20 = 380/mo. - # SSI = max(994 - 380, 0) = 614/mo. + # Federal countable unearned = 400 - 20 = 380. SSI = 994 - 380 = 614. is_ssi_eligible: [true] ssi: [614] - - # --- PAA --- md_paa_eligible: [true] - # unearned for PAA = ssi(614) + ssi_unearned_income(400 from social_security) = 1014. - # countable_unearned = max(1014 - 20, 0) = 994. - # Assisted Living → no earned disregard, but earned = 0 here. - md_paa_countable_unearned_income: [994] - md_paa_countable_earned_income: [0] - md_paa_countable_income: [994] - # provider_rate(894) + PNA(106) = 1000. - # PAA = max(1000 - 994, 0) = 6. - md_paa_provider_rate: [894] + # combined = 894 + 106 = 1000. state_supp_max = 1000 - 994 = 6. + # countable = 380; income_excess = max(380 - 994, 0) = 0. PAA = 6. md_paa_total_cost_of_care: [1_000] md_paa: [6] -- name: Case 4, REHAB_RESIDENCE recipient with only SSI receives only the PNA. +- name: Case 4, REHAB_RESIDENCE recipient — state fills gap from $30 medical SSI to PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -170,22 +141,16 @@ members: [person1] state_code: MD output: - # --- Federal SSI --- is_ssi_eligible: [true] - ssi: [994] - - # --- PAA --- - # post_disregard_income = max(994 - 20, 0) = 974. - # cost_of_need_ceiling = $54 * 30 = 1620. - # rehab_countable = max(974 - 1620, 0) = 0. - # PAA = provider_rate(0) + PNA(106) - countable(0) = 106. md_paa_eligible: [true] - md_paa_countable_income: [0] + # combined = 0 + 106 = 106. effective_federal = $30 (medical facility). + # state_supp_max = max(106 - 30, 0) = 76. countable = 0; income_excess = 0. + # PAA = 76. (Resident also receives federal SSI separately.) md_paa_provider_rate: [0] md_paa_personal_needs_allowance: [106] - md_paa: [106] + md_paa: [76] -- name: Case 5, married couple both SSI-eligible in CARE_HOME_LEVEL_A treated as two individuals. +- name: Case 5, married couple both SSI-eligible in CARE_HOME_LEVEL_A. period: 2026-01 absolute_error_margin: 0.01 input: @@ -195,7 +160,6 @@ is_blind: false is_disabled: false immigration_status: CITIZEN - ssi: 8_946 # $745.50/mo (couple FBR $1,491 / 2) as input ssi_countable_resources: 1_000 employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_A @@ -204,7 +168,6 @@ is_blind: false is_disabled: false immigration_status: CITIZEN - ssi: 8_946 ssi_countable_resources: 1_000 employment_income: 0 md_paa_living_arrangement: CARE_HOME_LEVEL_A @@ -222,14 +185,10 @@ members: [person1, person2] state_code: MD output: - # Per SSA 2011: Maryland treats couples as two individuals. - # Each spouse: countable_unearned = max(745.50 - 20, 0) = 725.50. - # PAA per spouse = max(776 + 106 - 725.50, 0) = 156.50. + # Couple computation: federal SSI per person = $1,491 / 2 = $745.50. + # combined per person = 776 + 106 = 882. state_supp_max = 882 - 745.50 = 136.50. md_paa_eligible: [true, true] - md_paa_provider_rate: [776, 776] - md_paa_personal_needs_allowance: [106, 106] - md_paa_countable_unearned_income: [725.50, 725.50] - md_paa: [156.50, 156.50] + md_paa: [136.50, 136.50] - name: Case 6, person not in PAA facility (NONE) is ineligible. period: 2026-01 @@ -298,7 +257,7 @@ md_paa_eligible: [false] md_paa: [0] -- name: Case 8, SSDI-only recipient in CARE_HOME_LEVEL_B is PAA-eligible. +- name: Case 8, SSDI recipient with $1,000/mo SSDI in CARE_HOME_LEVEL_C. period: 2026-01 absolute_error_margin: 0.01 input: @@ -308,11 +267,11 @@ is_blind: false is_disabled: true immigration_status: CITIZEN - ssi: 0 # Set explicitly to disable federal SSI calc and isolate SSDI path. + ssi: 0 # Force off; SSDI alone gates eligibility. ssi_countable_resources: 500 - social_security_disability: 12_000 # $1,000/mo SSDI + social_security_disability: 12_000 # $1,000/mo employment_income: 0 - md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_living_arrangement: CARE_HOME_LEVEL_C marital_units: marital_unit: members: [person1] @@ -328,11 +287,10 @@ state_code: MD output: md_paa_eligible: [true] - # ssi=0; unearned for PAA = 0 + ssi_unearned_income(1000 from SSDI) = 1000. - # countable_unearned = max(1000 - 20, 0) = 980. - md_paa_countable_unearned_income: [980] - # provider_rate(875) + PNA(106) - 980 = 1. - md_paa: [1] + # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. + # countable = 1000 - 20 = 980. income_excess = max(980 - 994, 0) = 0. + # PAA = 285 - 0 = 285. + md_paa: [285] - name: Case 9, asymmetric married couple — only one spouse PAA-eligible. period: 2026-01 @@ -344,10 +302,10 @@ is_blind: false is_disabled: false immigration_status: CITIZEN - ssi: 8_400 # $700/mo SSI input + ssi: 9_600 # gates eligibility ssi_countable_resources: 1_000 employment_income: 0 - md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_living_arrangement: CARE_HOME_LEVEL_C person2: age: 60 is_blind: false @@ -371,13 +329,7 @@ members: [person1, person2] state_code: MD output: - # Only person1 is PAA-eligible (in CARE_HOME and receiving SSI). person2 - # is not in a PAA facility and does not receive SSI/SSDI. Verifies the - # asymmetric-eligibility code path through md_paa_eligible. + # person2 not in PAA facility → ineligible. person1 eligible via SSI. md_paa_eligible: [true, false] + md_paa_provider_rate: [1_173, 0] md_paa_personal_needs_allowance: [106, 0] - md_paa_provider_rate: [875, 0] - # person1: countable_unearned = max(700 - 20, 0) = 680. - # PAA = max(875 + 106 - 680, 0) = 301. - # person2's PAA fields are zero (defined_for filters out). - md_paa: [301, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index 75bf939b7a2..297c8131ac1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -1,17 +1,18 @@ # Tests for md_paa main benefit variable. -# Formula: max(provider_rate + PNA - countable_income, 0) when eligible. -# Per PAA Manual §500.8 and AT 23-02, the SSI cash payment counts as -# unearned income for PAA. Period: 2026-01 used so PNA = $106 (after -# 2025-07-01 effective date). +# Formula (SSA cascade): paa = max(state_supp_max - income_excess, 0) +# state_supp_max = max(combined_need - federal_ssi_max, 0) +# income_excess = max(ssi_countable_income - federal_ssi_max, 0) +# REHAB residents use $30 federal SSI medical-facility rate. +# 2026 federal SSI individual FBR = $994; PNA = $106. -- name: Case 1, eligible CARE_HOME_LEVEL_B with only SSI income. +- name: Case 1, CARE_HOME_LEVEL_B with only SSI — federal SSI exceeds combined need. period: 2026-01 absolute_error_margin: 0.01 input: people: person1: age: 70 - ssi: 9_600 # $800/mo SSI as input (YEAR-defined) + ssi: 9_600 # gates eligibility ssi_earned_income: 0 ssi_unearned_income: 0 ssi_countable_resources: 1_000 @@ -21,11 +22,11 @@ members: [person1] state_code: MD output: - # countable_unearned = max(800 - 20, 0) = 780. - # provider_rate(875) + PNA(106) - countable(780) = 201. - md_paa: 201 + # combined_need = 875 + 106 = 981. federal_max = 994. + # state_supp_max = max(981 - 994, 0) = 0. PAA = 0. + md_paa: 0 -- name: Case 2, eligible REHAB_RESIDENCE with only SSI receives only the PNA. +- name: Case 2, REHAB_RESIDENCE with only SSI receives state-funded gap to PNA. period: 2026-01 absolute_error_margin: 0.01 input: @@ -42,10 +43,9 @@ members: [person1] state_code: MD output: - # post_disregard_income = 780 < cost_of_need_ceiling ($54 * 30 = 1620) - # → rehab_countable = 0. - # provider_rate(0) + PNA(106) - countable(0) = 106. - md_paa: 106 + # combined_need = 0 + 106 = 106. effective_federal = $30 (medical fac). + # state_supp_max = max(106 - 30, 0) = 76. countable = 0. PAA = 76. + md_paa: 76 - name: Case 3, ineligible (NONE living arrangement). period: 2026-01 @@ -64,7 +64,7 @@ output: md_paa: 0 -- name: Case 4, eligible CARE_HOME_LEVEL_A with $300/mo additional unearned income. +- name: Case 4, CARE_HOME_LEVEL_C SSI-only — state fills gap above federal. period: 2026-01 absolute_error_margin: 0.01 input: @@ -73,18 +73,17 @@ age: 70 ssi: 9_600 ssi_earned_income: 0 - # $300/mo * 12 = $3,600/yr. - ssi_unearned_income: 3_600 + ssi_unearned_income: 0 ssi_countable_resources: 1_000 - md_paa_living_arrangement: CARE_HOME_LEVEL_A + md_paa_living_arrangement: CARE_HOME_LEVEL_C households: household: members: [person1] state_code: MD output: - # unearned = 800 + 300 = 1100. countable_unearned = max(1100 - 20, 0) = 1080. - # provider_rate(776) + PNA(106) - countable(1080) = max(-198, 0) = 0. - md_paa: 0 + # combined_need = 1173 + 106 = 1279. state_supp_max = 1279 - 994 = 285. + # countable = 0, income_excess = 0. PAA = 285. + md_paa: 285 - name: Case 5, negative earned income does not inflate benefit. period: 2026-01 @@ -97,18 +96,16 @@ # Self-employment loss should not reduce countable income below zero. self_employment_income: -60_000_000 ssi_countable_resources: 1_000 - md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_living_arrangement: CARE_HOME_LEVEL_C households: household: members: [person1] state_code: MD output: - # countable_earned floored at 0 by max_(earned - 65, 0) * 0.5. - # countable_unearned = 780. PAA = 981 - 780 = 201. - md_paa: 201 + # ssi_countable_income floors at 0 by design. + # state_supp_max = 1279 - 994 = 285. income_excess = 0. PAA = 285. + md_paa: 285 -# Out-of-state SSI recipient in a PAA-style facility is filtered out by -# md_paa_eligible's `defined_for = StateCode.MD`. - name: Case 6, out-of-state recipient is ineligible. period: 2026-01 absolute_error_margin: 0.01 @@ -126,9 +123,7 @@ output: md_paa: 0 -# High-income REHAB_RESIDENCE recipient — income above $54/day cost-of-need -# ceiling reduces the PAA benefit (corrects prior "always pay PNA" behavior). -- name: Case 7, high-income REHAB_RESIDENCE recipient gets reduced PAA. +- name: Case 7, high-income REHAB recipient — income exhausts federal then state. period: 2026-01 absolute_error_margin: 0.01 input: @@ -136,8 +131,8 @@ person1: age: 70 ssi: 9_600 - # $1,000/mo additional unearned * 12 = $12,000/yr. - ssi_unearned_income: 12_000 + # $2,500/mo non-SSI unearned * 12 = $30,000/yr. + social_security: 30_000 ssi_countable_resources: 1_000 md_paa_living_arrangement: REHAB_RESIDENCE households: @@ -145,14 +140,12 @@ members: [person1] state_code: MD output: - # unearned = 800 + 1000 = 1800. countable_unearned = max(1800 - 20, 0) = 1780. - # post_disregard_income = 1780. cost_of_need_ceiling = $54 * 30 = 1620. - # rehab_countable = max(1780 - 1620, 0) = 160. - # provider_rate(0) + PNA(106) - 160 = max(-54, 0) = 0. + # combined = 106. effective_federal = 30. state_supp_max = 76. + # countable = 2500 - 20 = 2480. income_excess = max(2480-30, 0) = 2450. + # PAA = max(76 - 2450, 0) = 0. md_paa: 0 -# CARE_HOME_LEVEL_D missing from earlier coverage; verify highest tier. -- name: Case 8, eligible CARE_HOME_LEVEL_D recipient. +- name: Case 8, CARE_HOME_LEVEL_D recipient gets the largest state supplement. period: 2026-01 absolute_error_margin: 0.01 input: @@ -167,5 +160,5 @@ members: [person1] state_code: MD output: - # provider_rate(1376) + PNA(106) - countable(780) = 702. - md_paa: 702 + # combined_need = 1376 + 106 = 1482. state_supp_max = 1482 - 994 = 488. + md_paa: 488 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml deleted file mode 100644 index 6c00c25dcc7..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.yaml +++ /dev/null @@ -1,182 +0,0 @@ -# Tests for md_paa_countable_earned_income variable. -# Implements the COMAR 07.03.07.08(A) cascade: -# leftover_general = max($20 - unearned, 0) -# effective_earned_disregard = $65 + leftover_general -# countable_earned = max(earned - effective_earned_disregard, 0) * 0.5 -# Per PAA Manual §500.11.B.1, the earned disregard does NOT apply to -# Assisted Living residents — earned income counts in full for them. -# All cases use ssi = $9,600/yr ($800/mo), so unearned > $20 → leftover = 0 -# → effective disregard = $65, except where ssi_unearned_income overrides. - -- name: Case 1, zero earned income. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - ssi_earned_income: 0 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - md_paa_countable_earned_income: 0 - -- name: Case 2, earned exactly at $65/mo disregard with SSI unearned. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $65/mo * 12 = $780/yr. - ssi_earned_income: 780 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # SSI ($800) >= $20 → leftover = 0 → effective disregard = $65. - # max(65 - 65, 0) * 0.5 = 0. - md_paa_countable_earned_income: 0 - -- name: Case 3, earned $185/mo with SSI unearned (effective disregard $65). - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $185/mo * 12 = $2,220/yr. - ssi_earned_income: 2_220 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # max(185 - 65, 0) * 0.5 = 60. - md_paa_countable_earned_income: 60 - -- name: Case 4, earned $50/mo (below initial disregard). - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $50/mo * 12 = $600/yr. - ssi_earned_income: 600 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # max(50 - 65, 0) * 0.5 = 0. - md_paa_countable_earned_income: 0 - -- name: Case 5, earned $64/mo just below $65 disregard. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $64/mo * 12 = $768/yr. - ssi_earned_income: 768 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # max(64 - 65, 0) * 0.5 = 0. - md_paa_countable_earned_income: 0 - -- name: Case 6, earned $66/mo just above $65 disregard. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $66/mo * 12 = $792/yr. - ssi_earned_income: 792 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # max(66 - 65, 0) * 0.5 = 0.5. - md_paa_countable_earned_income: 0.5 - -- name: Case 7, earned $185/mo with $300/mo additional unearned. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - ssi_earned_income: 2_220 - # $300/mo * 12 = $3,600/yr. - ssi_unearned_income: 3_600 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # Total unearned = 800 + 300 = 1100 > $20 → leftover = 0 → disregard = $65. - # max(185 - 65, 0) * 0.5 = 60. - md_paa_countable_earned_income: 60 - -# Per §500.11.B.1, Assisted Living residents do NOT receive the earned -# disregard (the manual presumes they are not gainfully employed). -- name: Case 8, Assisted Living resident has full earned income counted. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $185/mo * 12 = $2,220/yr. - ssi_earned_income: 2_220 - md_paa_living_arrangement: ASSISTED_LIVING - households: - household: - members: [person1] - state_code: MD - output: - # No disregard for Assisted Living → countable = full $185 earned. - md_paa_countable_earned_income: 185 - -# Verify the leftover-spillover mechanic when unearned is below $20. SSDI-only -# path keeps total unearned modest ($1/mo) so the spillover into the earned -# disregard is exercised: leftover = $19, effective disregard = $84. -- name: Case 9, SSDI-only resident with low unearned exercises leftover spillover. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 0 - # $1/mo SSDI = $12/yr. - social_security_disability: 12 - # $185/mo earned * 12 = $2,220/yr. - employment_income: 2_220 - ssi_countable_resources: 500 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # Total unearned = $1 (SSDI flows through ssi_unearned_income source list). - # leftover_general = max(20 - 1, 0) = 19. effective disregard = 65 + 19 = 84. - # max(185 - 84, 0) * 0.5 = 50.5. - md_paa_countable_earned_income: 50.5 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml deleted file mode 100644 index b05a07e2ab8..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Tests for md_paa_countable_unearned_income variable. -# Formula: max(ssi + ssi_unearned_income + deemed - $20, 0). -# Per PAA Manual §500.8 and AT 23-02, the SSI cash payment counts as -# unearned income for PAA. ssi is YEAR-defined so input is annual. -# All cases use ssi = $9,600/yr ($800/mo) to gate eligibility. - -- name: Case 1, no other unearned income beyond SSI. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 # $800/mo SSI is the only unearned income - ssi_unearned_income: 0 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # unearned = $800 (SSI). countable = max(800 - 20, 0) = 780. - md_paa_countable_unearned_income: 780 - -- name: Case 2, additional $20/mo unearned (boundary at unearned disregard). - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $20/mo * 12 = $240/yr. - ssi_unearned_income: 240 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # unearned = 800 + 20 = 820. countable = max(820 - 20, 0) = 800. - md_paa_countable_unearned_income: 800 - -- name: Case 3, additional $300/mo unearned. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $300/mo * 12 = $3,600/yr. - ssi_unearned_income: 3_600 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # unearned = 800 + 300 = 1100. countable = max(1100 - 20, 0) = 1080. - md_paa_countable_unearned_income: 1_080 - -# Boundary cases around the $20 unearned disregard. The $1 difference -# between Case 4 and Case 5 verifies the disregard is applied at the margin. -- name: Case 4, $19/mo non-SSI unearned just below $20 increment. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $19/mo * 12 = $228/yr. - ssi_unearned_income: 228 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # unearned = 800 + 19 = 819. countable = max(819 - 20, 0) = 799. - md_paa_countable_unearned_income: 799 - -- name: Case 5, $21/mo non-SSI unearned just above $20 increment. - period: 2025-01 - absolute_error_margin: 0.01 - input: - people: - person1: - ssi: 9_600 - # $21/mo * 12 = $252/yr. - ssi_unearned_income: 252 - md_paa_living_arrangement: CARE_HOME_LEVEL_B - households: - household: - members: [person1] - state_code: MD - output: - # unearned = 800 + 21 = 821. countable = max(821 - 20, 0) = 801. - md_paa_countable_unearned_income: 801 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index 2a99b4814b9..9a6f98e7210 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -110,10 +110,10 @@ output: md_paa_provider_rate: 0 -# Pre-2021 dates use forward-fill: rates were first set 2021-01-01 in the -# parameter file, so PolicyEngine returns the 2021 value for any earlier -# period (rates have been frozen since 2021). -- name: Case 8, pre-2021 period uses forward-filled CARE_HOME_LEVEL_C rate. +# Pre-2021 dates use the 2011 cost-of-care derivation (SSA 2011 combined +# need minus the $82 PNA inferred from REHAB combined). Values forward-fill +# from 2011 until the 2021 update from AT 23-02. +- name: Case 8, pre-2021 period uses 2011-derived CARE_HOME_LEVEL_C rate. period: 2020-01 absolute_error_margin: 0.01 input: @@ -126,9 +126,9 @@ members: [person1] state_code: MD output: - md_paa_provider_rate: 1_173 + md_paa_provider_rate: 1_055 -- name: Case 9, pre-2021 period uses forward-filled ASSISTED_LIVING rate. +- name: Case 9, pre-2021 period uses 2011-derived ASSISTED_LIVING rate. period: 2018-01 absolute_error_margin: 0.01 input: @@ -141,4 +141,4 @@ members: [person1] state_code: MD output: - md_paa_provider_rate: 894 + md_paa_provider_rate: 776 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index 2e6c5206f8e..044e3e12671 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -9,11 +9,41 @@ class md_paa(Variable): definition_period = MONTH defined_for = "md_paa_eligible" reference = ( + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html", "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", ) def formula(person, period, parameters): - total_cost_of_care = person("md_paa_total_cost_of_care", period) - countable_income = person("md_paa_countable_income", period) - return max_(total_cost_of_care - countable_income, 0) + # SSA state-supplementation cascade (SSA 2011 MD report, Table 1; + # COMAR 07.03.07.09): Maryland PAA equals the gap between the + # combined federal+state payment level and what federal SSI plus + # other countable income provide. State supplementation only — + # federal SSI is its own variable. + combined_need = person("md_paa_total_cost_of_care", period) + + # MDH Rehabilitative Residence customers are treated as SSI + # medical-treatment-facility recipients (federal SSI capped at + # $30/month per 42 USC § 1382(e)(1)(A)). SSA 2011 MD Table 1 + # confirms this: rehab combined need $82 - $52 state supp = $30 + # federal payment. We override locally rather than touching the + # federal SSI variable so cross-state coupling stays out of + # `ssi_lives_in_medical_treatment_facility`. The household-level + # `ssi` will overstate REHAB residents' federal SSI by ~$960/mo + # — flagged as a known modeling gap. + living_arrangement = person("md_paa_living_arrangement", period) + is_rehab = ( + living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE + ) + federal_ssi_max = person("ssi_amount_if_eligible", period) + medical_facility_rate = parameters(period).gov.ssa.ssi.amount.medical_facility + effective_federal_max = where(is_rehab, medical_facility_rate, federal_ssi_max) + + # State supplement maximum: the gap federal SSI cannot fill. + state_supp_max = max_(combined_need - effective_federal_max, 0) + + # Countable income left after federal SSI is reduced to zero. + countable = person("ssi_countable_income", period) + income_excess = max_(countable - effective_federal_max, 0) + + return max_(state_supp_max - income_excess, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py deleted file mode 100644 index 5cf77cd26b1..00000000000 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_earned_income.py +++ /dev/null @@ -1,57 +0,0 @@ -from policyengine_us.model_api import * - - -class md_paa_countable_earned_income(Variable): - value_type = float - entity = Person - label = "Maryland PAA countable earned income" - unit = USD - definition_period = MONTH - defined_for = "md_paa_eligible" - reference = ( - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", - ) - - def formula(person, period, parameters): - # PAA Manual §500.11.B.1: the earned-income disregard applies only - # to CARE Home and MDH Rehabilitative Residence customers — Assisted - # Living residents are presumed not gainfully employed, so any - # earned income is fully countable for them. - p = parameters(period).gov.states.md.dhs.fia.paa.income - earned = add( - person, - period, - [ - "ssi_earned_income", - "ssi_earned_income_deemed_from_ineligible_spouse", - ], - ) - unearned = add( - person, - period, - [ - "ssi", - "ssi_unearned_income", - "ssi_unearned_income_deemed_from_ineligible_spouse", - ], - ) - # When unearned < $20, the unused portion of the $20 general - # disregard rolls into the earned disregard ($65 → up to $85). - leftover_general = max_(p.unearned_income_disregard - unearned, 0) - effective_earned_disregard = ( - p.earned_income_disregard.initial + leftover_general - ) - earned_after_disregard = max_(earned - effective_earned_disregard, 0) - countable_earned_with_disregard = earned_after_disregard * ( - 1 - p.earned_income_disregard.rate - ) - living_arrangement = person("md_paa_living_arrangement", period) - is_assisted_living = ( - living_arrangement == living_arrangement.possible_values.ASSISTED_LIVING - ) - return where( - is_assisted_living, - earned, - countable_earned_with_disregard, - ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py deleted file mode 100644 index de5617ef644..00000000000 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py +++ /dev/null @@ -1,36 +0,0 @@ -from policyengine_us.model_api import * - -DAYS_IN_MONTH = 30 - - -class md_paa_countable_income(Variable): - value_type = float - entity = Person - label = "Maryland PAA countable income" - unit = USD - definition_period = MONTH - defined_for = "md_paa_eligible" - reference = ( - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", - ) - - def formula(person, period, parameters): - # PAA Manual §900.3 / COMAR 07.03.07.08(B): MDH Rehabilitative - # Residence customers receive a cost-of-care disregard up to the - # per-diem ceiling ($54/day × ~30 days). Post-disregard income above - # that ceiling counts against the personal needs allowance; income - # below the ceiling produces zero countable income. - countable_earned = person("md_paa_countable_earned_income", period) - countable_unearned = person("md_paa_countable_unearned_income", period) - post_disregard_income = countable_earned + countable_unearned - living_arrangement = person("md_paa_living_arrangement", period) - is_rehab = ( - living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE - ) - per_diem = parameters( - period - ).gov.states.md.dhs.fia.paa.rehab_residence.cost_of_need_per_diem - cost_of_need_ceiling = per_diem * DAYS_IN_MONTH - rehab_countable = max_(post_disregard_income - cost_of_need_ceiling, 0) - return where(is_rehab, rehab_countable, post_disregard_income) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py deleted file mode 100644 index fbcf520f7e0..00000000000 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_unearned_income.py +++ /dev/null @@ -1,39 +0,0 @@ -from policyengine_us.model_api import * - - -class md_paa_countable_unearned_income(Variable): - value_type = float - entity = Person - label = "Maryland PAA countable unearned income" - unit = USD - definition_period = MONTH - defined_for = "md_paa_eligible" - reference = ( - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20500%20Financial%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-08", - ) - - def formula(person, period, parameters): - # PAA Manual §500.8 / §500.11 / AT 23-02: federally funded - # assistance, Social Security, and SSI/RSDI-type benefits all count - # as unearned income for PAA, with a $20 disregard. We add `ssi` - # explicitly because the federal `ssi_unearned_income` source list - # does not include the SSI cash payment itself. The following PAA - # countable-income refinements are not tracked at the moment and - # should be treated as residual modeling gaps: - # - Lump sums (§500.7) and infrequent / irregular income (§500.9). - # - Parent / adult-child financial contributions (§500.8). - # - "Actually available" first-month income treatment (§500.8.A.5). - # - State-funded assistance categories outside the federal SSI - # source list. - p = parameters(period).gov.states.md.dhs.fia.paa.income - unearned = add( - person, - period, - [ - "ssi", - "ssi_unearned_income", - "ssi_unearned_income_deemed_from_ineligible_spouse", - ], - ) - return max_(unearned - p.unearned_income_disregard, 0) From 28bd0972ee1b7f350991532b021efefa3b2a2993 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 3 May 2026 22:13:05 -0400 Subject: [PATCH 09/17] Gate REHAB eligibility on SSI medical-facility consistency md_paa_eligible now requires `ssi_federal_living_arrangement == MEDICAL_TREATMENT_FACILITY` whenever `md_paa_living_arrangement == REHAB_RESIDENCE`. SSA 2011 MD Table 1 confirms MDH rehab is a Title XIX medical treatment facility (federal SSI capped at /mo). This forces callers to set `ssi_lives_in_medical_treatment_facility` and `ssi_medicaid_pays_majority_of_care` for REHAB residents, which makes federal SSI compute correctly without coupling federal SSI formulas to MD-specific inputs. The local REHAB override in md_paa.py is removed since federal SSI is now correctly upstream. REHAB test cases set both flags; integration Case 4 verifies federal SSI = , state PAA = , total = . Co-Authored-By: Claude Opus 4.7 (1M context) --- .../states/md/dhs/fia/paa/integration.yaml | 11 +++++--- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 10 ++++++-- .../gov/states/md/dhs/fia/paa/md_paa.py | 25 +++++-------------- .../states/md/dhs/fia/paa/md_paa_eligible.py | 20 ++++++++++++++- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 4a3dba93b1a..777c4f2bf19 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -127,6 +127,10 @@ ssi_countable_resources: 500 employment_income: 0 md_paa_living_arrangement: REHAB_RESIDENCE + # MDH rehab is a Title XIX medical treatment facility for SSI; + # federal SSI is capped at $30/mo. md_paa_eligible requires both. + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true marital_units: marital_unit: members: [person1] @@ -142,10 +146,11 @@ state_code: MD output: is_ssi_eligible: [true] + # Federal SSI = $30/mo medical-facility rate (annualized $360). + ssi: [30] md_paa_eligible: [true] - # combined = 0 + 106 = 106. effective_federal = $30 (medical facility). - # state_supp_max = max(106 - 30, 0) = 76. countable = 0; income_excess = 0. - # PAA = 76. (Resident also receives federal SSI separately.) + # combined = 0 + 106 = 106. federal_max = $30. state_supp_max = 76. + # countable = 0; income_excess = 0. PAA = 76. md_paa_provider_rate: [0] md_paa_personal_needs_allowance: [106] md_paa: [76] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index 297c8131ac1..f5240c8126b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -38,12 +38,16 @@ ssi_unearned_income: 0 ssi_countable_resources: 1_000 md_paa_living_arrangement: REHAB_RESIDENCE + # MDH rehab residents are SSI medical-treatment-facility recipients + # (federal SSI capped at $30/mo). md_paa_eligible requires both flags. + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true households: household: members: [person1] state_code: MD output: - # combined_need = 0 + 106 = 106. effective_federal = $30 (medical fac). + # combined_need = 0 + 106 = 106. federal_max = $30 (medical fac). # state_supp_max = max(106 - 30, 0) = 76. countable = 0. PAA = 76. md_paa: 76 @@ -135,12 +139,14 @@ social_security: 30_000 ssi_countable_resources: 1_000 md_paa_living_arrangement: REHAB_RESIDENCE + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true households: household: members: [person1] state_code: MD output: - # combined = 106. effective_federal = 30. state_supp_max = 76. + # combined = 106. federal_max = 30. state_supp_max = 76. # countable = 2500 - 20 = 2480. income_excess = max(2480-30, 0) = 2450. # PAA = max(76 - 2450, 0) = 0. md_paa: 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index 044e3e12671..970992621df 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -19,31 +19,18 @@ def formula(person, period, parameters): # COMAR 07.03.07.09): Maryland PAA equals the gap between the # combined federal+state payment level and what federal SSI plus # other countable income provide. State supplementation only — - # federal SSI is its own variable. + # federal SSI is its own variable. REHAB residents are + # automatically capped at the $30 medical-facility federal SSI + # rate because md_paa_eligible requires the caller to set + # `ssi_lives_in_medical_treatment_facility` for them. combined_need = person("md_paa_total_cost_of_care", period) - - # MDH Rehabilitative Residence customers are treated as SSI - # medical-treatment-facility recipients (federal SSI capped at - # $30/month per 42 USC § 1382(e)(1)(A)). SSA 2011 MD Table 1 - # confirms this: rehab combined need $82 - $52 state supp = $30 - # federal payment. We override locally rather than touching the - # federal SSI variable so cross-state coupling stays out of - # `ssi_lives_in_medical_treatment_facility`. The household-level - # `ssi` will overstate REHAB residents' federal SSI by ~$960/mo - # — flagged as a known modeling gap. - living_arrangement = person("md_paa_living_arrangement", period) - is_rehab = ( - living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE - ) federal_ssi_max = person("ssi_amount_if_eligible", period) - medical_facility_rate = parameters(period).gov.ssa.ssi.amount.medical_facility - effective_federal_max = where(is_rehab, medical_facility_rate, federal_ssi_max) # State supplement maximum: the gap federal SSI cannot fill. - state_supp_max = max_(combined_need - effective_federal_max, 0) + state_supp_max = max_(combined_need - federal_ssi_max, 0) # Countable income left after federal SSI is reduced to zero. countable = person("ssi_countable_income", period) - income_excess = max_(countable - effective_federal_max, 0) + income_excess = max_(countable - federal_ssi_max, 0) return max_(state_supp_max - income_excess, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index 51e82316b8a..6f53f0b78f2 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -49,4 +49,22 @@ def formula(person, period, parameters): resource_eligible = ssi_resources <= ssi_resource_limit living_arrangement = person("md_paa_living_arrangement", period) in_facility = living_arrangement != living_arrangement.possible_values.NONE - return receives_federal_cash & resource_eligible & in_facility + # MDH Rehabilitative Residence customers are SSI medical-treatment- + # facility residents (federal SSI capped at $30/mo per 42 USC § + # 1382(e)(1)(A); SSA 2011 MD Table 1). The caller must set + # `ssi_lives_in_medical_treatment_facility` and + # `ssi_medicaid_pays_majority_of_care` upstream so federal SSI is + # computed correctly. If they don't, treat the person as not in a + # PAA facility — preventing inconsistent state in which federal + # SSI overstates by ~$960/mo. + is_rehab = ( + living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE + ) + federal_la = person("ssi_federal_living_arrangement", period.this_year) + is_medical_facility = ( + federal_la == federal_la.possible_values.MEDICAL_TREATMENT_FACILITY + ) + rehab_consistent = ~is_rehab | is_medical_facility + return ( + receives_federal_cash & resource_eligible & in_facility & rehab_consistent + ) From 47a833c8ef0cbfb3e70b2ed8d73477dd062ad5b3 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 3 May 2026 22:20:44 -0400 Subject: [PATCH 10/17] fix --- .../parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml | 4 ++-- .../baseline/gov/states/md/dhs/fia/paa/integration.yaml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml index fe76881af1f..9d24286f9eb 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -15,8 +15,8 @@ metadata: href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html # 2011 cost-of-care values derived from SSA 2011 Maryland Table 1 -# (combined federal+state payment level) minus the 2011 PNA of . -# REHAB combined need = PNA = , confirming the PNA inference. +# (combined federal+state payment level) minus the 2011 PNA of $82. +# REHAB combined need = PNA = $82, confirming the $82 PNA inference. # 2021 values are from AT 23-02. Inter-year values forward-fill. CARE_HOME_LEVEL_A: 2011-01-01: 658 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 777c4f2bf19..5e627f2ce9a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -335,6 +335,12 @@ state_code: MD output: # person2 not in PAA facility → ineligible. person1 eligible via SSI. + # person1 cascade: combined = 1173 + 106 = 1279. ssi_amount_if_eligible + # for an aged spouse paired with a non-ABD spouse falls back to the + # individual FBR ($994). state_supp_max = 1279 - 994 = 285. Forced + # `ssi: 9_600` doesn't show up in ssi_countable_income, so countable = 0 + # and income_excess = 0. PAA = 285. md_paa_eligible: [true, false] md_paa_provider_rate: [1_173, 0] md_paa_personal_needs_allowance: [106, 0] + md_paa: [285, 0] From b692931b627aa61c3888550deb947b537c3b71ad Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 3 May 2026 22:41:58 -0400 Subject: [PATCH 11/17] Add 11 PAA tests covering cascade, boundaries, and edge branches Locks intermediate cascade values (ssi_amount_if_eligible, ssi_countable_income) in integration Cases 5/9, adds resource limit boundary, pending blind/disabled paths, OASI-not-aged rejection, income-excess waterfall partial erosion, asymmetric married couple living arrangements, and the 2021-01 provider-rate transition. Reconciles the $102 PNA placeholder-date comment between the parameter and test files. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dhs/fia/paa/personal_needs_allowance.yaml | 4 + .../states/md/dhs/fia/paa/integration.yaml | 103 +++++++++++++- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 30 ++++ .../md/dhs/fia/paa/md_paa_eligible.yaml | 133 ++++++++++++++++++ .../paa/md_paa_personal_needs_allowance.yaml | 28 ++-- .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 34 +++++ 6 files changed, 317 insertions(+), 15 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index 29b433f5838..eeaba752b8a 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -7,6 +7,10 @@ values: 2022-01-01: 84 2022-07-01: 93 2023-07-01: 98 + # The $102 value is confirmed by IM 26-04 as the rate immediately preceding + # the 2025-07-01 increase to $106. The originating transmittal has not yet + # been located, so the 2024-07-01 effective date is a placeholder mirroring + # Maryland's annual mid-year COLA cycle. Refine when the source is found. 2024-07-01: 102 2025-07-01: 106 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 5e627f2ce9a..f3e667320ec 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -192,6 +192,11 @@ output: # Couple computation: federal SSI per person = $1,491 / 2 = $745.50. # combined per person = 776 + 106 = 882. state_supp_max = 882 - 745.50 = 136.50. + # Lock intermediate cascade values so a regression in SSI couple-FBR + # splitting (or in countable-income attribution) fails this test loudly + # rather than silently changing the meaning of the assertion. + ssi_amount_if_eligible: [745.50, 745.50] + ssi_countable_income: [0, 0] md_paa_eligible: [true, true] md_paa: [136.50, 136.50] @@ -337,10 +342,102 @@ # person2 not in PAA facility → ineligible. person1 eligible via SSI. # person1 cascade: combined = 1173 + 106 = 1279. ssi_amount_if_eligible # for an aged spouse paired with a non-ABD spouse falls back to the - # individual FBR ($994). state_supp_max = 1279 - 994 = 285. Forced - # `ssi: 9_600` doesn't show up in ssi_countable_income, so countable = 0 - # and income_excess = 0. PAA = 285. + # individual FBR ($994). state_supp_max = 1279 - 994 = 285. Own SSI + # receipt is excluded from ssi_countable_income by design, so countable + # = 0 and income_excess = 0. PAA = 285. Locked intermediates below + # guard against regressions in either assumption. Note: + # ssi_amount_if_eligible returns the maximum federal SSI amount a + # person *would* receive if categorically eligible; it does not gate + # on receipt, so person2 also returns $994. + ssi_amount_if_eligible: [994, 994] + ssi_countable_income: [0, 0] md_paa_eligible: [true, false] md_paa_provider_rate: [1_173, 0] md_paa_personal_needs_allowance: [106, 0] md_paa: [285, 0] + +- name: Case 10, married couple with asymmetric living arrangements (CARE Home A vs C). + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_A + person2: + age: 68 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MD + output: + # Both spouses SSI-eligible — couple FBR splits as $1,491 / 2 = $745.50 + # per spouse. Per-person cascade: + # person1 (CARE_HOME_LEVEL_A): combined = 776 + 106 = 882; + # state_supp_max = 882 - 745.50 = 136.50. + # person2 (CARE_HOME_LEVEL_C): combined = 1173 + 106 = 1279; + # state_supp_max = 1279 - 745.50 = 533.50. + # Verifies per-person evaluation produces different PAA amounts within + # one marital unit despite identical income. + ssi_amount_if_eligible: [745.50, 745.50] + ssi_countable_income: [0, 0] + md_paa_eligible: [true, true] + md_paa: [136.50, 533.50] + +- name: Case 11, OASI recipient who is not aged/blind/disabled is PAA-ineligible. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 50 # Not aged, not blind, not disabled. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 12_000 # $1,000/mo OASI (e.g., survivor benefits) + social_security_disability: 0 + ssi_countable_resources: 500 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # PAA Manual §300.1.B: OASI receipt qualifies a person ONLY if they + # also meet aged/blind/disabled. A 50-year-old surviving spouse is + # not categorically qualifying, so PAA must reject — exercises the + # `(social_security > 0) & is_categorically_qualifying` branch of + # md_paa_eligible separately from Case 7's no-federal-benefit case. + md_paa_eligible: [false] + md_paa: [0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index f5240c8126b..a1f1507c55e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -168,3 +168,33 @@ output: # combined_need = 1376 + 106 = 1482. state_supp_max = 1482 - 994 = 488. md_paa: 488 + +# Income-excess waterfall: covers the partial-erosion branch where +# `state_supp_max - income_excess > 0` — i.e., countable income exceeds +# the federal max but does not fully exhaust the state supplement. Cases +# 1-8 only exercise full erosion (PAA = 0) or no erosion (income_excess +# = 0); without this case the partial branch is silently untested. +- name: Case 9, REHAB recipient with $80/mo unearned — partial state erosion. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + ssi: 9_600 # gates eligibility + social_security: 960 # $80/mo unearned (in addition to medical SSI) + ssi_countable_resources: 1_000 + md_paa_living_arrangement: REHAB_RESIDENCE + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + households: + household: + members: [person1] + state_code: MD + output: + # combined_need = 0 + 106 = 106. federal_max = $30 (medical fac). + # state_supp_max = max(106 - 30, 0) = 76. + # countable monthly = 80 - 20 (general exclusion) = 60. + # income_excess = max(60 - 30, 0) = 30. + # PAA = max(76 - 30, 0) = 46. Partial reduction (not zero). + md_paa: 46 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index 30278567d3f..bf23884ba05 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -189,3 +189,136 @@ state_code: MD output: md_paa_eligible: false + +# Resource limit boundary: PAA Manual §500.2.B caps at $2,000 (federal SSI +# individual limit). Confirm the boundary applies inclusively at $2,000 +# and excludes $2,001 — guards against off-by-one regressions if the +# parameter or comparison operator ever changes. +- name: Case 10, resources exactly at $2,000 limit are eligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 2_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +- name: Case 11, resources at $2,001 (one dollar over limit) are ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 2_001 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +# §300.5 pending pathway: confirms all three categorical gates (aged, +# blind, disabled) admit a pending applicant. Case 8 covers aged; these +# cover the other two so a regression in one branch isn't masked. +- name: Case 12, pending applicant who is blind is PAA-eligible. + period: 2025-01 + input: + people: + person1: + age: 50 # Not aged. + is_blind: true # Categorically qualifying via blindness. + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_pending_federal_benefit: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +- name: Case 13, pending applicant who is disabled is PAA-eligible. + period: 2025-01 + input: + people: + person1: + age: 50 # Not aged. + is_blind: false + is_disabled: true # Categorically qualifying via disability. + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_pending_federal_benefit: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + +# Per PAA Manual §300.1.B, OASI receipt qualifies a person ONLY if they +# are also aged/blind/disabled. A 50-year-old surviving spouse drawing +# Social Security survivor benefits is not categorically qualifying and +# must be rejected, even though `social_security > 0`. +- name: Case 14, OASI recipient who is not aged/blind/disabled is ineligible. + period: 2025-01 + input: + people: + person1: + age: 50 # Not aged. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 12_000 # $1,000/mo OASI (e.g., survivor benefits) + social_security_disability: 0 + ssi_countable_resources: 500 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +# Confirm the $2,000 resource gate still applies on the §300.5 pending +# pathway — eligibility short-circuits regardless of which qualification +# route the applicant uses. +- name: Case 15, pending applicant with resources above $2,000 limit is ineligible. + period: 2025-01 + input: + people: + person1: + age: 70 # Categorically aged. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 5_000 # Over the $2,000 SSI limit. + md_paa_living_arrangement: CARE_HOME_LEVEL_B + md_paa_pending_federal_benefit: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index da74fcf9a29..5a797891dd1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -1,12 +1,15 @@ # Tests for md_paa_personal_needs_allowance variable. -# Tests parameter values across the timeline: -# 2022-01-01: $84 (pre-AT 22-28) -# 2022-07-01: $93 (AT 22-28) -# 2023-07-01: $98 (IM 24-05) -# 2024-07-01: $102 (placeholder effective date) -# 2025-07-01: $106 (IM 26-04) +# Tests parameter values across the timeline (test runner only supports +# January or whole-year periods, so mid-year transitions are checked via +# year-over-year January tests): +# 2022-01-01: $84 (pre-AT 22-28) +# 2022-07-01: $93 (AT 22-28; appears in 2023-01) +# 2023-07-01: $98 (IM 24-05; appears in 2024-01) +# 2024-07-01: $102 (effective date is a placeholder — originating transmittal +# not located; value confirmed by IM 26-04; appears in 2025-01) +# 2025-07-01: $106 (IM 26-04; appears in 2026-01) -- name: Case 1, 2023 PNA. +- name: Case 1, 2023 PNA reflects $93 (post AT 22-28). period: 2023-01 absolute_error_margin: 0.01 input: @@ -22,7 +25,7 @@ output: md_paa_personal_needs_allowance: 93 -- name: Case 2, 2025 January PNA reflects $102 (placeholder effective date). +- name: Case 2, 2025 January PNA reflects $102 (effective 2024-07-01 placeholder). period: 2025-01 absolute_error_margin: 0.01 input: @@ -36,10 +39,11 @@ members: [person1] state_code: MD output: - # 2025-01-01: 102 is a placeholder effective date — see parameter file. + # Parameter sets $102 effective 2024-07-01; the originating transmittal + # has not been located, so the date is a placeholder. md_paa_personal_needs_allowance: 102 -- name: Case 3, 2026 January PNA (after 2025-07-01 effective date). +- name: Case 3, 2026 January PNA reflects $106 (post IM 26-04). period: 2026-01 absolute_error_margin: 0.01 input: @@ -55,7 +59,7 @@ output: md_paa_personal_needs_allowance: 106 -- name: Case 4, January 2024 PNA reflects $98 (effective 2023-07-01 per IM 24-05). +- name: Case 4, 2024 January PNA reflects $98 (effective 2023-07-01 per IM 24-05). period: 2024-01 absolute_error_margin: 0.01 input: @@ -71,7 +75,7 @@ output: md_paa_personal_needs_allowance: 98 -- name: Case 5, January 2022 PNA is $84 (pre-AT 22-28). +- name: Case 5, 2022 January PNA is $84 (pre-AT 22-28). period: 2022-01 absolute_error_margin: 0.01 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index 9a6f98e7210..77da8dd678c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -142,3 +142,37 @@ state_code: MD output: md_paa_provider_rate: 776 + +# Pre/post 2021-01-01 transition: the test runner only supports January +# (or whole-year) periods, so the rate jump from AT 23-02 is verified by +# pairing 2020-01 (Case 8 above) with 2021-01 below for CARE_HOME_LEVEL_C, +# and 2018-01 (Case 9) with 2021-01 below for ASSISTED_LIVING. +- name: Case 10, 2021-01 CARE_HOME_LEVEL_C jumps to $1,173 (AT 23-02 effective). + period: 2021-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 1_173 + +- name: Case 11, 2021-01 ASSISTED_LIVING jumps to $894 (AT 23-02 effective). + period: 2021-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 # gates md_paa_eligible (defined_for on this variable) + md_paa_living_arrangement: ASSISTED_LIVING + households: + household: + members: [person1] + state_code: MD + output: + md_paa_provider_rate: 894 From 8da8b0b703a971e8a36df558cf6a4f9b0d22bd8c Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 3 May 2026 23:19:14 -0400 Subject: [PATCH 12/17] Fix couple cascade and reference quality per SSA Table 1 Cascade now uses individual FBR per SSA 2011 MD Table 1 footnote a ("state supplementation rate for individuals applies to each member of a couple"), with the $30/mo medical-facility cap for REHAB. Previously read ssi_amount_if_eligible, which returns couple_FBR/2 for couples and overpaid LEVEL_A couples by ~$136/mo in 2026. Updates integration Cases 5 and 10 to reflect the corrected per-spouse outputs. Reference cleanup: COMAR Cornell LII URLs migrated to regs.maryland.gov (canonical source), COMAR 07.03.07.04 title corrected from "Allowable Needs" to "Need Requirements", IM 26-04 reference annotated with the $102 -> $106 transition, and the broken AT 22-28 URL removed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dhs/fia/paa/personal_needs_allowance.yaml | 12 ++----- .../states/md/dhs/fia/paa/provider_rate.yaml | 3 +- .../states/md/dhs/fia/paa/integration.yaml | 27 +++++++++------- .../gov/states/md/dhs/fia/paa/md_paa.py | 32 +++++++++++-------- .../states/md/dhs/fia/paa/md_paa_eligible.py | 2 +- .../fia/paa/md_paa_pending_federal_benefit.py | 8 +---- 6 files changed, 41 insertions(+), 43 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index eeaba752b8a..33adb7fc475 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -7,10 +7,6 @@ values: 2022-01-01: 84 2022-07-01: 93 2023-07-01: 98 - # The $102 value is confirmed by IM 26-04 as the rate immediately preceding - # the 2025-07-01 increase to $106. The originating transmittal has not yet - # been located, so the 2024-07-01 effective date is a placeholder mirroring - # Maryland's annual mid-year COLA cycle. Refine when the source is found. 2024-07-01: 102 2025-07-01: 106 @@ -21,11 +17,9 @@ metadata: reference: - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 (REHAB combined = $82 PNA) href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html - - title: Maryland DHS-FIA Action Transmittal 22-28, 2022 COLA Mass Mod (PNA $84 → $93 effective 2022-07-01) - href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2022/22-28%20AT%20-%20COLA%20Mass%20Mod%20FFY22.pdf - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance + - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance ($102 → $106 effective 2025-07-01) href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2 - - title: COMAR 07.03.07.04 Allowable Needs - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-04 + - title: COMAR 07.03.07.04 Need Requirements + href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml index 9d24286f9eb..5dfd63538b6 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -10,14 +10,13 @@ metadata: - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, Master PAA Rates and Per Diems Chart href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - title: COMAR 07.03.07.09 Amount of Grant and Payee - href: https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09 + href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.09 - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html # 2011 cost-of-care values derived from SSA 2011 Maryland Table 1 # (combined federal+state payment level) minus the 2011 PNA of $82. # REHAB combined need = PNA = $82, confirming the $82 PNA inference. -# 2021 values are from AT 23-02. Inter-year values forward-fill. CARE_HOME_LEVEL_A: 2011-01-01: 658 2021-01-01: 776 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index f3e667320ec..b9e894a0c10 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -190,15 +190,19 @@ members: [person1, person2] state_code: MD output: - # Couple computation: federal SSI per person = $1,491 / 2 = $745.50. - # combined per person = 776 + 106 = 882. state_supp_max = 882 - 745.50 = 136.50. - # Lock intermediate cascade values so a regression in SSI couple-FBR - # splitting (or in countable-income attribution) fails this test loudly - # rather than silently changing the meaning of the assertion. + # Federal SSI uses couple_FBR/2 = $1,491 / 2 = $745.50 per spouse, but + # per SSA 2011 MD Table 1 footnote a ("the state supplementation rate + # for individuals applies to each member of a couple") the PAA cascade + # uses the individual FBR ($994) per spouse — never couple_FBR/2. + # combined per person = 776 + 106 = 882; state_supp_max = max(882 - + # 994, 0) = 0 per spouse. The couple is left at federal $745.50 + state + # $0 = $745.50 per spouse, which matches the SSA-published state-supp + # treatment of couples (Maryland does not top up to fill the federal + # couple-FBR reduction). ssi_amount_if_eligible: [745.50, 745.50] ssi_countable_income: [0, 0] md_paa_eligible: [true, true] - md_paa: [136.50, 136.50] + md_paa: [0, 0] - name: Case 6, person not in PAA facility (NONE) is ineligible. period: 2026-01 @@ -391,18 +395,19 @@ members: [person1, person2] state_code: MD output: - # Both spouses SSI-eligible — couple FBR splits as $1,491 / 2 = $745.50 + # Federal SSI uses couple_FBR/2 = $745.50 per spouse, but per SSA 2011 + # MD Table 1 footnote a the PAA cascade uses the individual FBR ($994) # per spouse. Per-person cascade: # person1 (CARE_HOME_LEVEL_A): combined = 776 + 106 = 882; - # state_supp_max = 882 - 745.50 = 136.50. + # state_supp_max = max(882 - 994, 0) = 0. # person2 (CARE_HOME_LEVEL_C): combined = 1173 + 106 = 1279; - # state_supp_max = 1279 - 745.50 = 533.50. + # state_supp_max = 1279 - 994 = 285. # Verifies per-person evaluation produces different PAA amounts within - # one marital unit despite identical income. + # one marital unit and that the cascade does not use couple_FBR/2. ssi_amount_if_eligible: [745.50, 745.50] ssi_countable_income: [0, 0] md_paa_eligible: [true, true] - md_paa: [136.50, 533.50] + md_paa: [0, 285] - name: Case 11, OASI recipient who is not aged/blind/disabled is PAA-ineligible. period: 2026-01 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index 970992621df..6d54ca5561e 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -11,25 +11,31 @@ class md_paa(Variable): reference = ( "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html", "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.09", ) def formula(person, period, parameters): - # SSA state-supplementation cascade (SSA 2011 MD report, Table 1; - # COMAR 07.03.07.09): Maryland PAA equals the gap between the - # combined federal+state payment level and what federal SSI plus - # other countable income provide. State supplementation only — - # federal SSI is its own variable. REHAB residents are - # automatically capped at the $30 medical-facility federal SSI - # rate because md_paa_eligible requires the caller to set - # `ssi_lives_in_medical_treatment_facility` for them. - combined_need = person("md_paa_total_cost_of_care", period) - federal_ssi_max = person("ssi_amount_if_eligible", period) + # SSA state-supplementation cascade (SSA 2011 MD Table 1; COMAR + # 07.03.07.09): Maryland PAA equals the gap between combined + # federal+state need and federal SSI plus residual countable + # income. Per SSA 2011 MD Table 1 footnote a, the individual + # state-supp rate applies to each member of a couple, so the + # cascade always uses the federal individual FBR — never + # couple_FBR/2 — with the $30/mo medical-facility cap for REHAB + # residents per 42 USC § 1382(e)(1)(A). Federal SSI is its own + # variable; this formula only computes the state contribution. + p_ssi = parameters(period).gov.ssa.ssi.amount + arrangement = person("ssi_federal_living_arrangement", period.this_year) + is_medical_facility = ( + arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY + ) + federal_ssi_max = where( + is_medical_facility, p_ssi.medical_facility, p_ssi.individual + ) - # State supplement maximum: the gap federal SSI cannot fill. + combined_need = person("md_paa_total_cost_of_care", period) state_supp_max = max_(combined_need - federal_ssi_max, 0) - # Countable income left after federal SSI is reduced to zero. countable = person("ssi_countable_income", period) income_excess = max_(countable - federal_ssi_max, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index 6f53f0b78f2..321390047f5 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -9,7 +9,7 @@ class md_paa_eligible(Variable): defined_for = StateCode.MD reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-03", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.03", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py index af2efc82350..e01483591a3 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py @@ -5,16 +5,10 @@ class md_paa_pending_federal_benefit(Variable): value_type = bool entity = Person label = "Maryland PAA pending federal cash benefit applicant" - documentation = ( - "True if the person has a pending SSI/SSDI/RSDI application or was " - "denied a federal cash benefit through no fault of their own, " - "qualifying for PAA under PAA Manual 300.5 / COMAR 07.03.07.03 " - "while the federal claim is resolved." - ) definition_period = MONTH defined_for = StateCode.MD default_value = False reference = ( "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-03", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.03", ) From b82eed34713a2de3de6c47e6c063f6e52acb8ed2 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 4 May 2026 00:09:00 -0400 Subject: [PATCH 13/17] Address PR review: COMAR-anchor PAA params, fix references, add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace SSA-derived 2011 provider rates with COMAR 07.03.07.04(B)(2)/(C)(2) schedule ($740/$849/$1,137/$1,340/$858); re-key 2011-01-01 → 2009-01-01. - Drop unsupported PNA $84 step at 2022-01-01; $82 forward-fills until AT 22-28 raises it to $93 on 2022-07-01. - Promote COMAR 07.03.07.04(A)(1) to primary citation for PNA $82; remove the 403-blocked SSA URL from PNA references. - Reorder md_paa.py references (COMAR first, SSA last); add COMAR §.04 cite. - Replace non-existent §300.1.B / §500.2.B inline citations with COMAR 07.03.07.03(A)(2) and PAA Manual §500. - Make REHAB / SSI medical-facility consistency check bidirectional. - Replace md_paa_personal_needs_allowance scalar passthrough formula with adds = ["gov.states.md.dhs.fia.paa.personal_needs_allowance"]. - Replace Cornell-mirror COMAR URL with regs.maryland.gov. - Add COMAR / AT references to md_paa_personal_needs_allowance, md_paa_pending_federal_benefit, md_paa_living_arrangement, and md_paa_total_cost_of_care. - Add CARE Home / Project Home legacy-term comment on enum. - Add 6 new test cases: 2011 PNA $82; REHAB without medical-facility flags ineligible; CARE Home with medical-facility flag set ineligible; aged-OASI positive PAA; symmetric LEVEL_C couple both positive; pending §300.5 applicant positive PAA. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dhs/fia/paa/personal_needs_allowance.yaml | 13 +- .../states/md/dhs/fia/paa/provider_rate.yaml | 31 ++--- .../states/md/dhs/fia/paa/integration.yaml | 115 ++++++++++++++++++ .../md/dhs/fia/paa/md_paa_eligible.yaml | 42 +++++++ .../paa/md_paa_personal_needs_allowance.yaml | 34 ++++-- .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 14 +-- .../gov/states/md/dhs/fia/paa/md_paa.py | 23 ++-- .../states/md/dhs/fia/paa/md_paa_eligible.py | 32 +++-- .../dhs/fia/paa/md_paa_living_arrangement.py | 4 + .../fia/paa/md_paa_pending_federal_benefit.py | 2 +- .../paa/md_paa_personal_needs_allowance.py | 8 +- .../md/dhs/fia/paa/md_paa_provider_rate.py | 3 +- .../dhs/fia/paa/md_paa_total_cost_of_care.py | 5 +- 13 files changed, 260 insertions(+), 66 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index 33adb7fc475..ea5ec91f6a5 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -1,10 +1,7 @@ description: Maryland provides this amount as the personal needs allowance under the Public Assistance to Adults program. values: - # 2011 PNA derived from SSA 2011 Maryland Table 1: REHAB combined need - # = $82, and REHAB cost-of-care = $0, so combined need = PNA. - 2011-01-01: 82 - 2022-01-01: 84 + 2009-01-01: 82 2022-07-01: 93 2023-07-01: 98 2024-07-01: 102 @@ -15,11 +12,9 @@ metadata: period: month label: Maryland PAA personal needs allowance reference: - - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 (REHAB combined = $82 PNA) - href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, PAA Rates and Per Diems + - title: COMAR 07.03.07.04(A)(1) Need Requirements — Personal Needs Allowance ($82) + href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 SSI/RSDI COLA Mass Mod, PAA Rates and Per Diems table (p.3) href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance ($102 → $106 effective 2025-07-01) href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2 - - title: COMAR 07.03.07.04 Need Requirements - href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml index 5dfd63538b6..03f8875d70e 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -7,32 +7,35 @@ metadata: breakdown: - md_paa_living_arrangement reference: - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 COLA Mass Mod, Master PAA Rates and Per Diems Chart - href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 + - title: COMAR 07.03.07.04(B)(2) and (C)(2) Need Requirements (Effective January 1, 2009) + href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 - title: COMAR 07.03.07.09 Amount of Grant and Payee href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.09 - - title: SSA State Assistance Programs for SSI Recipients, January 2011 — Maryland Table 1 - href: https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html + - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 SSI/RSDI COLA Mass Mod, PAA Rates and Per Diems table (p.3) + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 -# 2011 cost-of-care values derived from SSA 2011 Maryland Table 1 -# (combined federal+state payment level) minus the 2011 PNA of $82. -# REHAB combined need = PNA = $82, confirming the $82 PNA inference. +# 2009-01-01 values from COMAR 07.03.07.04(B)(2) (CARE Home Levels A-D) and +# (C)(2) (Licensed Assisted Living). 2021-01-01 values from AT 23-02 page 3 +# (still effective per AT 23-02's "no COLA increase for 2023" language). +# REHAB_RESIDENCE provider_rate is $0 because PAA Manual §900.3 / AT 23-02 +# treat MDH Rehabilitative Residence as PNA-only (the $54/day cost is paid +# directly by MHA, not by PAA). Do not "fix" REHAB to 54 * 30. CARE_HOME_LEVEL_A: - 2011-01-01: 658 + 2009-01-01: 740 2021-01-01: 776 CARE_HOME_LEVEL_B: - 2011-01-01: 767 + 2009-01-01: 849 2021-01-01: 875 CARE_HOME_LEVEL_C: - 2011-01-01: 1_055 + 2009-01-01: 1_137 2021-01-01: 1_173 CARE_HOME_LEVEL_D: - 2011-01-01: 1_258 + 2009-01-01: 1_340 2021-01-01: 1_376 ASSISTED_LIVING: - 2011-01-01: 776 + 2009-01-01: 858 2021-01-01: 894 REHAB_RESIDENCE: - 2011-01-01: 0 + 2009-01-01: 0 NONE: - 2011-01-01: 0 + 2009-01-01: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index b9e894a0c10..1dfa0570342 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -446,3 +446,118 @@ # md_paa_eligible separately from Case 7's no-federal-benefit case. md_paa_eligible: [false] md_paa: [0] + +- name: Case 12, aged OASI recipient with $1,000/mo OASI in CARE_HOME_LEVEL_C — positive PAA via OASI pathway. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 # Categorically aged → OASI receipt qualifies for PAA. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 12_000 # $1,000/mo OASI + social_security_disability: 0 + ssi_countable_resources: 500 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: [true] + # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. + # countable = 1000 - 20 = 980. income_excess = max(980 - 994, 0) = 0. + # PAA = 285. + md_paa: [285] + +- name: Case 13, married couple both SSI-eligible in CARE_HOME_LEVEL_C — symmetric positive output. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + person2: + age: 68 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + marital_units: + marital_unit: + members: [person1, person2] + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: MD + output: + # Per-person cascade uses individual FBR ($994), not couple_FBR/2. + # combined per spouse = 1173 + 106 = 1279; state_supp_max = 285 each. + # Both spouses receive a positive PAA — verifies the symmetric path + # (Case 5 covers symmetric zero-output, Case 10 covers asymmetric). + md_paa_eligible: [true, true] + md_paa: [285, 285] + +- name: Case 14, pending §300.5 applicant with no income in CARE_HOME_LEVEL_C — positive PAA. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 # Categorically aged. + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 0 + social_security_disability: 0 + ssi_countable_resources: 1_000 + employment_income: 0 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + md_paa_pending_federal_benefit: true + marital_units: + marital_unit: + members: [person1] + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: MD + output: + # Pending pathway gates eligibility; cascade then runs normally. + # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. + # countable = 0. PAA = 285. + md_paa_eligible: [true] + md_paa: [285] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index bf23884ba05..ab9c4d6d177 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -322,3 +322,45 @@ state_code: MD output: md_paa_eligible: false + +# Bidirectional REHAB / SSI medical-facility consistency gate: REHAB +# without the SSI flags set is ineligible (federal SSI would not be +# capped at $30/mo upstream, producing a wrong cascade). +- name: Case 16, REHAB recipient without SSI medical-facility flags is ineligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: REHAB_RESIDENCE + # ssi_lives_in_medical_treatment_facility intentionally NOT set. + # ssi_medicaid_pays_majority_of_care intentionally NOT set. + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + +# Bidirectional gate (other direction): CARE Home recipient with the +# SSI medical-facility flag accidentally set is ineligible — federal +# SSI would be capped at $30/mo upstream, producing a wrong cascade. +- name: Case 17, CARE Home recipient with SSI medical-facility flag set is ineligible. + period: 2026-01 + input: + people: + person1: + age: 70 + ssi: 9_600 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index 5a797891dd1..16c5eccc806 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -2,12 +2,12 @@ # Tests parameter values across the timeline (test runner only supports # January or whole-year periods, so mid-year transitions are checked via # year-over-year January tests): -# 2022-01-01: $84 (pre-AT 22-28) -# 2022-07-01: $93 (AT 22-28; appears in 2023-01) -# 2023-07-01: $98 (IM 24-05; appears in 2024-01) -# 2024-07-01: $102 (effective date is a placeholder — originating transmittal -# not located; value confirmed by IM 26-04; appears in 2025-01) -# 2025-07-01: $106 (IM 26-04; appears in 2026-01) +# 2009-01-01: $82 (COMAR 07.03.07.04(A)(1); forward-fills until 2022-07-01) +# 2022-07-01: $93 (AT 22-28; transmittal not located, $93 confirmed by AT 23-02) +# 2023-07-01: $98 (IM 24-05; transmittal not located, $98 implied by IM 26-04) +# 2024-07-01: $102 (originating transmittal not located; value confirmed +# by IM 26-04 saying "$102 → $106"; appears in 2025-01) +# 2025-07-01: $106 (IM 26-04 page 2; appears in 2026-01) - name: Case 1, 2023 PNA reflects $93 (post AT 22-28). period: 2023-01 @@ -75,7 +75,23 @@ output: md_paa_personal_needs_allowance: 98 -- name: Case 5, 2022 January PNA is $84 (pre-AT 22-28). +- name: Case 6, 2011 January PNA is $82 (codified in COMAR 07.03.07.04(A)(1)). + period: 2011-01 + absolute_error_margin: 0.01 + input: + people: + person1: + ssi: 9_600 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_B + households: + household: + members: [person1] + state_code: MD + output: + md_paa_personal_needs_allowance: 82 + +- name: Case 5, 2022 January PNA is $82 (forward-filled from COMAR 2009). period: 2022-01 absolute_error_margin: 0.01 input: @@ -89,4 +105,6 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 84 + # Forward-fills from COMAR 07.03.07.04(A)(1) ($82, effective 2009-01-01) + # until AT 22-28 raises it to $93 on 2022-07-01. + md_paa_personal_needs_allowance: 82 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index 77da8dd678c..a96a61beb20 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -110,10 +110,10 @@ output: md_paa_provider_rate: 0 -# Pre-2021 dates use the 2011 cost-of-care derivation (SSA 2011 combined -# need minus the $82 PNA inferred from REHAB combined). Values forward-fill -# from 2011 until the 2021 update from AT 23-02. -- name: Case 8, pre-2021 period uses 2011-derived CARE_HOME_LEVEL_C rate. +# Pre-2021 dates use the COMAR 07.03.07.04(B)(2)/(C)(2) "Effective +# January 1, 2009" schedule. Values forward-fill from 2009 until the +# 2021 update reflected in AT 23-02 page 3. +- name: Case 8, pre-2021 period uses COMAR 2009 CARE_HOME_LEVEL_C rate. period: 2020-01 absolute_error_margin: 0.01 input: @@ -126,9 +126,9 @@ members: [person1] state_code: MD output: - md_paa_provider_rate: 1_055 + md_paa_provider_rate: 1_137 -- name: Case 9, pre-2021 period uses 2011-derived ASSISTED_LIVING rate. +- name: Case 9, pre-2021 period uses COMAR 2009 ASSISTED_LIVING rate. period: 2018-01 absolute_error_margin: 0.01 input: @@ -141,7 +141,7 @@ members: [person1] state_code: MD output: - md_paa_provider_rate: 776 + md_paa_provider_rate: 858 # Pre/post 2021-01-01 transition: the test runner only supports January # (or whole-year) periods, so the rate jump from AT 23-02 is verified by diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index 6d54ca5561e..0b02550f3d6 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -9,21 +9,22 @@ class md_paa(Variable): definition_period = MONTH defined_for = "md_paa_eligible" reference = ( - "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html", - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", "https://regs.maryland.gov/us/md/exec/comar/07.03.07.09", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.04", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + "https://www.ssa.gov/policy/docs/progdesc/ssi_st_asst/2011/md.html", ) def formula(person, period, parameters): - # SSA state-supplementation cascade (SSA 2011 MD Table 1; COMAR - # 07.03.07.09): Maryland PAA equals the gap between combined - # federal+state need and federal SSI plus residual countable - # income. Per SSA 2011 MD Table 1 footnote a, the individual - # state-supp rate applies to each member of a couple, so the - # cascade always uses the federal individual FBR — never - # couple_FBR/2 — with the $30/mo medical-facility cap for REHAB - # residents per 42 USC § 1382(e)(1)(A). Federal SSI is its own - # variable; this formula only computes the state contribution. + # SSA state-supplementation cascade per COMAR 07.03.07.09(A): + # Maryland PAA equals the amount by which allowable needs exceed + # net countable income, with federal SSI absorbing income first. + # COMAR §07.03.07.04(A)(1)/(B)/(C) establishes the rate schedule + # per recipient — couples are evaluated per-individual, so the + # cascade uses the federal individual FBR for each spouse rather + # than couple_FBR/2. The $30/mo medical-facility cap applies to + # REHAB residents per 42 USC § 1382(e)(1)(A). Federal SSI remains + # its own variable; this formula computes only the state share. p_ssi = parameters(period).gov.ssa.ssi.amount arrangement = person("ssi_federal_living_arrangement", period.this_year) is_medical_facility = ( diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index 321390047f5..e970940fd2a 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -13,12 +13,15 @@ class md_paa_eligible(Variable): ) def formula(person, period, parameters): - # PAA Manual §300.1.B / COMAR 07.03.07.03: applicants must be aged, - # blind, or disabled and receive SSI, SSDI, or other federal cash - # benefit on the basis of those criteria. SSI and SSDI receipt - # already implies categorical qualification, so the aged/blind/ - # disabled gate only applies to the broader OASI / survivor and - # the §300.5 pending-application / no-fault-denial pathways. + # COMAR 07.03.07.03(A)(2): applicants must be aged, blind, or + # disabled and receive SSI, SSDI, or other federal cash benefit + # on the basis of those criteria. SSI and SSDI receipt already + # implies categorical qualification, so the aged/blind/disabled + # gate only applies to the broader OASI / survivor and the + # §300.5 / COMAR 07.03.07.03(G)(1) pending-application pathway, + # which under COMAR additionally requires DHS to verify ABD + # status before granting interim PAA — narrower than §300.5 + # standing alone. receives_ssi = person("ssi", period) > 0 receives_ssdi = person("social_security_disability", period) > 0 is_categorically_qualifying = person( @@ -37,10 +40,10 @@ def formula(person, period, parameters): | receives_other_oasdi_qualifying | pending_federal_benefit ) - # PAA Manual §500.2.B: $2,000 resource limit (parity with federal SSI + # PAA Manual §500: $2,000 resource limit (parity with federal SSI # individual limit). Couple cases are evaluated per-person. PAA's # resource base also includes real property, money on hand, trusts, - # transfer-of-asset penalties (§500.4) — those refinements are not + # and transfer-of-asset penalties — those refinements are not # tracked at the moment, so we approximate via ssi_countable_resources. ssi_resource_limit = parameters( period @@ -64,7 +67,16 @@ def formula(person, period, parameters): is_medical_facility = ( federal_la == federal_la.possible_values.MEDICAL_TREATMENT_FACILITY ) - rehab_consistent = ~is_rehab | is_medical_facility + # Bidirectional: REHAB iff SSI medical-facility. A CARE Home recipient + # with the SSI medical-facility flag set would have federal SSI capped + # at $30/mo and produce a wrong PAA cascade; conversely, REHAB without + # the flag set would skip the cap. Either inconsistency makes the + # person ineligible so the inputs are surfaced rather than silently + # producing the wrong amount. + facility_consistent = is_rehab == is_medical_facility return ( - receives_federal_cash & resource_eligible & in_facility & rehab_consistent + receives_federal_cash + & resource_eligible + & in_facility + & facility_consistent ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py index d5ec909c29f..cf70d2748e9 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_living_arrangement.py @@ -2,6 +2,8 @@ class MDPAALivingArrangement(Enum): + # CARE Home is the current term per PAA Manual §300.7.B; AT 23-02 + # page 3 still uses the legacy "Project Home Level *" wording. CARE_HOME_LEVEL_A = "CARE Home Level A" CARE_HOME_LEVEL_B = "CARE Home Level B" CARE_HOME_LEVEL_C = "CARE Home Level C" @@ -20,6 +22,8 @@ class md_paa_living_arrangement(Variable): possible_values = MDPAALivingArrangement default_value = MDPAALivingArrangement.NONE reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.04", "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py index e01483591a3..8492f7fa052 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_pending_federal_benefit.py @@ -9,6 +9,6 @@ class md_paa_pending_federal_benefit(Variable): defined_for = StateCode.MD default_value = False reference = ( - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", "https://regs.maryland.gov/us/md/exec/comar/07.03.07.03", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20300%20Technical%20Eligibility%20rev%2011.22.docx", ) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py index 7803b3df3f7..b484221b307 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.py @@ -9,9 +9,9 @@ class md_paa_personal_needs_allowance(Variable): definition_period = MONTH defined_for = "md_paa_eligible" reference = ( - "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.04", + "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", ) - - def formula(person, period, parameters): - return parameters(period).gov.states.md.dhs.fia.paa.personal_needs_allowance + adds = ["gov.states.md.dhs.fia.paa.personal_needs_allowance"] diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py index efdb34e3783..60d882805e1 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_provider_rate.py @@ -9,8 +9,9 @@ class md_paa_provider_rate(Variable): definition_period = MONTH defined_for = "md_paa_eligible" reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.04", + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.09", "https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3", - "https://www.law.cornell.edu/regulations/maryland/COMAR-07-03-07-09", ) def formula(person, period, parameters): diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py index 2256e2f4a59..c6a49939237 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_total_cost_of_care.py @@ -8,5 +8,8 @@ class md_paa_total_cost_of_care(Variable): unit = USD definition_period = MONTH defined_for = "md_paa_eligible" - reference = "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx" + reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.09", + "https://dhs.maryland.gov/documents/FIA/Manuals/Public%20Assistance%20to%20Adults%20%28PAA%29%20Manual/PAA%20900%20Calculation%20of%20Benefits%20rev%2011.22.docx", + ) adds = ["md_paa_provider_rate", "md_paa_personal_needs_allowance"] From 4ea64e9286c5638200e66803bbcb5ce52fb036f5 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 4 May 2026 00:15:19 -0400 Subject: [PATCH 14/17] Update PAA comments to use COMAR citations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace stale §300.1.B / §500.2.B / SSA 2011 footnote citations in test and variable comments with COMAR 07.03.07.03(A)(2), PAA Manual §500, and COMAR 07.03.07.04 — aligns inline documentation with the parameter file references. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../states/md/dhs/fia/paa/integration.yaml | 26 ++++++++--------- .../md/dhs/fia/paa/md_paa_eligible.yaml | 28 +++++++++++-------- .../states/md/dhs/fia/paa/md_paa_eligible.py | 10 +++---- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 1dfa0570342..9d2e61a57dd 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -191,14 +191,12 @@ state_code: MD output: # Federal SSI uses couple_FBR/2 = $1,491 / 2 = $745.50 per spouse, but - # per SSA 2011 MD Table 1 footnote a ("the state supplementation rate - # for individuals applies to each member of a couple") the PAA cascade + # per COMAR 07.03.07.04 (rate schedule per recipient) the PAA cascade # uses the individual FBR ($994) per spouse — never couple_FBR/2. # combined per person = 776 + 106 = 882; state_supp_max = max(882 - # 994, 0) = 0 per spouse. The couple is left at federal $745.50 + state - # $0 = $745.50 per spouse, which matches the SSA-published state-supp - # treatment of couples (Maryland does not top up to fill the federal - # couple-FBR reduction). + # $0 = $745.50 per spouse — Maryland does not top up to fill the + # federal couple-FBR reduction. ssi_amount_if_eligible: [745.50, 745.50] ssi_countable_income: [0, 0] md_paa_eligible: [true, true] @@ -267,7 +265,7 @@ state_code: MD output: # No federal cash benefit (SSI / SSDI / OASI) → PAA-ineligible per - # PAA Manual §300.1.B. + # COMAR 07.03.07.03(A)(2). md_paa_eligible: [false] md_paa: [0] @@ -395,9 +393,9 @@ members: [person1, person2] state_code: MD output: - # Federal SSI uses couple_FBR/2 = $745.50 per spouse, but per SSA 2011 - # MD Table 1 footnote a the PAA cascade uses the individual FBR ($994) - # per spouse. Per-person cascade: + # Federal SSI uses couple_FBR/2 = $745.50 per spouse, but per COMAR + # 07.03.07.04 (rate schedule per recipient) the PAA cascade uses the + # individual FBR ($994) per spouse. Per-person cascade: # person1 (CARE_HOME_LEVEL_A): combined = 776 + 106 = 882; # state_supp_max = max(882 - 994, 0) = 0. # person2 (CARE_HOME_LEVEL_C): combined = 1173 + 106 = 1279; @@ -439,11 +437,11 @@ members: [person1] state_code: MD output: - # PAA Manual §300.1.B: OASI receipt qualifies a person ONLY if they - # also meet aged/blind/disabled. A 50-year-old surviving spouse is - # not categorically qualifying, so PAA must reject — exercises the - # `(social_security > 0) & is_categorically_qualifying` branch of - # md_paa_eligible separately from Case 7's no-federal-benefit case. + # COMAR 07.03.07.03(A)(2): OASI receipt qualifies a person ONLY if + # they also meet aged/blind/disabled. A 50-year-old surviving spouse + # is not categorically qualifying, so PAA must reject — exercises + # the `(social_security > 0) & is_categorically_qualifying` branch + # of md_paa_eligible separately from Case 7's no-federal-benefit case. md_paa_eligible: [false] md_paa: [0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index ab9c4d6d177..14e06387ffb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -1,9 +1,13 @@ # Tests for md_paa_eligible variable. # Eligibility requires: # - state_code = MD (defined_for filter) -# - ssi > 0 OR social_security_disability > 0 (federal cash benefit per §300.1.B) -# - ssi_countable_resources <= $2,000 (parity with federal SSI per §500.2.B) -# - md_paa_living_arrangement != NONE (§300.3, §300.4) +# - ssi > 0 OR social_security_disability > 0 OR (oasi > 0 AND aged/blind/ +# disabled) OR pending §300.5 application (federal cash benefit per +# COMAR 07.03.07.03(A)(2)) +# - ssi_countable_resources <= $2,000 (parity with federal SSI per +# PAA Manual §500) +# - md_paa_living_arrangement != NONE (PAA Manual §300.3, §300.4) +# - REHAB ⇄ ssi_lives_in_medical_treatment_facility consistency - name: Case 1, SSI recipient in CARE_HOME_LEVEL_B in Maryland is eligible. period: 2025-01 @@ -70,8 +74,8 @@ output: md_paa_eligible: false -# Per PAA Manual §300.1.B, applicants who receive SSDI (without SSI) on the -# basis of disability are also PAA-eligible. +# Per COMAR 07.03.07.03(A)(2), applicants who receive SSDI (without SSI) +# on the basis of disability are also PAA-eligible. - name: Case 5, SSDI-only recipient is eligible. period: 2025-01 input: @@ -89,7 +93,7 @@ output: md_paa_eligible: true -# Per PAA Manual §500.2.B, the $2,000 SSI resource limit applies. Verify +# Per PAA Manual §500, the $2,000 SSI resource limit applies. Verify # the resource gate is enforced independently of SSI receipt. - name: Case 6, resources above $2,000 limit are ineligible. period: 2025-01 @@ -110,7 +114,7 @@ # A person with only earned income (no SSI, SSDI, or OASI receipt) cannot # qualify for PAA, even if otherwise in a PAA facility — PAA requires # receipt of a federal cash benefit on the basis of age/blindness/ -# disability per §300.1.B. +# disability per COMAR 07.03.07.03(A)(2). - name: Case 7, no federal cash benefit receipt is PAA-ineligible. period: 2025-01 input: @@ -190,7 +194,7 @@ output: md_paa_eligible: false -# Resource limit boundary: PAA Manual §500.2.B caps at $2,000 (federal SSI +# Resource limit boundary: PAA Manual §500 caps at $2,000 (federal SSI # individual limit). Confirm the boundary applies inclusively at $2,000 # and excludes $2,001 — guards against off-by-one regressions if the # parameter or comparison operator ever changes. @@ -273,10 +277,10 @@ output: md_paa_eligible: true -# Per PAA Manual §300.1.B, OASI receipt qualifies a person ONLY if they -# are also aged/blind/disabled. A 50-year-old surviving spouse drawing -# Social Security survivor benefits is not categorically qualifying and -# must be rejected, even though `social_security > 0`. +# Per COMAR 07.03.07.03(A)(2), OASI receipt qualifies a person ONLY if +# they are also aged/blind/disabled. A 50-year-old surviving spouse +# drawing Social Security survivor benefits is not categorically +# qualifying and must be rejected, even though `social_security > 0`. - name: Case 14, OASI recipient who is not aged/blind/disabled is ineligible. period: 2025-01 input: diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index e970940fd2a..f9c43ce85bb 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -52,14 +52,12 @@ def formula(person, period, parameters): resource_eligible = ssi_resources <= ssi_resource_limit living_arrangement = person("md_paa_living_arrangement", period) in_facility = living_arrangement != living_arrangement.possible_values.NONE - # MDH Rehabilitative Residence customers are SSI medical-treatment- - # facility residents (federal SSI capped at $30/mo per 42 USC § - # 1382(e)(1)(A); SSA 2011 MD Table 1). The caller must set + # MDH Rehabilitative Residence is a Title XIX medical-treatment- + # facility setting per PAA Manual §900.3, so federal SSI is capped + # at $30/mo per 42 USC § 1382(e)(1)(A). The caller must set # `ssi_lives_in_medical_treatment_facility` and # `ssi_medicaid_pays_majority_of_care` upstream so federal SSI is - # computed correctly. If they don't, treat the person as not in a - # PAA facility — preventing inconsistent state in which federal - # SSI overstates by ~$960/mo. + # computed correctly. is_rehab = ( living_arrangement == living_arrangement.possible_values.REHAB_RESIDENCE ) From b397c27fb76b736430c5b471809abf34e1c1174d Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 4 May 2026 00:21:55 -0400 Subject: [PATCH 15/17] Address second-round PR feedback for MD PAA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - programs.yaml: add `parameter_prefix: gov.states.md.dhs.fia.paa` to the Maryland PAA SSP entry (matches MI / other state SSP entries). - md_paa_eligible.py: subtract SSDI from the OASI branch's `social_security` so the OR-chain doesn't double-count SSDI recipients (SSDI is a component of OASDI). Behavior unchanged (boolean OR is idempotent), but the intent is now explicit. - md_paa_eligible.py: clarify that PAA evaluates each person against the federal SSI individual resource limit independently — the couple limit is not used. - md_paa_personal_needs_allowance.yaml: renumber test cases in chronological order (2011 → 2026) so case numbers match listing order. Co-Authored-By: Claude Opus 4.7 (1M context) --- policyengine_us/programs.yaml | 1 + .../paa/md_paa_personal_needs_allowance.yaml | 42 +++++++++---------- .../states/md/dhs/fia/paa/md_paa_eligible.py | 23 ++++++---- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 1cd65e3fda0..51012596139 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -656,6 +656,7 @@ programs: name: Maryland PAA full_name: Maryland Public Assistance to Adults variable: md_paa + parameter_prefix: gov.states.md.dhs.fia.paa - state: MI status: complete name: Michigan SSP diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index 16c5eccc806..b5d55b394ba 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -9,13 +9,13 @@ # by IM 26-04 saying "$102 → $106"; appears in 2025-01) # 2025-07-01: $106 (IM 26-04 page 2; appears in 2026-01) -- name: Case 1, 2023 PNA reflects $93 (post AT 22-28). - period: 2023-01 +- name: Case 1, 2011 January PNA is $82 (codified in COMAR 07.03.07.04(A)(1)). + period: 2011-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 # gates md_paa_eligible + ssi: 9_600 ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: @@ -23,10 +23,10 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 93 + md_paa_personal_needs_allowance: 82 -- name: Case 2, 2025 January PNA reflects $102 (effective 2024-07-01 placeholder). - period: 2025-01 +- name: Case 2, 2022 January PNA is $82 (forward-filled from COMAR 2009). + period: 2022-01 absolute_error_margin: 0.01 input: people: @@ -39,17 +39,17 @@ members: [person1] state_code: MD output: - # Parameter sets $102 effective 2024-07-01; the originating transmittal - # has not been located, so the date is a placeholder. - md_paa_personal_needs_allowance: 102 + # Forward-fills from COMAR 07.03.07.04(A)(1) ($82, effective 2009-01-01) + # until AT 22-28 raises it to $93 on 2022-07-01. + md_paa_personal_needs_allowance: 82 -- name: Case 3, 2026 January PNA reflects $106 (post IM 26-04). - period: 2026-01 +- name: Case 3, 2023 PNA reflects $93 (post AT 22-28). + period: 2023-01 absolute_error_margin: 0.01 input: people: person1: - ssi: 9_600 + ssi: 9_600 # gates md_paa_eligible ssi_countable_resources: 1_000 md_paa_living_arrangement: CARE_HOME_LEVEL_B households: @@ -57,7 +57,7 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 106 + md_paa_personal_needs_allowance: 93 - name: Case 4, 2024 January PNA reflects $98 (effective 2023-07-01 per IM 24-05). period: 2024-01 @@ -75,8 +75,8 @@ output: md_paa_personal_needs_allowance: 98 -- name: Case 6, 2011 January PNA is $82 (codified in COMAR 07.03.07.04(A)(1)). - period: 2011-01 +- name: Case 5, 2025 January PNA reflects $102 (effective 2024-07-01 placeholder). + period: 2025-01 absolute_error_margin: 0.01 input: people: @@ -89,10 +89,12 @@ members: [person1] state_code: MD output: - md_paa_personal_needs_allowance: 82 + # Parameter sets $102 effective 2024-07-01; the originating transmittal + # has not been located, so the date is a placeholder. + md_paa_personal_needs_allowance: 102 -- name: Case 5, 2022 January PNA is $82 (forward-filled from COMAR 2009). - period: 2022-01 +- name: Case 6, 2026 January PNA reflects $106 (post IM 26-04). + period: 2026-01 absolute_error_margin: 0.01 input: people: @@ -105,6 +107,4 @@ members: [person1] state_code: MD output: - # Forward-fills from COMAR 07.03.07.04(A)(1) ($82, effective 2009-01-01) - # until AT 22-28 raises it to $93 on 2022-07-01. - md_paa_personal_needs_allowance: 82 + md_paa_personal_needs_allowance: 106 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index f9c43ce85bb..3cd5dfcd57d 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -23,12 +23,18 @@ def formula(person, period, parameters): # status before granting interim PAA — narrower than §300.5 # standing alone. receives_ssi = person("ssi", period) > 0 - receives_ssdi = person("social_security_disability", period) > 0 + ssdi = person("social_security_disability", period) + receives_ssdi = ssdi > 0 is_categorically_qualifying = person( "is_ssi_aged_blind_disabled", period.this_year ) + # `social_security` is the OASDI sum (retirement + survivors + + # disability + dependents). Subtract SSDI so the OASI branch only + # fires for non-SSDI Social Security receipt — SSDI already covers + # itself via `receives_ssdi`. + non_ssdi_oasi = person("social_security", period) - ssdi receives_other_oasdi_qualifying = ( - person("social_security", period) > 0 + non_ssdi_oasi > 0 ) & is_categorically_qualifying pending_federal_benefit = ( person("md_paa_pending_federal_benefit", period) @@ -40,11 +46,14 @@ def formula(person, period, parameters): | receives_other_oasdi_qualifying | pending_federal_benefit ) - # PAA Manual §500: $2,000 resource limit (parity with federal SSI - # individual limit). Couple cases are evaluated per-person. PAA's - # resource base also includes real property, money on hand, trusts, - # and transfer-of-asset penalties — those refinements are not - # tracked at the moment, so we approximate via ssi_countable_resources. + # PAA Manual §500: $2,000 resource limit. Each person's resources + # are evaluated against the individual limit independently — the + # couple limit ($3,000) is not used because PAA scopes eligibility + # per recipient (one assistance unit per person), not per couple. + # PAA's resource base also includes real property, money on hand, + # trusts, and transfer-of-asset penalties — those refinements are + # not tracked at the moment, so we approximate via + # ssi_countable_resources. ssi_resource_limit = parameters( period ).gov.ssa.ssi.eligibility.resources.limit.individual From cb9e64799cd0da340fad005ed3e652c02df47949 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 12 May 2026 17:13:11 -0400 Subject: [PATCH 16/17] fix --- .../states/md/dhs/fia/paa/integration.yaml | 79 ++++++++-------- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 39 ++++---- .../fia/paa/md_paa_imputed_federal_ssi.yaml | 89 +++++++++++++++++++ .../gov/states/md/dhs/fia/paa/md_paa.py | 33 ++----- .../dhs/fia/paa/md_paa_imputed_federal_ssi.py | 17 ++++ 5 files changed, 168 insertions(+), 89 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.yaml create mode 100644 policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml index 9d2e61a57dd..00452d06534 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/integration.yaml @@ -1,7 +1,8 @@ # Integration tests for Maryland Public Assistance to Adults (PAA). -# Verifies the SSA cascade method (state SSP = combined need - federal SSI - -# residual countable income). Period 2026-01: federal individual FBR = $994, -# couple FBR = $1,491, medical-facility rate = $30, PNA = $106. +# Verifies the prospective SSI cascade method: PAA equals combined need minus +# SSI countable income and imputed federal SSI. Period 2026-01: federal +# individual FBR = $994, couple FBR = $1,491, medical-facility rate = $30, +# PNA = $106. - name: Case 1, single SSI in CARE_HOME_LEVEL_B — federal SSI exceeds combined need. period: 2026-01 @@ -33,8 +34,8 @@ is_ssi_eligible: [true] ssi: [994] md_paa_eligible: [true] - # combined_need = 875 + 106 = 981. federal_max = 994. - # state_supp_max = max(981 - 994, 0) = 0. PAA = 0. + # combined_need = 875 + 106 = 981. imputed SSI = 994. + # PAA = max(981 - 994, 0) = 0. md_paa_provider_rate: [875] md_paa_personal_needs_allowance: [106] md_paa_total_cost_of_care: [981] @@ -72,8 +73,8 @@ is_ssi_eligible: [true] ssi: [936.50] md_paa_eligible: [true] - # combined_need = 1173 + 106 = 1279. state_supp_max = 1279 - 994 = 285. - # countable = 57.50; income_excess = max(57.50 - 994, 0) = 0. PAA = 285. + # combined_need = 1173 + 106 = 1279. imputed SSI = 936.50. + # countable = 57.50. PAA = 1279 - 936.50 - 57.50 = 285. md_paa_total_cost_of_care: [1_279] md_paa: [285] @@ -109,8 +110,8 @@ is_ssi_eligible: [true] ssi: [614] md_paa_eligible: [true] - # combined = 894 + 106 = 1000. state_supp_max = 1000 - 994 = 6. - # countable = 380; income_excess = max(380 - 994, 0) = 0. PAA = 6. + # combined = 894 + 106 = 1000. imputed SSI = 614. + # countable = 380. PAA = 1000 - 614 - 380 = 6. md_paa_total_cost_of_care: [1_000] md_paa: [6] @@ -149,8 +150,8 @@ # Federal SSI = $30/mo medical-facility rate (annualized $360). ssi: [30] md_paa_eligible: [true] - # combined = 0 + 106 = 106. federal_max = $30. state_supp_max = 76. - # countable = 0; income_excess = 0. PAA = 76. + # combined = 0 + 106 = 106. imputed SSI = $30. + # countable = 0. PAA = 76. md_paa_provider_rate: [0] md_paa_personal_needs_allowance: [106] md_paa: [76] @@ -190,17 +191,14 @@ members: [person1, person2] state_code: MD output: - # Federal SSI uses couple_FBR/2 = $1,491 / 2 = $745.50 per spouse, but - # per COMAR 07.03.07.04 (rate schedule per recipient) the PAA cascade - # uses the individual FBR ($994) per spouse — never couple_FBR/2. - # combined per person = 776 + 106 = 882; state_supp_max = max(882 - - # 994, 0) = 0 per spouse. The couple is left at federal $745.50 + state - # $0 = $745.50 per spouse — Maryland does not top up to fill the - # federal couple-FBR reduction. + # Imputed federal SSI uses couple_FBR/2 = $1,491 / 2 = $745.50 per + # spouse. Combined need per person = 776 + 106 = 882, so Maryland fills + # the $136.50 gap for each spouse. ssi_amount_if_eligible: [745.50, 745.50] ssi_countable_income: [0, 0] + md_paa_imputed_federal_ssi: [745.50, 745.50] md_paa_eligible: [true, true] - md_paa: [0, 0] + md_paa: [136.50, 136.50] - name: Case 6, person not in PAA facility (NONE) is ineligible. period: 2026-01 @@ -299,9 +297,8 @@ state_code: MD output: md_paa_eligible: [true] - # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. - # countable = 1000 - 20 = 980. income_excess = max(980 - 994, 0) = 0. - # PAA = 285 - 0 = 285. + # combined = 1173 + 106 = 1279. countable = 1000 - 20 = 980. + # Imputed SSI = max(994 - 980, 0) = 14. PAA = 1279 - 980 - 14 = 285. md_paa: [285] - name: Case 9, asymmetric married couple — only one spouse PAA-eligible. @@ -344,15 +341,16 @@ # person2 not in PAA facility → ineligible. person1 eligible via SSI. # person1 cascade: combined = 1173 + 106 = 1279. ssi_amount_if_eligible # for an aged spouse paired with a non-ABD spouse falls back to the - # individual FBR ($994). state_supp_max = 1279 - 994 = 285. Own SSI - # receipt is excluded from ssi_countable_income by design, so countable - # = 0 and income_excess = 0. PAA = 285. Locked intermediates below + # individual FBR ($994). Own SSI receipt is excluded from + # ssi_countable_income by design, so countable = 0 and imputed SSI = + # 994. PAA = 1279 - 994 = 285. Locked intermediates below # guard against regressions in either assumption. Note: # ssi_amount_if_eligible returns the maximum federal SSI amount a # person *would* receive if categorically eligible; it does not gate # on receipt, so person2 also returns $994. ssi_amount_if_eligible: [994, 994] ssi_countable_income: [0, 0] + md_paa_imputed_federal_ssi: [994, 0] md_paa_eligible: [true, false] md_paa_provider_rate: [1_173, 0] md_paa_personal_needs_allowance: [106, 0] @@ -393,19 +391,19 @@ members: [person1, person2] state_code: MD output: - # Federal SSI uses couple_FBR/2 = $745.50 per spouse, but per COMAR - # 07.03.07.04 (rate schedule per recipient) the PAA cascade uses the - # individual FBR ($994) per spouse. Per-person cascade: + # Imputed federal SSI uses couple_FBR/2 = $745.50 per spouse. + # Per-person cascade: # person1 (CARE_HOME_LEVEL_A): combined = 776 + 106 = 882; - # state_supp_max = max(882 - 994, 0) = 0. + # PAA = 882 - 745.50 = 136.50. # person2 (CARE_HOME_LEVEL_C): combined = 1173 + 106 = 1279; - # state_supp_max = 1279 - 994 = 285. + # PAA = 1279 - 745.50 = 533.50. # Verifies per-person evaluation produces different PAA amounts within - # one marital unit and that the cascade does not use couple_FBR/2. + # one marital unit. ssi_amount_if_eligible: [745.50, 745.50] ssi_countable_income: [0, 0] + md_paa_imputed_federal_ssi: [745.50, 745.50] md_paa_eligible: [true, true] - md_paa: [0, 285] + md_paa: [136.50, 533.50] - name: Case 11, OASI recipient who is not aged/blind/disabled is PAA-ineligible. period: 2026-01 @@ -476,9 +474,8 @@ state_code: MD output: md_paa_eligible: [true] - # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. - # countable = 1000 - 20 = 980. income_excess = max(980 - 994, 0) = 0. - # PAA = 285. + # combined = 1173 + 106 = 1279. countable = 1000 - 20 = 980. + # Imputed SSI = max(994 - 980, 0) = 14. PAA = 1279 - 980 - 14 = 285. md_paa: [285] - name: Case 13, married couple both SSI-eligible in CARE_HOME_LEVEL_C — symmetric positive output. @@ -516,12 +513,12 @@ members: [person1, person2] state_code: MD output: - # Per-person cascade uses individual FBR ($994), not couple_FBR/2. - # combined per spouse = 1173 + 106 = 1279; state_supp_max = 285 each. - # Both spouses receive a positive PAA — verifies the symmetric path - # (Case 5 covers symmetric zero-output, Case 10 covers asymmetric). + # Imputed federal SSI uses couple_FBR/2 = $745.50 per spouse. + # combined per spouse = 1173 + 106 = 1279, so PAA = 533.50 each. + # Both spouses receive a positive PAA — verifies the symmetric path. md_paa_eligible: [true, true] - md_paa: [285, 285] + md_paa_imputed_federal_ssi: [745.50, 745.50] + md_paa: [533.50, 533.50] - name: Case 14, pending §300.5 applicant with no income in CARE_HOME_LEVEL_C — positive PAA. period: 2026-01 @@ -555,7 +552,7 @@ state_code: MD output: # Pending pathway gates eligibility; cascade then runs normally. - # combined = 1173 + 106 = 1279. federal_max = 994. state_supp_max = 285. + # combined = 1173 + 106 = 1279. imputed SSI = 994. # countable = 0. PAA = 285. md_paa_eligible: [true] md_paa: [285] diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index a1f1507c55e..bce526451b8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -1,8 +1,7 @@ # Tests for md_paa main benefit variable. -# Formula (SSA cascade): paa = max(state_supp_max - income_excess, 0) -# state_supp_max = max(combined_need - federal_ssi_max, 0) -# income_excess = max(ssi_countable_income - federal_ssi_max, 0) -# REHAB residents use $30 federal SSI medical-facility rate. +# Formula (prospective SSI cascade): +# paa = max(combined_need - ssi_countable_income - imputed_federal_ssi, 0) +# REHAB residents impute the $30 federal SSI medical-facility rate. # 2026 federal SSI individual FBR = $994; PNA = $106. - name: Case 1, CARE_HOME_LEVEL_B with only SSI — federal SSI exceeds combined need. @@ -22,8 +21,7 @@ members: [person1] state_code: MD output: - # combined_need = 875 + 106 = 981. federal_max = 994. - # state_supp_max = max(981 - 994, 0) = 0. PAA = 0. + # combined_need = 875 + 106 = 981. imputed SSI = 994. PAA = 0. md_paa: 0 - name: Case 2, REHAB_RESIDENCE with only SSI receives state-funded gap to PNA. @@ -47,8 +45,7 @@ members: [person1] state_code: MD output: - # combined_need = 0 + 106 = 106. federal_max = $30 (medical fac). - # state_supp_max = max(106 - 30, 0) = 76. countable = 0. PAA = 76. + # combined_need = 0 + 106 = 106. imputed SSI = $30. PAA = 76. md_paa: 76 - name: Case 3, ineligible (NONE living arrangement). @@ -85,8 +82,7 @@ members: [person1] state_code: MD output: - # combined_need = 1173 + 106 = 1279. state_supp_max = 1279 - 994 = 285. - # countable = 0, income_excess = 0. PAA = 285. + # combined_need = 1173 + 106 = 1279. imputed SSI = 994. PAA = 285. md_paa: 285 - name: Case 5, negative earned income does not inflate benefit. @@ -107,7 +103,7 @@ state_code: MD output: # ssi_countable_income floors at 0 by design. - # state_supp_max = 1279 - 994 = 285. income_excess = 0. PAA = 285. + # imputed SSI = 994. PAA = 1279 - 994 = 285. md_paa: 285 - name: Case 6, out-of-state recipient is ineligible. @@ -146,9 +142,9 @@ members: [person1] state_code: MD output: - # combined = 106. federal_max = 30. state_supp_max = 76. - # countable = 2500 - 20 = 2480. income_excess = max(2480-30, 0) = 2450. - # PAA = max(76 - 2450, 0) = 0. + # combined = 106. countable = 2500 - 20 = 2480. + # imputed SSI = 0 because countable income exceeds the $30 medical rate. + # PAA = max(106 - 2480, 0) = 0. md_paa: 0 - name: Case 8, CARE_HOME_LEVEL_D recipient gets the largest state supplement. @@ -166,14 +162,11 @@ members: [person1] state_code: MD output: - # combined_need = 1376 + 106 = 1482. state_supp_max = 1482 - 994 = 488. + # combined_need = 1376 + 106 = 1482. imputed SSI = 994. PAA = 488. md_paa: 488 -# Income-excess waterfall: covers the partial-erosion branch where -# `state_supp_max - income_excess > 0` — i.e., countable income exceeds -# the federal max but does not fully exhaust the state supplement. Cases -# 1-8 only exercise full erosion (PAA = 0) or no erosion (income_excess -# = 0); without this case the partial branch is silently untested. +# Partial-erosion case: countable income exceeds the imputed medical-facility +# SSI amount but does not fully exhaust the PNA. - name: Case 9, REHAB recipient with $80/mo unearned — partial state erosion. period: 2026-01 absolute_error_margin: 0.01 @@ -192,9 +185,7 @@ members: [person1] state_code: MD output: - # combined_need = 0 + 106 = 106. federal_max = $30 (medical fac). - # state_supp_max = max(106 - 30, 0) = 76. # countable monthly = 80 - 20 (general exclusion) = 60. - # income_excess = max(60 - 30, 0) = 30. - # PAA = max(76 - 30, 0) = 46. Partial reduction (not zero). + # imputed SSI = max(30 - 60, 0) = 0. + # PAA = max(106 - 60, 0) = 46. Partial reduction (not zero). md_paa: 46 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.yaml new file mode 100644 index 00000000000..9d85205b49d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.yaml @@ -0,0 +1,89 @@ +# Tests for md_paa_imputed_federal_ssi. +# Formula: max(uncapped_ssi, 0), gated by Maryland PAA eligibility. + +- name: Case 1, SSI-eligible CARE Home applicant with no income imputes full individual SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + ssi_countable_income: 0 + md_paa_imputed_federal_ssi: 994 + +- name: Case 2, OASI income reduces imputed federal SSI dollar-for-dollar after disregard. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi: 0 + social_security: 12_000 + social_security_disability: 0 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + # $1,000/mo OASI - $20 general exclusion = $980 countable. + # Imputed federal SSI = max(994 - 980, 0) = 14. + ssi_countable_income: 980 + md_paa_imputed_federal_ssi: 14 + +- name: Case 3, income above the federal SSI amount imputes no federal SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 60 + is_blind: false + is_disabled: true + immigration_status: CITIZEN + ssi: 0 + social_security_disability: 30_000 + ssi_countable_resources: 1_000 + md_paa_living_arrangement: CARE_HOME_LEVEL_C + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: true + md_paa_imputed_federal_ssi: 0 + +- name: Case 4, ineligible applicant has no imputed federal SSI. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_blind: false + is_disabled: false + immigration_status: CITIZEN + ssi_countable_resources: 1_000 + md_paa_living_arrangement: NONE + households: + household: + members: [person1] + state_code: MD + output: + md_paa_eligible: false + md_paa_imputed_federal_ssi: 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index 0b02550f3d6..e7193a065c6 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -16,28 +16,13 @@ class md_paa(Variable): ) def formula(person, period, parameters): - # SSA state-supplementation cascade per COMAR 07.03.07.09(A): - # Maryland PAA equals the amount by which allowable needs exceed - # net countable income, with federal SSI absorbing income first. - # COMAR §07.03.07.04(A)(1)/(B)/(C) establishes the rate schedule - # per recipient — couples are evaluated per-individual, so the - # cascade uses the federal individual FBR for each spouse rather - # than couple_FBR/2. The $30/mo medical-facility cap applies to - # REHAB residents per 42 USC § 1382(e)(1)(A). Federal SSI remains - # its own variable; this formula computes only the state share. - p_ssi = parameters(period).gov.ssa.ssi.amount - arrangement = person("ssi_federal_living_arrangement", period.this_year) - is_medical_facility = ( - arrangement == arrangement.possible_values.MEDICAL_TREATMENT_FACILITY - ) - federal_ssi_max = where( - is_medical_facility, p_ssi.medical_facility, p_ssi.individual - ) - + # Model PAA as a prospective SSI state-supplement cascade: the + # applicant applies for federal SSI, federal SSI is imputed under + # federal rules, and Maryland pays the remaining gap to PAA needs. combined_need = person("md_paa_total_cost_of_care", period) - state_supp_max = max_(combined_need - federal_ssi_max, 0) - - countable = person("ssi_countable_income", period) - income_excess = max_(countable - federal_ssi_max, 0) - - return max_(state_supp_max - income_excess, 0) + available_resources = add( + person, + period, + ["ssi_countable_income", "md_paa_imputed_federal_ssi"], + ) + return max_(combined_need - available_resources, 0) diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py new file mode 100644 index 00000000000..94af9ead313 --- /dev/null +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class md_paa_imputed_federal_ssi(Variable): + value_type = float + entity = Person + label = "Maryland PAA imputed federal SSI" + unit = USD + definition_period = MONTH + defined_for = "md_paa_eligible" + reference = ( + "https://regs.maryland.gov/us/md/exec/comar/07.03.07.03", + "https://www.law.cornell.edu/uscode/text/42/1382", + ) + + def formula(person, period, parameters): + return max_(person("uncapped_ssi", period), 0) From c794f1904318cbb756f8552b2ad4c346c09ce6f8 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 14 May 2026 18:02:07 -0400 Subject: [PATCH 17/17] Address review feedback on Maryland PAA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add missing 2022-01-01 $84 PNA transition (AT 22-09 FFY22 COLA Mass Mod) - Backdate provider rates from 2021-01-01 to 2018-01-01 (AT 18-11) - Expand references: AT 22-09, 22-28, 24-05, 18-11 - Refine COMAR citations to specific subsections (04(D), 09A) - Anchor 42 USC 1382 link to #e_1_A - Document PAA-specific income disregards and §300.5/A3 scoping - Swap test state_code from CA to MA per convention Co-Authored-By: Claude Opus 4.7 (1M context) --- .../dhs/fia/paa/personal_needs_allowance.yaml | 13 +++++--- .../states/md/dhs/fia/paa/provider_rate.yaml | 23 +++++++------- .../gov/states/md/dhs/fia/paa/md_paa.yaml | 2 +- .../md/dhs/fia/paa/md_paa_eligible.yaml | 2 +- .../paa/md_paa_personal_needs_allowance.yaml | 19 ++++++------ .../md/dhs/fia/paa/md_paa_provider_rate.yaml | 30 +++++++++---------- .../gov/states/md/dhs/fia/paa/md_paa.py | 5 ++++ .../states/md/dhs/fia/paa/md_paa_eligible.py | 6 +++- .../dhs/fia/paa/md_paa_imputed_federal_ssi.py | 2 +- 9 files changed, 60 insertions(+), 42 deletions(-) diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml index ea5ec91f6a5..f97760640e9 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/personal_needs_allowance.yaml @@ -2,6 +2,7 @@ description: Maryland provides this amount as the personal needs allowance under values: 2009-01-01: 82 + 2022-01-01: 84 2022-07-01: 93 2023-07-01: 98 2024-07-01: 102 @@ -12,9 +13,13 @@ metadata: period: month label: Maryland PAA personal needs allowance reference: - - title: COMAR 07.03.07.04(A)(1) Need Requirements — Personal Needs Allowance ($82) + - title: COMAR 07.03.07.04(A)(1) Need Requirements href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 SSI/RSDI COLA Mass Mod, PAA Rates and Per Diems table (p.3) - href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 - - title: Maryland DHS-FIA Information Memo 26-04, Increase in PAA Personal Needs Allowance ($102 → $106 effective 2025-07-01) + - title: Maryland DHS-FIA Action Transmittal 22-09, FFY22 COLA Mass Mod + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2022/22-09%20AT-%20COLA%20Mass%20Mod%20FFY22.pdf + - title: Maryland DHS-FIA Action Transmittal 22-28, Increase in Personal Needs Allowance + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2022/22-28%20AT%20-%20INCREASE%20IN%20PERSONAL%20NEEDS%20ALLOWANCE.pdf + - title: Maryland DHS-FIA Information Memo 24-05, June 2023 Mass Modification + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2024/24-05%20IM%20-%20JUNE%202023%20MASS%20MODIFICATION.pdf + - title: Maryland DHS-FIA Information Memo 26-04, 2025 PNA Increase href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2026/26-04%20IM%202025%20PNA%20Increase.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml index 03f8875d70e..8ca82130603 100644 --- a/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml +++ b/policyengine_us/parameters/gov/states/md/dhs/fia/paa/provider_rate.yaml @@ -7,34 +7,37 @@ metadata: breakdown: - md_paa_living_arrangement reference: - - title: COMAR 07.03.07.04(B)(2) and (C)(2) Need Requirements (Effective January 1, 2009) + - title: COMAR 07.03.07.04(B)(2), (C)(2), and (D) Need Requirements href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.04 - - title: COMAR 07.03.07.09 Amount of Grant and Payee + - title: COMAR 07.03.07.09A Amount of Grant href: https://regs.maryland.gov/us/md/exec/comar/07.03.07.09 - - title: Maryland DHS-FIA Action Transmittal 23-02, 2023 SSI/RSDI COLA Mass Mod, PAA Rates and Per Diems table (p.3) + - title: Maryland DHS-FIA Action Transmittal 18-11, 2018 COLA Increase + href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2018/18-11%20AT%20COLA%20Increase%202018.doc + - title: Maryland DHS-FIA Action Transmittal 23-02, FFY23 COLA Mass Mod (PAA rates remain the same) href: https://dhs.maryland.gov/documents/FIA/Action%20Transmittals-AT%20-%20Information%20Memo-IM/AT-IM2023/23-02%20AT%20-%20COLA%20Mass%20Mod%20FFY23.pdf#page=3 # 2009-01-01 values from COMAR 07.03.07.04(B)(2) (CARE Home Levels A-D) and -# (C)(2) (Licensed Assisted Living). 2021-01-01 values from AT 23-02 page 3 -# (still effective per AT 23-02's "no COLA increase for 2023" language). +# (C)(2) (Licensed Assisted Living). 2018-01-01 values first documented in +# AT 18-11 and unchanged through every subsequent COLA Mass Mod (AT 19-08, +# 20-10, 21-18, 22-09, 23-02 — all say "PAA Rates remain the same"). # REHAB_RESIDENCE provider_rate is $0 because PAA Manual §900.3 / AT 23-02 # treat MDH Rehabilitative Residence as PNA-only (the $54/day cost is paid # directly by MHA, not by PAA). Do not "fix" REHAB to 54 * 30. CARE_HOME_LEVEL_A: 2009-01-01: 740 - 2021-01-01: 776 + 2018-01-01: 776 CARE_HOME_LEVEL_B: 2009-01-01: 849 - 2021-01-01: 875 + 2018-01-01: 875 CARE_HOME_LEVEL_C: 2009-01-01: 1_137 - 2021-01-01: 1_173 + 2018-01-01: 1_173 CARE_HOME_LEVEL_D: 2009-01-01: 1_340 - 2021-01-01: 1_376 + 2018-01-01: 1_376 ASSISTED_LIVING: 2009-01-01: 858 - 2021-01-01: 894 + 2018-01-01: 894 REHAB_RESIDENCE: 2009-01-01: 0 NONE: diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml index bce526451b8..6cc5fa1df87 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa.yaml @@ -119,7 +119,7 @@ households: household: members: [person1] - state_code: CA + state_code: MA output: md_paa: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml index 14e06387ffb..e07bb4dfb20 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_eligible.yaml @@ -70,7 +70,7 @@ households: household: members: [person1] - state_code: CA + state_code: MA output: md_paa_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml index b5d55b394ba..2e9346bef14 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_personal_needs_allowance.yaml @@ -2,10 +2,11 @@ # Tests parameter values across the timeline (test runner only supports # January or whole-year periods, so mid-year transitions are checked via # year-over-year January tests): -# 2009-01-01: $82 (COMAR 07.03.07.04(A)(1); forward-fills until 2022-07-01) -# 2022-07-01: $93 (AT 22-28; transmittal not located, $93 confirmed by AT 23-02) -# 2023-07-01: $98 (IM 24-05; transmittal not located, $98 implied by IM 26-04) -# 2024-07-01: $102 (originating transmittal not located; value confirmed +# 2009-01-01: $82 (COMAR 07.03.07.04(A)(1); forward-fills until 2022-01-01) +# 2022-01-01: $84 (AT 22-09 FFY22 COLA Mass Mod) +# 2022-07-01: $93 (AT 22-28) +# 2023-07-01: $98 (IM 24-05 June 2023 Mass Modification; appears in 2024-01) +# 2024-07-01: $102 (originating transmittal not located; value corroborated # by IM 26-04 saying "$102 → $106"; appears in 2025-01) # 2025-07-01: $106 (IM 26-04 page 2; appears in 2026-01) @@ -25,7 +26,7 @@ output: md_paa_personal_needs_allowance: 82 -- name: Case 2, 2022 January PNA is $82 (forward-filled from COMAR 2009). +- name: Case 2, 2022 January PNA is $84 (AT 22-09 FFY22 COLA Mass Mod). period: 2022-01 absolute_error_margin: 0.01 input: @@ -39,9 +40,9 @@ members: [person1] state_code: MD output: - # Forward-fills from COMAR 07.03.07.04(A)(1) ($82, effective 2009-01-01) - # until AT 22-28 raises it to $93 on 2022-07-01. - md_paa_personal_needs_allowance: 82 + # AT 22-09 raised PNA from $82 to $84 effective 2022-01-01, + # then AT 22-28 raised it to $93 on 2022-07-01. + md_paa_personal_needs_allowance: 84 - name: Case 3, 2023 PNA reflects $93 (post AT 22-28). period: 2023-01 @@ -59,7 +60,7 @@ output: md_paa_personal_needs_allowance: 93 -- name: Case 4, 2024 January PNA reflects $98 (effective 2023-07-01 per IM 24-05). +- name: Case 4, 2024 January PNA reflects $98 (effective 2023-07-01 per Information Memo 24-05). period: 2024-01 absolute_error_margin: 0.01 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml index a96a61beb20..9697f753e14 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_provider_rate.yaml @@ -1,7 +1,8 @@ # Tests for md_paa_provider_rate variable. # Rate is determined by md_paa_living_arrangement enum. # Each tier has at least one test (per testing-patterns: every dimension value). -# Rates effective 2021-01-01 onwards (per AT 23-02). +# Current rates effective 2018-01-01 onwards (per AT 18-11; unchanged through +# every subsequent COLA Mass Mod including AT 23-02). - name: Case 1, CARE_HOME_LEVEL_A. period: 2025-01 @@ -110,11 +111,11 @@ output: md_paa_provider_rate: 0 -# Pre-2021 dates use the COMAR 07.03.07.04(B)(2)/(C)(2) "Effective +# Pre-2018 dates use the COMAR 07.03.07.04(B)(2)/(C)(2) "Effective # January 1, 2009" schedule. Values forward-fill from 2009 until the -# 2021 update reflected in AT 23-02 page 3. -- name: Case 8, pre-2021 period uses COMAR 2009 CARE_HOME_LEVEL_C rate. - period: 2020-01 +# 2018 update first documented in AT 18-11. +- name: Case 8, pre-2018 period uses COMAR 2009 CARE_HOME_LEVEL_C rate. + period: 2010-01 absolute_error_margin: 0.01 input: people: @@ -128,8 +129,8 @@ output: md_paa_provider_rate: 1_137 -- name: Case 9, pre-2021 period uses COMAR 2009 ASSISTED_LIVING rate. - period: 2018-01 +- name: Case 9, pre-2018 period uses COMAR 2009 ASSISTED_LIVING rate. + period: 2010-01 absolute_error_margin: 0.01 input: people: @@ -143,12 +144,11 @@ output: md_paa_provider_rate: 858 -# Pre/post 2021-01-01 transition: the test runner only supports January -# (or whole-year) periods, so the rate jump from AT 23-02 is verified by -# pairing 2020-01 (Case 8 above) with 2021-01 below for CARE_HOME_LEVEL_C, -# and 2018-01 (Case 9) with 2021-01 below for ASSISTED_LIVING. -- name: Case 10, 2021-01 CARE_HOME_LEVEL_C jumps to $1,173 (AT 23-02 effective). - period: 2021-01 +# Pre/post 2018-01-01 transition: the test runner only supports January +# (or whole-year) periods, so the rate jump first documented in AT 18-11 +# is verified by pairing 2010-01 (Cases 8 and 9 above) with 2018-01 below. +- name: Case 10, 2018-01 CARE_HOME_LEVEL_C jumps to $1,173 (AT 18-11 effective). + period: 2018-01 absolute_error_margin: 0.01 input: people: @@ -162,8 +162,8 @@ output: md_paa_provider_rate: 1_173 -- name: Case 11, 2021-01 ASSISTED_LIVING jumps to $894 (AT 23-02 effective). - period: 2021-01 +- name: Case 11, 2018-01 ASSISTED_LIVING jumps to $894 (AT 18-11 effective). + period: 2018-01 absolute_error_margin: 0.01 input: people: diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py index e7193a065c6..5497c7d6c46 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa.py @@ -19,6 +19,11 @@ def formula(person, period, parameters): # Model PAA as a prospective SSI state-supplement cascade: the # applicant applies for federal SSI, federal SSI is imputed under # federal rules, and Maryland pays the remaining gap to PAA needs. + # COMAR 07.03.07.08 defines PAA countable income; we approximate it + # with ssi_countable_income because PAA historically mirrors SSI's + # $20 unearned / $65 earned exclusions. PAA-specific income details + # (lump sums, parent contributions, irregular-income carve-outs) + # are not tracked at the moment. combined_need = person("md_paa_total_cost_of_care", period) available_resources = add( person, diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py index 3cd5dfcd57d..67b2dd8ac95 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_eligible.py @@ -21,7 +21,11 @@ def formula(person, period, parameters): # §300.5 / COMAR 07.03.07.03(G)(1) pending-application pathway, # which under COMAR additionally requires DHS to verify ABD # status before granting interim PAA — narrower than §300.5 - # standing alone. + # standing alone. COMAR 07.03.07.03(A)(3) ("would-be SSI-eligible + # except for income") is captured through the same + # md_paa_pending_federal_benefit input — we don't auto-detect + # income-denied SSI applicants at the moment, so the caller must + # set the flag manually. receives_ssi = person("ssi", period) > 0 ssdi = person("social_security_disability", period) receives_ssdi = ssdi > 0 diff --git a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py index 94af9ead313..dac5d85a381 100644 --- a/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py +++ b/policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_imputed_federal_ssi.py @@ -10,7 +10,7 @@ class md_paa_imputed_federal_ssi(Variable): defined_for = "md_paa_eligible" reference = ( "https://regs.maryland.gov/us/md/exec/comar/07.03.07.03", - "https://www.law.cornell.edu/uscode/text/42/1382", + "https://www.law.cornell.edu/uscode/text/42/1382#e_1_A", ) def formula(person, period, parameters):