diff --git a/changelog.d/msp-abd-spouse-income.fixed.md b/changelog.d/msp-abd-spouse-income.fixed.md new file mode 100644 index 00000000000..f51776f4faa --- /dev/null +++ b/changelog.d/msp-abd-spouse-income.fixed.md @@ -0,0 +1 @@ +Apply SSI's eligible-couple and spouse deeming rules to Medicare Savings Program countable income and the poverty guideline when only one spouse is on Medicare, including a spouse who is aged, blind or disabled. diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_countable_income.yaml index fb81c53bf64..8b63696d1bd 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_countable_income.yaml @@ -339,8 +339,9 @@ output: # PM 15-04-03-a: one applicant, so one $25. Deemed: unearned 600 - 25 = 575; # earned (2,500 - 65) / 2 = 1,217.50; 575 + 1,217.50 = 1,792.50. - # The spouse without Medicare: (2,500 - 25 - 65) / 2 = 1,205. - msp_countable_income: [1_792.5, 1_205] + # The spouse without Medicare has person1's $600 deemed the same way, since + # 600 is above the 497 rate difference. + msp_countable_income: [1_792.5, 1_792.5] - name: Case 16, Illinois caps a spouse's 25 dollar exemption at that spouse's income. period: 2026-01 @@ -454,5 +455,415 @@ # SSI methodology deems the spouse's wages whether or not the person # meets SSI's noncitizen test: 20,000 > (1,450 - 967) x 12 = 5,796. # (1,000 - 20) + (1,666.67 - 65) / 2 = 980 + 800.83 = 1,780.83. + # person2 has person1's income deemed the same way: 12,000 > 5,796. is_ssi_spousal_deeming_applies: [true, false] - msp_countable_income: [1_780.83, 790.83] + msp_countable_income: [1_780.83, 1_780.83] + +- name: Case 21, income of a disabled spouse who is not on Medicare counts for the couple. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + output: + # (1,000 + 1,500) - 20 = 2,480, above 135% of the two-person guideline: + # 1.35 x 1,803.33 = 2,434.50. + is_medicare_eligible: [true, false] + ssi_couple_computation_applies: [true, true] + msp_countable_income: [2_480, 2_480] + msp_category: [NONE, NONE] + +- name: Case 22, Illinois gives one exemption when only one spouse is on Medicare. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: IL + output: + # (1,000 + 1,500) - 25 = 2,475. + msp_countable_income: [2_475, 2_475] + +- name: Case 23, a disabled spouse living apart in a facility is not counted. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + output: + # 20 CFR 416.414(b)(3) computes the spouses separately: 1,000 - 20 = 980. + ssi_couple_computation_applies: [false, false] + msp_countable_income: [980, 1_480] + +- name: Case 24, allocations for ineligible children come off the spouse's income before deeming. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 18_000 + person3: + age: 12 + person4: + age: 10 + tax_units: + tax_unit: + members: [person1, person2, person3, person4] + marital_units: + marital_unit: + members: [person1, person2] + child1_marital_unit: + members: [person3] + child2_marital_unit: + members: [person4] + households: + household: + members: [person1, person2, person3, person4] + state_code: TX + output: + # 20 CFR 416.1163(b): two allocations of 1,491 - 994 = 497 total 994. + # person1: 1,500 - 994 = 506, above 497, so it is deemed (416.1163(d)(2)): + # 1,000 + 506 - 20 = 1,486, at or below the two-person 21,640 / 12 = 1,803.33. + # person2: 1,000 - 994 = 6, not above 497, so 1,500 - 20 = 1,480 alone. + msp_countable_income: [1_486, 1_480, 0, 0] + msp_fpg: [1_803.33, 1_330, 1_330, 1_330] + msp_category: [QMB, NONE, NONE, NONE] + +- name: Case 25, a disabled spouse's income at or below the rate difference is not deemed. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 20_400 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 5_400 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # 20 CFR 416.1163(d)(1): person2's 450 is not above 497, so person1 is tested + # alone: 1,700 - 20 = 1,680 against the one-person 15,960 / 12 = 1,330. + # 120% is 1,596 and 135% is 1,795.50, so QI. + # person2 has person1's 1,700 deemed: 450 + 1,700 - 20 = 2,130. + msp_countable_income: [1_680, 2_130] + msp_fpg: [1_330, 1_803.33] + msp_category: [QI, NONE] + +- name: Case 26, an eligible SSI couple is tested together when one spouse is on Medicare. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 16_800 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # 20 CFR 416.1801(c): 1,400 - 20 = 1,380 is below the 1,491 couple rate, so each + # spouse is eligible for SSI: (1,491 - 1,380) / 2 = 55.50. + # 1,380 is at or below the two-person 1,803.33, so QMB. Tested on the one-person + # 1,330 instead, person1 would be SLMB. + uncapped_ssi: [55.50, 55.50] + msp_countable_income: [1_380, 1_380] + msp_fpg: [1_803.33, 1_803.33] + msp_category: [QMB, NONE] + +- name: Case 27, income at or below the rate difference is not deemed from a spouse who is not aged, blind or disabled. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 20_160 + person2: + age: 50 + is_tax_unit_spouse: true + ssi_unearned_income: 5_400 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # 20 CFR 416.1163(d)(1): person2's 450 is not above 497, so person1 is tested + # alone: 1,680 - 20 = 1,660 against the one-person 1,330. + # 120% is 1,596 and 135% is 1,795.50, so QI. The two-person 1,803.33 would be QMB. + # person2 has person1's 1,680 deemed: 450 + 1,680 - 20 = 2,110. + msp_countable_income: [1_660, 2_110] + msp_fpg: [1_330, 1_803.33] + msp_category: [QI, NONE] + +- name: Case 28, an undocumented spouse's income counts whether or not that spouse can enroll in Medicare. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 70 + is_tax_unit_spouse: true + immigration_status: UNDOCUMENTED + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # With both on Medicare the incomes combine. With person2 off Medicare, each + # spouse's income is above 497 and is deemed (20 CFR 416.1163(d)(2)). Either way: + # 1,000 + 1,500 - 20 = 2,480, above 135% of the two-person guideline: + # 1.35 x 1,803.33 = 2,434.50. + msp_countable_income: [2_480, 2_480] + msp_fpg: [1_803.33, 1_803.33] + msp_category: [NONE, NONE] + +- name: Case 29, Illinois gives one 25 dollar exemption to a spouse on Medicare who has no income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 21_900 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: IL + output: + # PM 15-04-03-a: person2's 1,825 is above 497 and is deemed: 1,825 - 25 = 1,800, + # at or below the two-person 1,803.33, so QMB. The SSI 20 would leave 1,805: SLMB. + # person2 is tested alone, since person1 has no income: 1,825 - 25 = 1,800. + msp_countable_income: [1_800, 1_800] + msp_fpg: [1_803.33, 1_330] + msp_category: [QMB, NONE] + +- name: Case 30, a minor's wages are not counted as a spouse's when marital units are omitted. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 20_400 + person2: + age: 16 + employment_income_before_lsr: 10_000 + households: + household: + members: [person1, person2] + state_code: TX + output: + # Omitted marital units put both people in one default unit, but a parent and the + # child they claim are not spouses, so each is tested alone. + # person1: 1,700 - 20 = 1,680 against the one-person 1,330. + # 120% is 1,596 and 135% is 1,795.50, and that Social Security income is too high + # for Texas Medicaid as a parent, so QI. + # person2: (10,000 / 12 - 20 - 65) / 2 = 374.17. + msp_countable_income: [1_680, 374.17] + msp_fpg: [1_330, 1_330] + msp_category: [QI, NONE] + +- name: Case 31, a spouse's income exactly at the rate difference is not deemed. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 20_160 + person2: + age: 50 + is_tax_unit_spouse: true + ssi_unearned_income: 5_964 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # 20 CFR 416.1163(d)(1): person2's 5,964 / 12 = 497 is not above 1,491 - 994 = 497, + # so person1 is tested alone: 1,680 - 20 = 1,660 against the one-person 1,330. + # 120% is 1,596 and 135% is 1,795.50, so QI. + # person2 has person1's 1,680 deemed: 497 + 1,680 - 20 = 2,157. + msp_countable_income: [1_660, 2_157] + msp_fpg: [1_330, 1_803.33] + msp_category: [QI, NONE] + +- name: Case 32, a spouse's income one dollar above the rate difference is deemed. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 20_160 + person2: + age: 50 + is_tax_unit_spouse: true + ssi_unearned_income: 5_976 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # 20 CFR 416.1163(d)(2): person2's 5,976 / 12 = 498 is above 497, so it is deemed: + # 1,680 + 498 - 20 = 2,158 against the two-person 1,803.33. + # 120% is 2,164, so SLMB. + msp_countable_income: [2_158, 2_158] + msp_fpg: [1_803.33, 1_803.33] + msp_category: [SLMB, NONE] + +- name: Case 33, Illinois gives an eligible SSI couple one 25 dollar exemption when one spouse is on Medicare. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 1_200 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_unearned_income: 15_600 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: IL + output: + # 20 CFR 416.1801(c): 100 + 1,300 - 20 = 1,380 is below the 1,491 couple rate, so + # each spouse is eligible for SSI: (1,491 - 1,380) / 2 = 55.50. + # PM 15-04-03-a: only person1 applies, so one $25: 100 + 1,300 - 25 = 1,375. + # A $25 for each spouse would give 1,350. + uncapped_ssi: [55.50, 55.50] + msp_countable_income: [1_375, 1_375] + msp_fpg: [1_803.33, 1_803.33] + msp_category: [QMB, NONE] diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_fpg.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_fpg.yaml index 14acc4c3fb0..0ffd2931b33 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_fpg.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/income/msp_fpg.yaml @@ -12,11 +12,89 @@ # Individual FPL = $15,060/year, monthly = $1,255 msp_fpg: 1_255 -- name: Case 2, married couple gets couple FPL. +- name: Case 2, married couple both on Medicare gets couple FPL. period: 2024-01 absolute_error_margin: 0.01 input: - spm_unit_is_married: true + people: + person1: + age: 68 + person2: + age: 66 + marital_units: + marital_unit: + members: [person1, person2] output: # Couple FPL = $20,440/year, monthly = $1,703.33 - msp_fpg: 1_703.33 + msp_fpg: [1_703.33, 1_703.33] + +- name: Case 3, spouses separated by a facility each keep the one-person guideline. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_lives_in_medical_treatment_facility: true + ssi_medicaid_pays_majority_of_care: true + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + output: + # 20 CFR 416.414(b)(3) computes the spouses separately, though each one's income + # is above the 497 rate difference. One-person guideline: 15,960 / 12 = 1,330. + msp_fpg: [1_330, 1_330] + +- name: Case 4, the guideline and income test change in the month a spouse enters a facility. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 68 + is_tax_unit_head: true + ssi_unearned_income: 12_000 + person2: + age: 62 + is_tax_unit_spouse: true + meets_ssi_disability_criteria: true + ssi_lives_in_medical_treatment_facility: + 2026-07: true + ssi_medicaid_pays_majority_of_care: + 2026-07: true + ssi_unearned_income: 18_000 + tax_units: + tax_unit: + members: [person1, person2] + marital_units: + marital_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: TX + output: + # June, both at home: each spouse's income is above 497 and is deemed: + # 1,000 + 1,500 - 20 = 2,480, above 1.35 x 21,640 / 12 = 2,434.50. + # July, person2 in a facility (20 CFR 416.414(b)(3)): each is tested alone. + # person1: 1,000 - 20 = 980, at or below 15,960 / 12 = 1,330, so QMB. + # person2: 1,500 - 20 = 1,480, and not on Medicare. + msp_fpg: + 2026-06: [1_803.33, 1_803.33] + 2026-07: [1_330, 1_330] + msp_countable_income: + 2026-06: [2_480, 2_480] + 2026-07: [980, 1_480] + msp_category: + 2026-06: [NONE, NONE] + 2026-07: [QMB, NONE] diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/integration.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/integration.yaml index c177b3f7b3a..fc59c8d7f50 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/savings_programs/integration.yaml @@ -1,5 +1,6 @@ # MSP uses SSI income methodology per 42 U.S.C. 1396d(p)(1)(B) -# For married couples, MSP uses couple FPL = $20,440 for 2024 +# A married person tested as a couple (msp_tests_as_couple) uses the two-person +# FPL = $20,440 for 2024; a married person tested alone uses the one-person FPL. - name: MSP integration - married couple, both QMB eligible period: 2024-01 @@ -111,8 +112,8 @@ # Edge case: only one spouse is Medicare-eligible. # person.marital_unit.sum(is_medicare_eligible) == 1, not 2, so full couple # aggregation does not apply. With no spouse income to deem, spouse1 is - # tested on their own income ($10,000) against the married MSP threshold. - # ($10,000 - $240) / 12 = $813.33/month < $1,703.33 -> QMB. + # tested on their own income ($10,000) against the one-person guideline. + # ($10,000 - $240) / 12 = $813.33/month < $1,255 -> QMB. # Spouse2 is not Medicare-eligible and cannot qualify for MSP. input: people: diff --git a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/_msp_spouse_income.py b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/_msp_spouse_income.py new file mode 100644 index 00000000000..d599a79bc19 --- /dev/null +++ b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/_msp_spouse_income.py @@ -0,0 +1,42 @@ +from policyengine_us.model_api import * + + +def msp_spouse_value(values: ArrayLike, spouse: ArrayLike) -> ArrayLike: + # Each person's spouse's value, or zero for a person with no spouse. The + # spouse is the row from _ssi_spouse_index, not the rest of the marital unit, + # because a situation that omits marital units puts everyone in one. + return values[np.maximum(spouse, 0)] * (spouse >= 0) + + +def msp_spouses_combine_income( + person: Population, period: Period, spouse: ArrayLike +) -> ArrayLike: + # Spouses who are both on Medicare are tested together. So is an eligible + # couple: each spouse must be eligible for SSI (20 CFR 416.1801(c); POMS SI + # 00501.154 A.1), not only aged, blind or disabled, and uncapped_ssi above + # zero adds the income test that is_ssi_eligible leaves out. + is_medicare_eligible = person("is_medicare_eligible", period.this_year) + both_on_medicare = is_medicare_eligible & msp_spouse_value( + is_medicare_eligible, spouse + ) + eligible_for_ssi = person("uncapped_ssi", period) > 0 + eligible_couple = ( + person("ssi_couple_computation_applies", period) + & eligible_for_ssi + & msp_spouse_value(eligible_for_ssi, spouse) + ) + return both_on_medicare | eligible_couple + + +def msp_spouse_income(person: Population, year: Period, spouse: ArrayLike) -> tuple: + # A spouse's annual earned and unearned income after allocations for + # ineligible children, which come off unearned income first (20 CFR + # 416.1163(a) and (b)). Any spouse who is not eligible for SSI is an + # ineligible spouse, whether or not aged, blind or disabled (20 CFR + # 416.1160(d)). + spouse_earned = msp_spouse_value(person("ssi_earned_income", year), spouse) + spouse_unearned = msp_spouse_value(person("ssi_unearned_income", year), spouse) + allocations = add(person.tax_unit, year, ["ssi_ineligible_child_allocation"]) + earned_left = max_(spouse_earned - max_(allocations - spouse_unearned, 0), 0) + unearned_left = max_(spouse_unearned - allocations, 0) + return earned_left, unearned_left diff --git a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py index 99b10f12989..196ced94d51 100644 --- a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py +++ b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py @@ -1,7 +1,15 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.medicare.savings_programs.income._msp_spouse_income import ( + msp_spouse_income, + msp_spouse_value, + msp_spouses_combine_income, +) from policyengine_us.variables.gov.ssa.ssi.eligibility.income._apply_ssi_exclusions import ( _apply_ssi_exclusions, ) +from policyengine_us.variables.gov.ssa.ssi.eligibility.resources.deemed._ssi_spouses import ( + _ssi_spouse_index, +) class msp_countable_income(Variable): @@ -12,7 +20,12 @@ class msp_countable_income(Variable): definition_period = MONTH reference = ( "https://www.law.cornell.edu/uscode/text/42/1396d#p", + "https://www.law.cornell.edu/cfr/text/20/416.412", + "https://www.law.cornell.edu/cfr/text/20/416.414", + "https://www.law.cornell.edu/cfr/text/20/416.1160", "https://www.law.cornell.edu/cfr/text/20/416.1163", + "https://www.law.cornell.edu/cfr/text/20/416.1801", + "https://secure.ssa.gov/poms.nsf/lnx/0500501154", "https://secure.ssa.gov/apps10/poms.nsf/lnx/0501715010", "https://www.medicare.gov/basics/costs/help/medicare-savings-programs", "https://www.law.cornell.edu/uscode/text/42/1396a#r_2", @@ -27,27 +40,40 @@ class msp_countable_income(Variable): 2. $65 earned income exclusion 3. 50% of remaining earned income excluded - For married couples where both spouses are Medicare-eligible, SSI couple - rules apply per 20 CFR 416.1160: both spouses' incomes are combined before - exclusions are applied, and the $20 exclusion is taken once on the combined - income. The combined countable income is then compared against the couple FPL - in the eligibility variables (is_qmb_eligible, is_slmb_eligible, is_qi_eligible). + For married couples where both spouses are Medicare-eligible, or who are an + eligible couple because each is eligible for SSI (20 CFR 416.412 and + 416.1801(c)), both spouses' incomes are combined before exclusions are + applied, and the $20 exclusion is taken once on the combined income. + + Any other spouse is an ineligible spouse under 20 CFR 416.1160(d), even one + who is aged, blind or disabled. Under 20 CFR 416.1163, allocations for + ineligible children come off that spouse's income. A remainder above the + difference between the couple and individual benefit rates is added to the + person's own income before exclusions, and otherwise nothing is deemed. A + medical treatment facility that separates a couple leaves each with their own + income (20 CFR 416.414(b)(3)). The eligibility variables (is_qmb_eligible, + is_slmb_eligible, is_qi_eligible) compare the result with msp_fpg, which is the + couple FPL whenever msp_tests_as_couple is true. Under 42 U.S.C. 1396a(r)(2) a state may exclude more than the $20 SSI general income exclusion; Illinois and Mississippi use their larger exclusion in its place. Mississippi takes it once from a couple's combined income; Illinois gives each applying spouse with income their - own exclusion, up to that spouse's income. Illinois gives its $25 only on - income other than SSI and contributions (alimony, child support, outside - cash help); the $20 SSI exclusion stays the floor. + own exclusion, up to that spouse's income, and one exclusion when only one + spouse is on Medicare. Illinois gives its $25 only on income other than SSI + and contributions (alimony, child support, outside cash help); the $20 SSI + exclusion stays the floor. """ def formula(person, period, parameters): year = period.this_year earned_income = person("ssi_earned_income", year) unearned_income = person("ssi_unearned_income", year) + spouse = _ssi_spouse_index(person, year) is_medicare_eligible = person("is_medicare_eligible", year) - both_medicare_eligible = person.marital_unit.sum(is_medicare_eligible) == 2 + both_medicare_eligible = is_medicare_eligible & msp_spouse_value( + is_medicare_eligible, spouse + ) blind_or_disabled_working_student_exclusion = person( "ssi_blind_or_disabled_working_student_exclusion", year @@ -56,7 +82,6 @@ def formula(person, period, parameters): earned_income - blind_or_disabled_working_student_exclusion, 0, ) - deeming_applies = person("is_ssi_spousal_deeming_applies", year) state = person.household("state_code_str", period) p = parameters(period).gov.hhs.medicare.savings_programs.eligibility.income ssi_general = parameters(period).gov.ssa.ssi.income.exclusions.general @@ -86,21 +111,22 @@ def formula(person, period, parameters): max_(ssi_general, min_(state_exclusion, qualifying_income)), general_exclusion, ) + # Two spouses on Medicare each apply. When only one does, PM 15-04-03-a + # allows one $25 on income in the standard, even if that spouse has no + # income other than SSI or contributions. per_spouse = p.state_general_exclusion_per_spouse[state].astype(bool) + own_exemption = min_(state_exclusion, qualifying_income) couple_general_exclusion = where( - per_spouse, + per_spouse & both_medicare_eligible, max_( ssi_general, - person.marital_unit.sum(min_(state_exclusion, qualifying_income)), + own_exemption + msp_spouse_value(own_exemption, spouse), ), general_exclusion, ) - spouse_earned_income = person( - "ssi_earned_income_deemed_from_ineligible_spouse", year - ) - spouse_unearned_income = person( - "ssi_unearned_income_deemed_from_ineligible_spouse", year + spouse_earned_income, spouse_unearned_income = msp_spouse_income( + person, year, spouse ) personal_countable_income = _apply_ssi_exclusions( personal_earned_income, @@ -112,11 +138,13 @@ def formula(person, period, parameters): couple_countable = _apply_ssi_exclusions( max_( - person.marital_unit.sum(earned_income) - - person.marital_unit.sum(blind_or_disabled_working_student_exclusion), + earned_income + + msp_spouse_value(earned_income, spouse) + - blind_or_disabled_working_student_exclusion + - msp_spouse_value(blind_or_disabled_working_student_exclusion, spouse), 0, ), - person.marital_unit.sum(unearned_income), + unearned_income + msp_spouse_value(unearned_income, spouse), parameters, year, general_exclusion=couple_general_exclusion, @@ -131,10 +159,14 @@ def formula(person, period, parameters): ) single_countable = where( - deeming_applies, deemed_countable, personal_countable_income + person("msp_tests_as_couple", period), + deemed_countable, + personal_countable_income, ) annual_countable = where( - both_medicare_eligible, couple_countable, single_countable + msp_spouses_combine_income(person, period, spouse), + couple_countable, + single_countable, ) return annual_countable / MONTHS_IN_YEAR diff --git a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_fpg.py b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_fpg.py index b1cb24efc20..be47fb61f66 100644 --- a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_fpg.py +++ b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_fpg.py @@ -6,16 +6,18 @@ class msp_fpg(Variable): entity = Person unit = USD label = "MSP Federal Poverty Guideline" - definition_period = YEAR + definition_period = MONTH reference = ( "https://www.law.cornell.edu/uscode/text/42/1396d#p", + "https://www.law.cornell.edu/cfr/text/20/416.1163", "https://www.medicare.gov/basics/costs/help/medicare-savings-programs", ) documentation = """ - The Federal Poverty Guideline used for MSP eligibility is based on + The monthly Federal Poverty Guideline used for MSP eligibility is based on individual or couple status, not the full household/SPM unit size. Per 42 U.S.C. 1396d(p), MSP income limits are based on the federal - poverty line for a family of the size involved. + poverty line for a family of the size involved. The family is two people + when msp_tests_as_couple is true, and one otherwise. Note: Published Medicare.gov limits are $20/month higher than raw FPL because they include the $20 general income exclusion in the threshold. @@ -29,12 +31,11 @@ def formula(person, period, parameters): # Note: These are raw FPL values. Published limits are $20 higher # because we apply the $20 exclusion to income (msp_countable_income) # rather than adding it to the threshold. - married = person.spm_unit("spm_unit_is_married", period.this_year) + tests_as_couple = person("msp_tests_as_couple", period) state_group = person.household("state_group_str", period) p = parameters(period).gov.hhs.fpg p1 = p.first_person[state_group] pn = p.additional_person[state_group] - # 1 person if single, 2 persons if married individual_fpg = p1 couple_fpg = p1 + pn - return where(married, couple_fpg, individual_fpg) + return where(tests_as_couple, couple_fpg, individual_fpg) / MONTHS_IN_YEAR diff --git a/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_tests_as_couple.py b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_tests_as_couple.py new file mode 100644 index 00000000000..e5c72cec3a1 --- /dev/null +++ b/policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_tests_as_couple.py @@ -0,0 +1,48 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.medicare.savings_programs.income._msp_spouse_income import ( + msp_spouse_income, + msp_spouses_combine_income, +) +from policyengine_us.variables.gov.ssa.ssi.eligibility.resources.deemed._ssi_spouses import ( + _ssi_spouse_index, +) + + +class msp_tests_as_couple(Variable): + value_type = bool + entity = Person + label = "Medicare Savings Program tests this person as a couple" + definition_period = MONTH + reference = ( + "https://www.law.cornell.edu/uscode/text/42/1396d#p", + "https://www.law.cornell.edu/cfr/text/20/416.414", + "https://www.law.cornell.edu/cfr/text/20/416.1160", + "https://www.law.cornell.edu/cfr/text/20/416.1163", + "https://www.law.cornell.edu/cfr/text/20/416.1801", + "https://secure.ssa.gov/poms.nsf/lnx/0500501154", + ) + documentation = """ + Whether SSI's spouse rules count a spouse's income for this person, which also + makes the two-person poverty guideline the income limit. That holds when both + spouses are on Medicare, when they are an eligible couple, or when the spouse's + income after allocations for ineligible children is more than the difference + between the couple and individual benefit rates (20 CFR 416.1163(d)(2)). If it + is not more, nothing is deemed (416.1163(d)(1)). A medical treatment facility + that separates a couple also leaves each spouse tested alone (416.414(b)(3)), + unless both are on Medicare, when the model still tests them together. A person + with no co-resident spouse (_ssi_spouse_index) is always tested alone. + """ + + def formula(person, period, parameters): + year = period.this_year + spouse = _ssi_spouse_index(person, year) + spouse_earned, spouse_unearned = msp_spouse_income(person, year, spouse) + p = parameters(period).gov.ssa.ssi.amount + rate_difference = (p.couple - p.individual) * MONTHS_IN_YEAR + spouse_income_is_deemed = spouse_earned + spouse_unearned > rate_difference + split_by_facility = person("ssi_claim_is_joint", year) & ~person( + "ssi_couple_computation_applies", period + ) + return msp_spouses_combine_income(person, period, spouse) | ( + spouse_income_is_deemed & ~split_by_facility + ) diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml index 02d04ed6e37..41303e1c9e6 100644 --- a/source_conflicts/candidates.yaml +++ b/source_conflicts/candidates.yaml @@ -2779,6 +2779,43 @@ candidates: state: ruled note: Ruled statute-first in October 2026; verify the incorporated dependency bar and annual questionnaire routing, including the 2026 draft. +- id: us-il:msp-exemption-applicant-without-income + program: medicare-savings-programs + years: + from: 2026 + type: calendar + question: Does an Illinois Medicare Savings Program applicant with no income of their own get the $25 exemption on + income counted from a spouse who is not applying? + sources: + - kind: manual + citation: IDHS Policy Manual PM 15-04-03-a + url: https://www.dhs.state.il.us/page.aspx?item=16387 + - kind: regulation + citation: 89 Ill. Adm. Code 120.335(a)(1) + url: https://www.ilga.gov/ftp/JCAR/AdminCode/089/089001200H03350R.html + - kind: manual + citation: IDHS Policy Manual PM 06-12-01-c + url: https://www.dhs.state.il.us/page.aspx?item=14179 + readings: + statute: 'No statute is supplied; 120.335(a)(1) exempts "the first $25.00 of a client''s earned or unearned income + other than SSI income, or contributions from a spouse or other individual", and PM 06-12-01-c allows "no more + than one income exemption for their own income" and an exemption on a spouse''s income only if the spouse "is + an applicant or client in another MANG case".' + other: 'PM 15-04-03-a: "If the person applying for or receiving AABD Medical has no income other than SSI or + contributions, and another person included in the standard has income, deduct one $25 exemption even if the + other person receives benefits under one of the Family Health Plans."' + example: An Illinois applicant, 68, with no income and a disabled spouse, 62, with $1,825 a month counts $1,800 and + is QMB with one $25, and counts $1,805 and is SLMB with only the SSI $20. + pe_us: + follows: conflicting-source + code: + - policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10633 + state: ruled + note: 'PE-US follows PM 15-04-03-a: one $25 applies when only one spouse is on Medicare, whether or not that spouse + has income of their own. Two spouses on Medicare keep one exemption each, capped at each spouse''s own income. Ruled + by the project lead, 2026-11-02: https://github.com/PolicyEngine/policyengine-us/pull/10633' - id: us-il:msp-income-exemption-contributions program: medicare-savings-programs years: @@ -10580,6 +10617,52 @@ candidates: three places, and floors the result at the federal deduction, a PE safeguard that binds only when the rounded share falls below the exact federal share. Settling the federal reading (rulespec-us#1413) and using it for both would make that floor unnecessary.' +- id: us:msp-eligible-spouse-ssi-definition + program: medicare-savings-programs + years: + from: 2026 + type: calendar + question: Under SSI methodology for the Medicare Savings Programs, is a spouse who is aged, blind or disabled but not + eligible for SSI an eligible spouse, whose income is combined, or an ineligible spouse, whose income is deemed? + sources: + - kind: statute + citation: 42 U.S.C. 1382c(b) and (f)(1) + url: https://www.law.cornell.edu/uscode/text/42/1382c + - kind: regulation + citation: 20 CFR 416.1801(c) + url: https://www.law.cornell.edu/cfr/text/20/416.1801 + - kind: regulation + citation: 20 CFR 416.1160(d) + url: https://www.law.cornell.edu/cfr/text/20/416.1160 + - kind: regulation + citation: 20 CFR 416.1163(b) and (d) + url: https://www.law.cornell.edu/cfr/text/20/416.1163 + - kind: manual + citation: POMS SI 00501.154 A.1 + url: https://secure.ssa.gov/poms.nsf/lnx/0500501154 + readings: + statute: 'Section 1382c(b) defines an eligible spouse as "an aged, blind, or disabled individual" married to and + living with another such individual, and (f)(1) deems income from a spouse who "is not an eligible spouse".' + other: '416.1801(c) defines an eligible spouse as a person "Who is eligible for SSI"; 416.1160(d) defines an + ineligible spouse as someone who "is not eligible for SSI benefits"; POMS SI 00501.154 A.1 applies couple + computation rules if "each is eligible for benefits". 416.1163 then allocates to ineligible children and deems + only a remainder above the rate difference.' + example: A, on Medicare, has $1,000 a month and B, 62 and disabled, has $1,500, with two ineligible children. Combined + income is $2,480 and no tier; under deeming it is $1,000 + ($1,500 - 2 x $497) - $20 = $1,486 and QMB. + pe_us: + follows: conflicting-source + code: + - policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_tests_as_couple.py + - policyengine_us/variables/gov/hhs/medicare/savings_programs/income/_msp_spouse_income.py + - policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_countable_income.py + - policyengine_us/variables/gov/hhs/medicare/savings_programs/income/msp_fpg.py + prs: + - https://github.com/PolicyEngine/policyengine-us/pull/10633 + state: ruled + note: 'PE-US follows the regulations for the Medicare Savings Programs: spouses are an eligible couple only if each + is eligible for SSI, and any other spouse is an ineligible spouse, with income deemed above the rate difference. + The SSI variables (is_ssi_ineligible_spouse, ssi_couple_computation_applies) still classify by aged, blind or + disabled status. Ruled by the project lead, 2026-11-02: https://github.com/PolicyEngine/policyengine-us/pull/10633' - id: us:niit-excess-business-loss-addback program: income-tax years: