Skip to content

Update Medicaid aged, blind and disabled pathway disregards and asset limits (CA, CT, MO, AR, OK, SC) - #10364

Open
hua7450 wants to merge 11 commits into
PolicyEngine:mainfrom
hua7450:medicaid-abd-pathway-values
Open

hua7450 wants to merge 11 commits into
PolicyEngine:mainfrom
hua7450:medicaid-abd-pathway-values

Conversation

@hua7450

@hua7450 hua7450 commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #10356

What and why

The optional Medicaid pathway for aged, blind and disabled people (is_optional_senior_or_disabled_for_medicaid) used outdated disregards and asset limits in six states. California still applied disregards the state removed in 2020, and the other five states had values that were never updated.

State Parameter Before After Source
CA Income disregard $230 / $310 in place of the $20 deduction Before 2020-12-01: the $20 deduction, then the $230 / $310 disregard after the SSI exclusions. From 2020-12-01: the $20 deduction only ACWDL 00-57, p1 (the $230 and $310 disregards), p3 (MC 176 line 8 "SUBTRACT $20 (Any Income Deduction)", lines 10-14 earned income, line 16 the disregard); ACWDL 20-24, p2 ("Counties shall no longer allow the standard disregard amounts of $230.00 for an individual and $310.00 for a couple"), p3 (the $20 any-income deduction remains)
CA Premium deduction None Health insurance premiums the person pays, from 2015 (the program deducted them from its start in 2001). From 2020-12-01, the full base Part B premium whoever pays it. Summed for a couple 22 CCR 50555.2 (a), (c) (premiums the person pays; Part B only in months the person pays it); ACWDL 00-57, MC 176 line 17 "HEALTH INSURANCE PREMIUMS"; ACWDL 20-18, p1-2 (Part B disregard from December 1, 2020); ACWDL 20-24, p3 (deductions that "will continue to apply" include health insurance premiums; Part B is deducted "regardless of how the premium is paid"), p4-8 (Examples 1-5)
CT Unearned income disregard $535 / $1,070 (2025) $562 / $1,124 from 2026-01-01 DSS Program Standards Chart as of 1/1/2026 ("Unearned Income $562"); UPM 5030.15 (raised "each January 1st"); couple amount from the CHOICES guide
SC Income disregard $20 / $20 $50 / $50 MPPM 303.02.01 and 303.06.03C ("Apply $50 general disregard (given only once in couple cases)"); State Plan Supplement 8a to Att. 2.6-A, effective 2009-01-01
MN Income disregard $20 / $20 $0 / $0 from 2015: Minnesota folds the $20 into its standard Minn. Stat. 256B.056, subd. 1b ("The $20 general income disregard allowed under the Supplemental Security Income program is included in the standard and shall not be allowed as a deduction from income for a person eligible under section 256B.055, subdivisions 7, 7a, and 12"; new in 2001)
MO Asset limit $3,000 / $6,000 from 2015; $5,726 / $11,452 from 2024-01-01 $1,000 / $2,000 from 2015; $2,000 / $4,000, $3,000 / $6,000, $4,000 / $8,000 and $5,000 / $10,000 on each July 1 from 2017 to 2020; $5,035 / $10,070 from 2021-07-01; $5,301.85 / $10,603.70 from 2022-07-01; $5,726 / $11,452 from 2023-07-01; $5,909.25 / $11,818.45 from 2024-07-01; $6,068.80 / $12,137.55 from 2025-07-01; $6,220.50 / $12,441 from 2026-07-01 13 CSR 40-8.020(4)(A)-(C) (2015-2020 steps; CPI increase each July from 2021, rounded to five cents); RSMo 208.010.2(7); IM-70 (2021), IM-77 (2022), IM-57 (2023); MHABD Appendix J (10/2026 edition) and archived 10/2022, 07/2024 and 07-2025 editions
CA Asset limit $2,000 / $3,000 until 2023, strict < $130,000 / $195,000 from 2022-07-01 (none in 2024–2025, reinstated 2026); resources equal to the limit qualify ACWDL 21-31, pp. 2-4; WIC 14005.40(c)(5) ("do not exceed the maximum levels"); 22 CCR 50420(a) ("The property reserve shall not exceed the following limits"); 42 U.S.C. 1396a(m)(1)(C)
AR Asset limit $9,430 / $14,130 from 2024 The Medicare Savings Program (MSP) resource limit ($9,950 / $14,910 in 2026) Ark. Code R. 016.20.10-001, MS 23110 ("ARSeniors, QMBs, SMBs or QI-1s can have resources equal to but not exceeding three times the SSI resource standard"); 2026 Medical Quick Reference Chart
OK Asset limit $2,000 / $3,000, then $9,430 / $14,130 from 2024 The MSP resource limit Archived OKDHS Appendix C-1 editions of 7/1/2026, Schedule VI ("full benefit Medicare Part D low income subsidy resource standards", $9,950 / $14,910), 12/1/2015 ($7,280 / $10,930) and 4/1/2023 ($9,090 / $13,630), each showing that year's MSP figure; OAC 317:35-7-40(a)(1), 2001 text
SC Asset limit $9,430 / $14,130 from 2024 The MSP resource limit State Plan Att. 2.6-A, p21a ("Same as QMB resource standards"); MPPM 103.14 ($9,950 / $14,910, effective 01/01/26)

How the model applies them

  • New by-state flag senior_or_disabled.assets.limit.uses_msp_limit, true for AR, OK and SC from 2015-01-01, the first date in the asset files. is_optional_senior_or_disabled_asset_eligible reads gov.hhs.medicare.savings_programs.eligibility.asset.{individual,couple} for those states. Their copied dollar entries are removed from the asset files, so they will not go stale again.
  • Resources equal to the MSP limit qualify in AR, OK and SC (<=), as in MS 23110(6), C-1 Schedule VI and MPPM 303.06.02, and as in msp_asset_eligible.
  • A Missouri couple whose resources equal the couple limit also qualifies, following RSMo 208.010.2(7): a married couple "may own resources not to exceed" the couple amount. Missouri individuals keep the strict test (<), the agency's reading in 13 CSR 40-8.020(4)(B).
  • California individuals and couples whose resources equal the limit qualify (WIC 14005.40(c)(5); 22 CCR 50420(a)).
  • Two new by-state flags, senior_or_disabled.assets.limit.individual_limit_inclusive (CA) and couple_limit_inclusive (CA, MO), switch the comparison to <=. Other states keep <; issue Medicaid aged, blind and disabled pathway: let resources equal the state asset limit unless a state source says otherwise #10466 tracks checking them, since the federal rule (1396a(m)(1)(C)) reads "do not exceed".
  • The asset test compares the marital unit's resources in whole cents. Inputs are stored as float32, so without rounding a Missouri couple at exactly $12,137.55 read as $12,137.5498.
  • For California, the senior_or_disabled.income.disregard value is now the $20 deduction in every year. The new senior_or_disabled.income.additional_disregard.{individual,couple} ($230 / $310 for CA until 2020-12-01) comes off after the SSI exclusions, as on MC 176 line 16. The unused part of the $20 still moves to earned income before the half, as on line 12.
  • Two by-state premium flags:
    • senior_or_disabled.income.deducts_health_insurance_premiums is true for CA from 2015. It deducts the premiums the person pays, from the shared medical_expense_health_insurance_premiums aggregate. That aggregate covers the direct health_insurance_premiums input or the decomposed inputs, and Part B net of MSP coverage.
    • senior_or_disabled.income.disregards_medicare_part_b_premium is true for CA from 2020-12-01. It swaps the person-paid Part B in that aggregate for the full base_part_b_premium, so an MSP buy-in does not remove the deduction and a self-paid premium is not counted twice.
  • The premiums and the additional disregard come off after any deemed spouse income is added, and the result is floored at zero. ACWDL 20-24 Examples 2 and 5 combine the spouses' income before the deductions, as 20 CFR 416.1163(d)(2) does.
  • California's 138% FPL income limit is re-dated from 2021-01-01 to 2020-12-01, the date in ACWDL 20-24. Annual results do not change.
  • The other changes are dated values in the existing parameters.

Examples:

  • In 2026, a California Medicare enrollee with $1,000 a month of Social Security has countable income of ($1,000 − $20 − $202.90) × 12 = $9,325.20.
  • In 2020 the same person had ($1,000 − $20 − $230) × 12 = $9,000.

Dates that differ from the issue

  • Connecticut: the issue gave 2026-03-01. DSS's chart "As of 1/1/2026" already shows the $562 disregard, while the chart's medically needy limit in the same document is still the 3/1/2025 value. UPM 5030.15 raises the disregard "each January 1st", and PolicyEngine's 2023–2025 entries are already dated January 1. The CHOICES guide's "Effective 3/1/26" label follows the March medically needy limit update.
  • Missouri: the limit changes each July 1. $5,909.25 / $11,818.45 took effect 2024-07-01, and $5,726 took effect 2023-07-01, not 2024-01-01. This PR also adds the 2025-07-01 step ($6,068.80 / $12,137.55), which was missing.

Missouri's July 1 values and California's December 1, 2020 changes first apply in the next year's calculation, because these variables are annual and read parameters on January 1.

Not confirmed against a primary source

  • Connecticut couple disregard ($1,124): only the CHOICES guide states it. DSS charts give the individual amount, and $1,124 is twice $562, as in PolicyEngine's 2024 and 2025 values.
  • Connecticut couple disregard before 2024 (outside this diff): PolicyEngine's 2023 couple value ($818) matches the 2023 CHOICES guide but is not twice the 2023 individual value ($482), and the 2015-2022 couple value ($339) equals the individual value. This needs a separate follow-up.
  • Archives: the 10/2026 Appendix J (the only source for Missouri's $6,220.50 / $12,441), the Arkansas 2026 chart and the CHOICES guide of May 29, 2026 have no Wayback copy. The links point to the live files.

The Missouri 2023 couple limit ($11,452) is confirmed by DSS memo IM-57.

The issue listed Arkansas 2026 and South Carolina as resting on KFF alone. Arkansas's April 2026 chart and MS 23110 now confirm the Arkansas limit. The SCDHHS manual and State Plan confirm South Carolina's disregard and asset limit.

Out of scope

Test plan

  • YAML cases built from the sources:
    • countable income for CA ($20, no Medicare), CT ($562) and SC ($50);
    • CA Part B: a 2026 Medicare enrollee ($9,325.20), and a couple with both Part B premiums and $360 of other premiums ($8,965.20 each, ACWDL 20-24 Example 3 at 2026 premiums);
    • CA premium inputs: a total health_insurance_premiums input ($10,560); an MSP buy-in with a total input ($8,125.20); a self-paid Part B inside the total, counted once ($18,325.20);
    • CA deemed spouse: the applicant's Part B offsets deemed income ($28,765.20, income eligible); Example 5 ($31,135.20, over the limit); the zero floor;
    • CA before 12/2020: 2019 premiums ($7,200), 2020 unearned ($9,000), 2020 earned ($2,730), a 2020 couple ($9,720 each), and the 2021 Part B premium ($9,978);
    • NC deducts no premium ($11,760);
    • couples: CA one $20 deduction ($11,580 each), CT with each spouse above the individual disregard ($4,956 each), SC $50 once per couple ($11,400 each);
    • assets at the MSP limit: AR at $9,950 (eligible) and $9,951 (ineligible), SC at $9,950, an OK couple at $14,910, an SC couple at $14,911 (ineligible);
    • earlier MSP years: AR 2015 and 2016, OK 2015 couple and 2023, SC 2023 and 2025;
    • California assets: an individual at $130,000 (eligible) and one cent over (ineligible) in 2026, a couple at $195,000, and 2023 at $130,000;
    • Minnesota: Social Security of $1,330 a month (the 2026 limit) is income eligible and $1,331 is not; earned income of $1,000 a month counts $5,610 a year, with the $65 and one-half exclusions and no $20;
    • Missouri: one case per dated value from 2017 to 2027; a couple at $12,137.55 in 2026 (eligible) and at $12,137.56 (ineligible); a 2018 couple at the $4,000 limit (eligible).
  • Source conflicts: us-mo:mhabd-couple-resource-limit-boundary, ruled: statute (ruling, 2026-10-27). The code already follows the statute, so the model needs no change.
    • RSMo 208.010.2(7) lets a couple own resources "not to exceed" the couple amount. 13 CSR 40-8.020(4)(B) makes a household ineligible "at or in excess of" the limit.
    • The candidate in source_conflicts/candidates.yaml records the ruling (state: ruled). The register entry and the # Source conflict marker follow the merge. The statute reading matches the Colorado ruling, us-co:premium-assistance-ptc-recipients.
  • make format (ruff)
  • Changelog fragment in changelog.d/
  • CI passes (tests were not run locally)

No partner contract file in tests/policy/baseline/partners/ changes. The only partner Medicaid case in these states is a California senior with $1,333 a month of Social Security. In 2026 that senior's countable income goes from $1,103 a month on main to $1,110 ($1,333 − $20 − $202.90 Part B) and stays under the $1,835 limit. The pre-2021 California changes do not reach it.

axiom: TheAxiomFoundation/rulespec-us#1933 queued

🤖 Generated with Claude Code

… limits

- California: $20 any-income deduction from 2020-12-01 (ACWDL 20-24).
- Connecticut: $562 / $1,124 unearned income disregard from 2026-01-01.
- South Carolina: $50 general income disregard.
- Missouri: July 1 asset limits for 2023 through 2026.
- Arkansas, Oklahoma and South Carolina: asset limit equals the Medicare
  Savings Program resource limit, read from that parameter.

Closes PolicyEngine#10356

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program Review

PR #10364 updates the optional Medicaid pathway for aged, blind and disabled people (is_optional_senior_or_disabled_for_medicaid) in CA, CT, MO, AR, OK and SC. IDs: C = critical, SA = should address, G = suggestion.

Paths: P/ = policyengine_us/parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/, V/ = policyengine_us/variables/gov/hhs/medicaid/, T/ = policyengine_us/tests/policy/baseline/gov/hhs/medicaid/. Line numbers refer to the reviewed head.

Source Documents

Branch Status

The branch is 1 commit ahead and 35 commits behind main (12b899c11a). GitHub reports it as MERGEABLE. Rebase before merge. The staleness did not affect any finding.

What is correct

Every value and date that the PR adds or changes matches its source. CA is $20 / $20 from 2020-12-01. CT is $562 / $1,124 from 2026-01-01. SC is $50 / $50 from 2015, given once per couple. MO steps from 5,726 / 11,452 to 6,220.50 / 12,441 on each July 1 from 2023 to 2026. AR, OK and SC are tied to the federal MSP resource limit for 2015-2026. The PDF audit found 36 matches and no mismatch in a changed value. Reading the federal MSP parameter instead of copying dollar values is the right design, and it follows the uses_ssi_federal_benefit_rate pattern. The new flag lists all 51 keys. No legal value is hard-coded. The changelog fragment is correct. The PR body documents the Missouri July 1 dates and the January 1 read. Seven of the eight #page= anchors are correct. The PR touches no partner contract file, and the 70 healthcare partner contract tests pass.

Critical (Must Fix)

C1. CI failure: the Missouri at-limit couple test fails because the resources are float32.

  • Test: T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml:124-147, "Missouri couple at the couple resource limit is ineligible" (2026). It expects [false, false] and gets [true, true]. This breaks "Quick Feedback (Selective Tests + Coverage)" and "Full Suite - Baseline (contrib-hhs)". The local run gives the same failure.
  • Cause (probe probe_mo_couple_float32.out): ssi_countable_resources is float32. The input 6,137.55 (line 133) is stored as 6,137.5498046875. The couple total is therefore 12,137.5498046875. The limit 2025-07-01: 12_137.55 (P/assets/limit/couple.yaml:85, read on 2026-01-01) is the float64 value 12,137.55. So return assets < asset_limit (V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:36) returns true. The old case passed because its limit (11,452) was a whole dollar.
  • The expected false follows 13 CSR 40-8.020(4)(B), which makes a household ineligible with countable assets "at or in excess of" the limit (FILE #page=2). The reviewers read the statute and the rule together as strict. Do not change the operator to <= for every state. See Next Steps item 5 for the repo's "statute governs" rule.
  • The same noise affects the 2026 individual limit 6,068.80 (stored as 6,068.7998). A 2025 couple at 11,818.45 is ineligible only by accident. The values 5,909.25, 6,220.50, 12,441 and all MSP limits are exact in float32.
  • Fix: choose one option (probe probe_fix_options.out). The C2 fix does not change the MO branch, so C1 needs its own fix.
    1. Test only (recommended). Move the at-limit case to 2027, where the couple limit is the whole-dollar 12,441. Replace lines 124-147 with:
- name: Missouri couple at the July 2026 couple resource limit is ineligible
  period: 2027
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 6_000
      person2:
        age: 68
        ssi_countable_resources: 6_441
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: MO
  output:
    # 13 CSR 40-8.020(4)(B): ineligible "at or in excess of" the limit.
    # 12,441 combined is not under the 12,441 couple limit effective July 1, 2026.
    is_optional_senior_or_disabled_asset_eligible: [false, false]
 To keep the 2026 limit tested, add the same household with `period: 2026` and person2 at `6_200`. The output is `[false, false]`, because 12,200 is over 12,137.55.
  1. Test only. Keep 2026 and test one cent over the limit (person2 6_137.56, output [false, false]). Rename the case "Missouri couple one cent over the couple resource limit is ineligible".
  2. Formula. Compare in whole cents, so that every decimal limit gives a fixed result. Change line 16 to assets = np.round(person.marital_unit.sum(personal_assets), 2). With this change, the current 2026 case gives [false, false] under <. A precedent is policyengine_us/variables/gov/local/ca/sbd/general_relief/eligibility/ca_sbd_general_relief_income_eligible.py:20.

C2. Regulatory mismatch: the new MSP branch makes AR, OK and SC applicants with resources equal to the limit ineligible.

  • Code: V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:31-36. The PR ties AR, OK and SC to the federal MSP resource limit. It then tests that limit with strict <. The cited rules allow resources equal to the limit:
    • OK Appendix C-1, Schedule VI: resources "must be equal to or less than" $9,950 / $14,910 (FILE #page=2, visually verified). OAC 317:35-7-40(1): resources "do not exceed" Schedule VI.
    • AR MS 23110(6): resources "equal to but not exceeding three times the SSI resource standard" (Cornell).
    • SC MPPM 303.06.02 (eff. 01/01/26): countable resources "must be at or below" $9,950 / $14,910 (Section_300.docx). The State Plan uses "Same as QMB resource standards" (FILE #page=41), and the QMB rule is "do not exceed" (42 USC 1396d(p)(1)(C)).
  • Probe (v1/probe_at_limit.head.out): an AR or SC individual with $9,950 in 2026, and an OK couple with 7,455 + 7,455 = $14,910, fail the asset test. They also lose is_optional_senior_or_disabled_for_medicaid. The same people pass msp_asset_eligible, which uses <= on the same parameter (policyengine_us/variables/gov/hhs/medicare/savings_programs/eligibility/msp_asset_eligible.py:32). Three at-limit YAML cases fail at head and pass with the fix.
  • The strict operator is older than this PR (#5891). But this PR moves these states onto it, and the PR body says that the tests are "at the MSP limit". The new cases at lines 159, 169 and 194 are $50, $10 and $60 under the limit.
  • Fix: keep Missouri strict (see C1) and make only the MSP branch inclusive. Replace lines 34-36 with the code below. Verified: the 11 proposed cases (below and in SA3) pass, and the Medicaid and Medicare suites give 995 passed and 1 failed, the C1 case only.
        # The MSP resource standard (42 USC 1396d(p)(1)(C)) and the AR, OK
        # and SC rules allow resources equal to the limit.
        within_msp_limit = assets <= msp_limit
        below_state_limit = assets < state_limit
        return where(uses_msp_limit, within_msp_limit, below_state_limit)
  • Append these cases to T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml:
- name: Arkansas applicant at the 2026 Medicare Savings Program individual resource limit is eligible.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 9_950
    state_code: AR
  output:
    # MS 23110(6): resources "equal to but not exceeding" the MSP standard.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Arkansas applicant one dollar over the 2026 Medicare Savings Program individual resource limit is ineligible.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 9_951
    state_code: AR
  output:
    is_optional_senior_or_disabled_asset_eligible: false

- name: South Carolina applicant at the 2026 Medicare Savings Program individual resource limit is eligible.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 9_950
    state_code: SC
  output:
    # MPPM 303.06.02: countable resources "must be at or below" 9,950.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Oklahoma couple at the 2026 Medicare Savings Program couple resource limit is eligible.
  period: 2026
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 7_455
      person2:
        age: 68
        ssi_countable_resources: 7_455
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: OK
  output:
    # C-1 Schedule VI: resources "equal to or less than" 14,910.
    is_optional_senior_or_disabled_asset_eligible: [true, true]

C3. Regulatory mismatch: California now deducts only $20, but ACWDL 20-24 also deducts the Medicare Part B premium and health insurance premiums.

  • Code: P/income/disregard/individual.yaml:40 and couple.yaml:40 (CA 2020-12-01: 20). CA uses the default SSI path at V/income/_apply_medicaid_optional_senior_or_disabled_exclusions.py:73-79, which has no premium step. No variable in the ABD income chain reads a premium.
  • Source: ACWDL 20-24 FILE #page=3 says "Apply all applicable program deductions and disregards, including: the $20.00 Any Income deduction, health insurance premiums, and ...". The same page adds "A new disregard for Medicare Part B premiums", deducted "regardless of how the premium is paid". Example 1 (FILE #page=4) gives $1,350 - $20 - $144.60 (Part B) = $1,185. Examples 2-5 (FILE pages 5-8) also deduct Part B. The PR cites this page in its reference title ("#page 2-3").
  • Effect (probe v2/v2_probe_ca_partb.out): the old $230 was close to $20 + Part B ($222.90 in 2026). With only $20, a CA Medicare enrollee aged 70 with Social Security of $1,855.41 to $2,058.30 a month (139.5%-154.8% FPL) now fails the income test in 2026. That person also has no other Medicaid category. In 2021 the band is $1,501.21 to $1,649.70. Most ABD FPL enrollees are 65 or older and have Part B.
  • The new test asserts the gap. In T/income/medicaid_optional_senior_or_disabled_countable_income.yaml:109-118 the person (age 70) is medicare_enrolled by default. Under ACWDL 20-24 the countable income is (1,000 - 20 - 202.90) x 12 = 9,325.20, not 11,760.
  • Fix (sketch, not run; check it against ACWDL 20-24 Examples 1-5):
    1. Add P/income/deducts_health_insurance_premiums.yaml. List every other state as false, as in uses_msp_limit.yaml:
description: The state deducts Medicare Part B and other health insurance premiums from countable income under the optional senior or disabled pathway, if this is true.
metadata:
  unit: bool
  period: year
  label: Medicaid senior or disabled pathway deducts health insurance premiums
  breakdown:
  - state_code
  reference:
    - title: "California DHCS All County Welfare Directors Letter 20-24, Aged and Disabled Federal Poverty Level program #page 3-4"
      href: https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/20-24.pdf#page=3

CA:
  2015-01-01: false
  2020-12-01: true
  1. In V/income/medicaid_optional_senior_or_disabled_countable_income.py, insert this after line 59. Use the base premium, not the net medicare_part_b_premium. The net premium is $0 when the MSP pays it, and it would create a Medicaid -> buy-in -> premium dependency.
        # ACWDL 20-24: Part B is deducted "regardless of how the premium is
        # paid", so use the base premium, not the net premium after MSP.
        own_premiums = person("base_part_b_premium", period) * person(
            "medicare_enrolled", period
        ) + person("health_insurance_premiums_without_medicare_part_b", period)
        premiums = where(
            couple_computation,
            person.marital_unit.sum(own_premiums),
            own_premiums,
        )
        p_income = parameters(
            period
        ).gov.hhs.medicaid.eligibility.categories.senior_or_disabled.income
        deducts_premiums = p_income.deducts_health_insurance_premiums[state]
        personal_countable = max_(
            personal_countable - where(deducts_premiums, premiums, 0), 0
        )
  1. Example 5 (FILE #page=8) also deducts the premium of a non-applicant spouse. Decide whether the spousal deeming path needs the same step.
  2. Change the CA test at lines 109-118. Assert 9,325.20 for the Medicare enrollee, or add medicare_enrolled: false to keep a $20-only case. Add a Part B case for 2021 or later (the Part B base premium parameter starts on 2021-01-01).
  • Partner impact: the CA partner case (partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/ca.yaml) asserts eligibility, not countable income. With Part B deducted, its countable income is $1,110.10 a month, so the case stays eligible.

C4. Missing reference: no cited source shows the Missouri couple limits 11,452 (2023), 11,818.45 (2024) or 12,137.55 (2025).

  • Code: P/assets/limit/couple.yaml:83-85, with the references at couple.yaml:15-26. The same reference block is in individual.yaml:15-26.
  • The cited Appendix J URL is a living file. Today it is the 10/2026 edition, which shows only 6,220.50 / 12,441 (#page=1). The DHSS notices give individual amounts only. RSMo and 13 CSR give only the method.
  • The values are correct (verified at 600 dpi). The 07/2024 edition, page 1, shows 11,818.45. The 07-2025 edition, page 1, shows 12,137.55. DSS memo IM-57 (June 8, 2023) shows "Couple Resource Limit: $11,452" from July 1, 2023. IM-57 is the only source for 11,452, so this item is critical. The 2024 and 2025 parts alone would be should-address items.
  • Fix: in both files, add the edition to the title of the existing Appendix J entry. Then add the three entries below it:
    - title: Missouri DSS MO HealthNet for the Aged, Blind and Disabled Appendix J - Eligibility Standards for Non-MAGI Programs (10/2026 edition; spend down resource maximum effective July 1, 2026)
      href: https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=1
    - title: Missouri DSS Family Support Division, IM-57 July 2023 Annual Non-MAGI Program Adjustments (individual $5,726, couple $11,452 effective July 1, 2023)
      href: https://web.archive.org/web/20230925041524/https://dssmanuals.mo.gov/2023/06/08/im-57-4/
    - title: Missouri DSS MHABD Appendix J - Eligibility Standards for Non-MAGI Programs (07/2024 edition; spend down resource maximum effective July 1, 2024)
      href: https://web.archive.org/web/20240612134513/https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=1
    - title: Missouri DSS MHABD Appendix J - Eligibility Standards for Non-MAGI Programs (07-2025 edition; spend down resource maximum effective July 1, 2025)
      href: https://web.archive.org/web/20250831223642/https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=1

C5. Missing reference: the Oklahoma limits that the PR changes for 2015-2025 have no corroborating source.

  • Code: P/assets/limit/uses_msp_limit.yaml:13-16 (OK references) and lines 94-95 (OK true from 2015-01-01). Before the PR, OK was 2,000 / 3,000 for 2015-2023. The cited C-1 is the 7/1/2026 edition. The cited OAC page is "Revised 09-14-20" and has no dollar values. The PR body says that every C-1 edition back to 2014 shows the MSP figure, but it cites none of them.
  • The value is correct. Nineteen archived C-1 editions from 12/1/2015 to 4/1/2023 show that Schedule VI equals the federal MSP limit in each year (7,280 / 10,930 in 2015 to 9,090 / 13,630 in 2023). The old 2,000 / 3,000 came from Schedule VIII (SSI-related ABD), not Schedule VI.
  • Fix: add these entries to the OK references in uses_msp_limit.yaml. The 2015 edition is an XFA form, so it has no usable page anchor. It opens in Adobe Reader or Firefox.
    - title: Oklahoma Human Services Appendix C-1 (edition 12/1/2015), Schedule VI (QMB Plus)
      href: https://web.archive.org/web/20151216003834/http://www.okdhs.org/okdhs%20form%20library/C-1.pdf
    - title: Oklahoma Human Services Appendix C-1 (edition 4/1/2023), Schedule VI (QMB Plus)
      href: https://web.archive.org/web/20230420231329/https://oklahoma.gov/content/dam/ok/en/okdhs/documents/searchcenter/okdhsformresults/c-1.pdf#page=2
    - title: Oklahoma Administrative Code 317:35-7-40(a)(1) (revised 05-11-01)
      href: https://web.archive.org/web/20151029121301/http://www.okdhs.org/library/policy/oac317/035/07/0040000.htm

Should Address

SA1. Wrong page anchor: the 13 CSR 40-8.020(4) href opens the table of contents.

  • Code: P/assets/limit/individual.yaml:20 and couple.yaml:20 (13c40-8.pdf#page=1). FILE page 1 is the cover and the table of contents. Section (4) is on FILE page 2 (printed page 3). It contains the (4)(B) "at or in excess of" text and the (4)(C) CPI rule.
  • Fix in both files:
    - title: 13 Missouri Code of State Regulations 40-8.020(4)(A)-(C)
      href: https://www.sos.mo.gov/CMSImages/AdRules/csr/current/13csr/13c40-8.pdf#page=2

SA2. Value mismatch (pre-existing, rated for this PR): Missouri's 3,000 / 6,000 from 2015 contradicts the statute and the rule that the PR now cites.

  • Code: P/assets/limit/individual.yaml:82 and couple.yaml:82. Annual calculations read the value on January 1, so 3,000 / 6,000 applies to every year from 2015 to 2023.
  • The law gives these values (13 CSR 40-8.020(4)(A)-(B), FILE #page=2, read at 600 dpi):
    • before 7/1/2017: 1,000 / 2,000 (aged or disabled);
    • then 2,000 / 4,000, 3,000 / 6,000, 4,000 / 8,000 and 5,000 / 10,000 on each July 1 from 2017 to 2020;
    • 5,035 / 10,070 from 7/1/2021 (IM-70);
    • 5,301.85 / 10,603.70 from 7/1/2022 (IM-77; Appendix J 10/2022, page 1).
  • Only 2019 is right. For example, the model uses 3,000 for 2023, but the law gives 5,301.85.
  • Why this is rated for this PR: the PR rewrites this MO block. It also attaches RSMo 208.010.2(7) and 13 CSR 40-8.020(4), and both contradict the entry. Every step now has a primary source. The PR did not make any year wrong that was right before: base and PR give the same annual values for 2015-2024. No MO test uses a year before 2025, so the fix breaks no test.
  • Fix: replace the MO blocks (individual first, then couple):
MO:
  2015-01-01: 1_000
  2017-07-01: 2_000
  2018-07-01: 3_000
  2019-07-01: 4_000
  2020-07-01: 5_000
  2021-07-01: 5_035
  2022-07-01: 5_301.85
  2023-07-01: 5_726
  2024-07-01: 5_909.25
  2025-07-01: 6_068.8
  2026-07-01: 6_220.5
MO:
  2015-01-01: 2_000
  2017-07-01: 4_000
  2018-07-01: 6_000
  2019-07-01: 8_000
  2020-07-01: 10_000
  2021-07-01: 10_070
  2022-07-01: 10_603.7
  2023-07-01: 11_452
  2024-07-01: 11_818.45
  2025-07-01: 12_137.55
  2026-07-01: 12_441

Add these references to both files, together with the SA1 fix:

    - title: Missouri DSS Family Support Division, IM-70 Annual MO HealthNet for the Aged, Blind, and Disabled (MHABD) Asset Limit Increase (individual $5,035, couple $10,070 effective July 1, 2021)
      href: https://web.archive.org/web/20210801111201/https://dssmanuals.mo.gov/2021/07/01/im-70-2/
    - title: Missouri DSS Family Support Division, IM-77 Annual MHABD Asset Limit Increase (individual $5,301.85, couple $10,603.70 effective July 1, 2022)
      href: https://web.archive.org/web/20220705011659/https://dssmanuals.mo.gov/2022/06/14/im-77-3/
    - title: Missouri DSS MHABD Appendix J - Eligibility Standards for MHABD (10/2022 edition; spend down resource maximum effective July 1, 2022)
      href: https://web.archive.org/web/20220927091025/https://dssmanuals.mo.gov/wp-content/uploads/2022/07/mhabd-appendix-j.pdf#page=1
  • Minimum fix: add the 2021-07-01 and 2022-07-01 steps, which correct 2022 and 2023, and track the 2015-2021 steps in an issue. Note: this parameter has no blind split. Before 7/1/2017, the blind limit was 2,000 / 4,000.

SA3. Missing edge cases: no couple disregard case for CA, CT or SC, and no pre-2024 case for the retroactive MSP linkage.

  • T/income/medicaid_optional_senior_or_disabled_countable_income.yaml has only individual cases for CA, CT and SC (lines 109-140). No test in the repo reaches p.couple[state] (V/income/medicaid_optional_senior_or_disabled_countable_income.py:47-51) for a changed couple value. So no test pins SC's "given only once in couple cases" (MPPM 303.06.03C) or the single $20 in ACWDL 20-24 Example 3 (FILE #page=5).
  • T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml tests the MSP branch only in 2025 and 2026. Every MSP case would still pass if uses_msp_limit started in 2025. But the PR changes OK, AR and SC values back to 2015.
  • The PR body says "asset tests for AR and SC at the MSP limit, an OK couple at the MSP couple limit". The cases are under the limit. Correct the body, or add the C2 cases.
  • Fix: these cases were run. Each one detects the PR change, and all of them pass at head. Append to the asset test file:
- name: Oklahoma uses the 2023 Medicare Savings Program individual resource limit.
  period: 2023
  input:
    age: 70
    ssi_countable_resources: 5_000
    state_code: OK
  output:
    # 5,000 is under the 2023 limit of 9,090.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Oklahoma uses the 2015 Medicare Savings Program couple resource limit.
  period: 2015
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 5_000
      person2:
        age: 68
        ssi_countable_resources: 5_000
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: OK
  output:
    # 10,000 combined is under the 2015 couple limit of 10,930.
    is_optional_senior_or_disabled_asset_eligible: [true, true]

- name: Arkansas uses the 2016 Medicare Savings Program individual resource limit.
  period: 2016
  input:
    age: 70
    ssi_countable_resources: 7_400
    state_code: AR
  output:
    # 7,400 is over the 2016 limit of 7,280.
    is_optional_senior_or_disabled_asset_eligible: false

- name: South Carolina uses the 2023 Medicare Savings Program individual resource limit.
  period: 2023
  input:
    age: 70
    ssi_countable_resources: 9_000
    state_code: SC
  output:
    # 9,000 is under the 2023 limit of 9,090.
    is_optional_senior_or_disabled_asset_eligible: true

Append to the countable income file. The same household also gives CT (1,950 - 1,124) x 12 / 2 = 4,956 each and SC (1,950 - 50) x 12 / 2 = 11,400 each. Copy the case with state_code: CT and state_code: SC and those outputs. A $50 disregard for each spouse would give 11,100 in SC, so the SC case pins "given only once".

- name: California applies one 20-dollar deduction to a couple's combined unearned income.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        is_tax_unit_head: true
        ssi_unearned_income: 15_600
      person2:
        age: 68
        is_tax_unit_spouse: true
        ssi_unearned_income: 7_800
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: CA
  output:
    ssi_couple_computation_applies: [true, true]
    # ACWDL 20-24 Example 3: (1,950 - 20) x 12 / 2 = 11,580 each.
    medicaid_optional_senior_or_disabled_countable_income: [11_580, 11_580]
  • The C3 fix changes the CA couple case. After C3, add medicare_enrolled: false to both people, or assert the Part B deduction for both spouses. By hand, under the C3 sketch: (1,950 - 20 - 2 x 202.90) x 12 / 2 = 9,145.20 each.

Suggestions

  • G1. Document the January 1 read next to the dated entries. P/assets/limit/individual.yaml:81-86, couple.yaml:81-86 and P/income/disregard/{individual,couple}.yaml:38-40. The lag is documented only in the PR body and in one test name. Fix: under MO: add # Annual calculations read parameters on January 1, so each July 1 value first applies in the next calendar year. Under CA: add # The 2020-12-01 value first applies in 2021 annual calculations.
  • G2. Describe the MSP linkage in the variable. V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:9, 31-34. Fix: make reference a tuple that adds https://www.law.cornell.edu/uscode/text/42/1396d#p. Add a one-line comment above line 31 that the flagged states use the QMB/MSP resource standard, as in medicaid_optional_senior_or_disabled_income_limit.py:44-53.
  • G3. The omitted AR, OK and SC keys resolve to $0. In P/assets/limit/individual.yaml and couple.yaml, homogenization fills the missing keys with 0. As a result, the parameter page shows $0 for these states. A reform that sets uses_msp_limit.AR to false also makes every AR applicant asset-ineligible. This follows the uses_ssi_federal_benefit_rate precedent, so it is not a defect. Fix: add a YAML comment that names the omitted states. You can also move the second description sentence into that comment, because a description has one sentence. Note: the variable reads only the MSP dollar limits, not gov.hhs.medicare.savings_programs.eligibility.asset.applies. Today applies is true for all three states.
  • G4. The CT couple value 1,124 rests only on the ADS CHOICES guide. P/income/disregard/couple.yaml:48. Fix: add # Couple = 2 x the individual standard (UPM 5030.15 A: subtracted from each unit member's unearned income); it rises each January 1. The CHOICES label "Effective 3/1/26" follows the March medically needy update. The DSS chart "As of 1/1/2026" supports the January date.
  • G5. Several hrefs point to living documents. Appendix J, C-1, the AR chart and the CHOICES guide are replaced each year, and Appendix J has already lost the 2023-2025 values. Fix: for each dated value, also cite an archived edition. C4 and C5 do this for MO and OK. Wayback has no capture of the 10/2026 Appendix J yet (the latest capture is 2026-03-04). Request one and cite it.
  • G6. The CA couple reference does not point to the one-$20 example. P/income/disregard/couple.yaml:17. Example 3 is on FILE page 5. Fix, in the repo's format for nonconsecutive pages: title: "California DHCS All County Welfare Directors Letter 20-24, Aged and Disabled Federal Poverty Level program #page 2-3,5".
  • G7. Add more MSP and MO test points. AR 2026 couple 7,400 + 7,400 gives [true, true]. SC 2026 couple 7,500 + 7,500 gives [false, false]. OK 2026 individual 9,900 gives true. SC 2025 individual 9,700 gives false, which pins the year that the name at line 194 claims. MO 2026 individual 6,200 gives false (the mirror of line 149). MO 2025 individual 5,800 gives true. Use a MO individual at-limit case only in 2027 (6,220.50 is exact). Do not use at-limit cases at 6,068.80, 11,818.45 or 12,137.55 (C1).
  • G8. The CA date boundary is not tested, although the name at line 109 claims it. T/income/medicaid_optional_senior_or_disabled_countable_income.yaml:109. Fix: add CA 2021 (12,000 gives 11,760 for a person without Medicare) and CA 2020 (gives 9,240, because 230 applies to all of 2020). Alternatively, rename the case. An optional SC 2015 case (11,400) pins the SC date.
  • G9. Use one test-name style in each file. In the asset file, the names at lines 149, 159, 169 and 194 end with a period, and the names at lines 99 and 124 do not.
  • G10. Re-date the CA 138% FPL income limit to 2020-12-01. P/income/limit/individual.yaml:29-31 and couple.yaml:29-31 (outside the diff) have CA 2021-01-01: 1.38. ACWDL 20-24 (FILE #page=1) says "effective December 1, 2020". This has no annual effect. The new date would match the PR's new 2020-12-01 disregard entry.
  • G11. Follow-up: the CA disregard before 12/2020 understates the total by $20. P/income/disregard/individual.yaml:39 and couple.yaml:39. The ACWDL 00-57 MC 176 form (FILE #page=3) subtracts the $20 any-income deduction. It then subtracts the $230 / $310 disregard after the earned-income steps (line 16), and health insurance premiums (line 17). ACWDL 02-38 (FILE #page=1) agrees. The helper puts the state value in place of the $20. So for unearned income, 230 / 310 is $20 too low. In 2019, Social Security of $1,270.84 to $1,290.83 a month flips to ineligible. Fix in a follow-up issue: use 250 / 330 from 2015-01-01, or apply 230 / 310 after the earned-income steps. Optional in this PR: cite ACWDL 00-57 #page=3 for the 2015-01-01 entry.

Out of scope / follow-up (pre-existing, not caused by this PR):

  • AR ARSeniors is aged-only (MS 23110(1); the chart on page 2 says "only for individuals aged 65 or older"). But uses_msp_limit applies the MSP test to blind and disabled people under 65 too. The PR lists this as out of scope.
  • P/assets/limit/individual.yaml:4 has period: month, but couple.yaml:5 has period: year. This has no numeric effect, and the fix is one line.
  • In P/assets/limit/couple.yaml:9-10, the KFF title and href do not match.
  • A larger operator design: use inclusive <= by default (42 USC 1396a(m)(1)(C), "do not exceed"), with a by-state strict switch for MO like income/limit/requires_income_below_limit.yaml. This changes every non-MSP state at the limit, so it needs a separate issue.
  • Before 2015-01-01, the variable raises ParameterNotFoundError for every state.
  • The CT income/limit entries for 2023 (0.327 / 0.149) were not checked.
  • Issue #9734 and the CA asset limit change on 2027-07-01 (the PR lists both).

Investigated and cleared:

  • Page lists at the end of reference titles ("#page 2-3", "#page 41-42"): this is the repo CLAUDE.md rule for several cited pages, not a violation.
  • OK linkage values for 2015-2023: all 19 archived editions show the MSP figure. The January editions show the prior year until the April update. The federal standard changes on January 1, so the model is correct. Only the reference gap (C5) remains.
  • CT 2026 income limit (raised by the PDF audit): this was a false positive. For 2026 the model reads 0.64 x FPG = $851.20 a month of countable income. The CHOICES guide gives $1,413 gross - $562 = $851.
  • MO strict <: correct for MO (13 CSR 40-8.020(4)(B)).
  • CT date 2026-01-01 against the CHOICES label "Effective 3/1/26": the date is accepted (DSS chart "As of 1/1/2026"; UPM 5030.15 "each January 1st").
  • MO 2023 couple value 11,452: correct (IM-57 and the CPI derivation).
  • SC $50 once per couple, with the unused part applied to earned income: the code gives the right numbers (probe: 6,900 and 2,490).

PDF Audit Summary

Category Count
Confirmed correct 38 (36 in the PDF audit, plus 2 from the verifiers: MO couple 11,452 from IM-57, and OK Schedule VI = MSP for 2015-2023)
Mismatches (code-path confirmed + visually verified) 0 in the values that the PR adds or changes. 2 regulatory mismatches in how the values are applied (C2, C3)
Mismatches rejected (code-path cleared) 2 (OK 2015-2023 linkage value; CT 2026 income limit)
Unmodeled items 2 (CA Part B and health insurance premium deductions, C3; AR ARSeniors aged-only rule, out of scope)
Pre-existing issues 4 (MO 2015-2023 limits, SA2; CA $20 before 12/2020, G11; CA 138% FPL date, G10; period metadata and KFF title, out of scope)

References: 18 distinct references added (30 entries). Of the 8 #page= anchors, 7 are correct and 1 is wrong (SA1). Three values had no corroborating cited source: MO couple 2023-2025 (C4) and OK 2015-2025 (C5).

Validation Summary

Check Result
Regulatory Accuracy 2 critical (C2, C3); 1 should address (SA2)
Reference Quality 2 critical (C4, C5); 1 should address (SA1); 4 suggestions (G4-G6, G10)
Code Patterns 0 blocking. 10 categories pass, with no hard-coded values. 3 suggestions (G1-G3)
Formatting (params & vars) 0 issues
Test Coverage 1 failing test (C1); at-limit gap (inside C2); 1 should address (SA3); 3 suggestions (G7-G9)
PDF Value Audit 0 mismatches in changed values / 36 confirmed (+2 by verifiers); 2 pre-existing MO mismatches (SA2)
CI Status Failing: 15 pass, 3 skipping, 3 fail. "Quick Feedback" and "Full Suite - Baseline (contrib-hhs)" fail on C1. "Package and compatibility" fails at python .github/release_lock.py --refresh (v2.56.0 -> v2.56.1rc1). The PR changes no uv.lock, pyproject.toml or .github file, and #10365 passes this job, so this failure does not come from the PR
Local tests Medicaid eligibility/categories: 245 passed, 1 failed (C1). Medicaid income: 164 passed. With the C2 fix, the Medicaid and Medicare suites give 995 passed and 1 failed (C1)
Partner contract tests No partner file touched. 70 healthcare partner contract tests pass
Branch 1 ahead, 35 behind main; MERGEABLE
Other reviews None

Review Severity: REQUEST_CHANGES

Next Steps

  1. Suggested order: C1 and C2 first (one formula edit and test edits in the same two files). Then C3, then the references (C4, C5, SA1), then SA2 and SA3.
  2. Run policyengine-core test policyengine_us/tests/policy/baseline/gov/hhs/medicaid -c policyengine_us before you push. The PR body says that the tests were not run locally.
  3. C3 changes CA countable income. Run the partner tests under policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/healthcare again. The CA case should stay eligible. Do not edit a partner file without the partner review gate.
  4. Not assessed by the review delegates: the PR body has no axiom: line. The repo CLAUDE.md section "Axiom Parity (required for policy changes)" asks for one line on every policy change: axiom: <legal id> encoded-correct | <rulespec PR> encoded | <rulespec issue> queued | n/a: <reason>. An external contributor who cannot do the Axiom work uses axiom: needed. Add the line when you update the body.
  5. Not assessed by the review delegates: the repo CLAUDE.md rule "Statute governs (ruled 2026-10-19)" says that a statute wins over a conflicting regulation. RSMo 208.010.2(7) says that a couple "may own resources not to exceed" the couple amount. 13 CSR 40-8.020(4)(B) says "at or in excess of" is ineligible. The reviewers read the two together as strict, because the statute's wording is garbled for an individual and RSMo 208.010.2(4) uses "or more". If you or a maintainer read this as a conflict, the statute governs. MO then needs <=, and the C1 test expects [true, true]. Record that choice in the source-conflict register with a # Source conflict MO:<id> line (CONTRIBUTING.md#source-conflicts). The float32 fix in C1 is still needed in both readings.
  6. To auto-fix issues: run the fix-pr workflow for this PR.

hua7450 and others added 2 commits October 23, 2026 22:24
…tory

- California's aged, blind and disabled income test deducts the Medicare
  Part B premium and other health insurance premiums from December 2020
  (ACWDL 20-24), through a new state flag.
- Resources equal to the Medicare Savings Program limit qualify in AR, OK
  and SC; Missouri stays strict ("at or in excess of").
- Compare assets in whole cents so decimal limits are not affected by
  float32 storage (fixes the Missouri at-limit couple test).
- Encode Missouri's asset limits from 2015 (13 CSR 40-8.020(4), IM-70,
  IM-77) and cite archived Missouri and Oklahoma editions.
- Fix the 13 CSR page anchor, re-date CA's 138% FPL limit to 2020-12-01,
  and add comments on the January 1 read and the MSP-linked states.
- Add at-limit, earlier-year MSP, couple disregard, CA Part B and CA date
  boundary tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program Review

This is round 2 of the review of PR #10364. It covers the author's round-2 commit cd33ee1a4e (13 files, +504 / -24) and every round-1 finding. The later merge of main changes no PR file. IDs: C = critical, SA = should address, G = suggestion. Round-1 IDs (C1-C5, SA1-SA3, G1-G11) keep their numbers. New findings start at C6, SA4 and G12.

Paths: P/ = policyengine_us/parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/, V/ = policyengine_us/variables/gov/hhs/medicaid/, T/ = policyengine_us/tests/policy/baseline/gov/hhs/medicaid/. Line numbers refer to the reviewed head.

Source Documents

Branch Status

The branch is 3 commits ahead and 31 commits behind main (0e7ced1747). GitHub reports it as MERGEABLE. The merge of main into the branch changes no PR file. The staleness did not affect any finding.

What round 2 fixed

The author applied almost every round-1 fix as proposed. Thank you.

  • C1: the asset test compares whole cents. The marital-unit sum is float64, so the MO at-limit couple now fails for the right reason.
  • C2: <= for the MSP branch and < for Missouri, with at-limit cases for AR, SC and OK.
  • C3: the new CA flag and premium step deduct the base Part B premium and other premiums. At 2026 premiums, ACWDL 20-24 Examples 1, 3 and 4 match exactly. Three gaps remain (C6, C7, SA4).
  • C4, C5, SA1: every proposed reference is in place and corroborates its value.
  • SA2: the Missouri schedule from 2015 to 2026 matches the sources (22 dated values). Tests are still missing (SA7).
  • SA3: every proposed couple case and back-year MSP case is present.
  • References: 35 references and values checked. 34 are correct, and 1 is wrong (C6).
  • G1-G4, G6, G8-G10: done.
  • Tests: Medicaid eligibility/categories 255 passed, Medicaid income 178 passed, Medicare 301 passed, healthcare partner contract tests 70 passed. No partner file changes. CI: 32 of 32 checks pass.

Three new items (C6, C7, SA4) come from our round-1 C3 sketch, which the author followed exactly. They correct our sketch. They are not author errors.

Round-1 status

ID Status Evidence
C1 RESOLVED Whole-cent comparison at V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:20. Without the rounding, T asset file line 124 fails.
C2 RESOLVED <= for the MSP branch and < for MO (lines 41-45). The at-limit cases at T asset file lines 204-259 fail with <.
C3 RESOLVED Flag and premium step at V/income/medicaid_optional_senior_or_disabled_countable_income.py:63-79. Examples 1, 3 and 4 are exact. New gaps: C6, C7, SA4.
C4 RESOLVED IM-57 and Appendix J 07/2024 and 07-2025 are cited, and each shows the MO couple value.
C5 RESOLVED OK C-1 12/1/2015, C-1 4/1/2023 and OAC 2001 are cited. Each Schedule VI equals that year's MSP limit.
SA1 RESOLVED The 13 CSR href is #page=2, and the title is "(4)(A)-(C)".
SA2 RESOLVED (values) All 22 MO dated values match the sources. No test pins them (SA7).
SA3 RESOLVED CA, CT and SC couple cases (T income file lines 157, 184, 208). OK 2015 and 2023, AR 2016 and SC 2023 cases (T asset file lines 261-315). The body is corrected.
G1 RESOLVED January 1 comments in the MO, CA and new flag blocks.
G2 RESOLVED The reference tuple adds 42 USC 1396d(p). MSP comments at lines 35-36 and 41-42.
G3 RESOLVED Comment on the omitted states at P/assets/limit/{individual,couple}.yaml:40-42. The second description sentence is removed.
G4 RESOLVED CT comment and the body's "Not confirmed" section. The comment describes a per-member rule that the code does not apply (see Out of scope).
G5 PARTIAL MO and OK editions are archived. The 10/2026 Appendix J, the AR 2026 chart, the CHOICES guide (5/29/2026) and the OK C-1 7/1/2026 are not. Only the live Appendix J supports MO 6,220.50 / 12,441.
G6 RESOLVED The CA couple title ends with "#page 2-3,5".
G7 PARTIAL SC 2025 9,700 was added. The MO part moves to SA7. The MSP part remains (below).
G8 RESOLVED CA 2020 (9,240) and CA 2021 (11,760) cases. The first year of the premium flag moves to SA7.
G9 RESOLVED The names of the PR's test cases end with a period.
G10 RESOLVED CA 1.38 is dated 2020-12-01, with ACWDL 20-24 #page=1.
G11 DECLINED (defensible) The body lists it under "Follow-up", with no issue number. C6 adds the premium deduction to the same pre-12/2020 gap.

Round-1 Next Steps 5 (Missouri statute against the rule): DECLINED. The author kept the strict test and registered no conflict. This is now SA8.

G7 remainder (suggestion; each case passes at head): AR 2026 couple 7,455 + 7,455 gives [true, true]. SC 2026 couple 7,455 + 7,455 gives [true, true], and 7,500 + 7,500 gives [false, false]. OK 2026 individual 9,950 gives true.

Critical (Must Fix)

C6. Value mismatch: the new California flag turns off the health insurance premium deduction for 2015-2020, but California deducted these premiums from 2001.

  • Code: P/income/deducts_health_insurance_premiums.yaml:20-23 (CA: 2015-01-01: false, 2020-12-01: true). This file is new in round 2. One boolean gates both the base Part B premium and health_insurance_premiums_without_medicare_part_b (V/income/medicaid_optional_senior_or_disabled_countable_income.py:63-79).
  • Sources:
    • ACWDL 00-57, MC 176 (10/00) form (FILE #page=3): line 16 subtracts the $230 / $310 disregard, and line 17 subtracts "HEALTH INSURANCE PREMIUMS". The program started on January 1, 2001.
    • ACWDL 20-24, the letter that the flag cites (FILE #page=3): the deductions used before the expansion "will continue to apply", and the list includes health insurance premiums. Only the Part B disregard is new.
    • 22 CCR 50555.2(a) and (c): premiums that a family member pays are deducted. Part B is deducted only in the months when the beneficiary pays it.
    • ACWDL 20-18 (FILE #page=2): before December 1, 2020, counties deducted Part B only when the person paid it. From that date, the disregard applies whoever pays.
  • The two rules have two start dates. One boolean cannot encode both.
  • Probe at head: a CA person aged 70, not on Medicare, with $12,000 of unearned income and $1,800 a year of other premiums. The result is 9,240 in 2015, 2019 and 2020 (no deduction) and 9,960 in 2021. By MC 176, the 2015-2020 amount is (1,000 - 230 - 150) x 12 = 7,440.
  • There is no regression against main, which deducted no premium in any year. Our round-1 C3 sketch prescribed this false entry, although round-1 G11 already cited MC 176. The error is ours.
  • Fix: split the flag. This fix also closes SA4 and adds the SA5 NOTE. It was run on a copy of head: Medicaid income 178 passed, eligibility/categories 255 passed, Medicare 301 passed, partner healthcare 70 passed. The case below and the 3 cases in SA4 fail at head and pass with the fix.
    1. In P/income/deducts_health_insurance_premiums.yaml, replace the header and the CA block. Keep the other 50 keys.
description: The state deducts health insurance premiums that the person pays from countable income under the optional Medicaid pathway for seniors or people with disabilities, if this is true.
metadata:
  unit: bool
  period: year
  label: Medicaid senior or disabled pathway deducts health insurance premiums
  breakdown:
  - state_code
  reference:
    - title: Cal. Code Regs. Tit. 22, § 50555.2 (a), (c) Health Insurance Premiums
      href: https://www.law.cornell.edu/regulations/california/22-CCR-50555.2
    - title: California DHCS All County Welfare Directors Letter 00-57, MC 176 Aged and Disabled FPL Financial Eligibility Form, line 17
      href: https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/c00-57.pdf#page=3
    - title: "California DHCS All County Welfare Directors Letter 20-24, Aged and Disabled Federal Poverty Level program #page 2-3"
      href: https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/20-24.pdf#page=2
CA:
  # The ABD FPL program deducted premiums from its start in 2001.
  2015-01-01: true
  1. Add P/income/disregards_medicare_part_b_premium.yaml. Give all 51 keys 2015-01-01: false, except CA:
description: The state disregards the full Medicare Part B premium, regardless of who pays it, from countable income under the optional Medicaid pathway for seniors or people with disabilities, if this is true.
metadata:
  unit: bool
  period: year
  label: Medicaid senior or disabled pathway Medicare Part B premium disregard
  breakdown:
  - state_code
  reference:
    - title: "California DHCS All County Welfare Directors Letter 20-18, Medicare Part B premium disregard #page 1-2"
      href: https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/20-18.pdf#page=1
    - title: "California DHCS All County Welfare Directors Letter 20-24, Aged and Disabled Federal Poverty Level program #page 3-8"
      href: https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/20-24.pdf#page=3

CA:
  # Before this date Part B counted only when the person paid it
  # (22 CCR 50555.2(c)); deducts_health_insurance_premiums covers that.
  # The 2020-12-01 value first applies in 2021 annual calculations.
  2015-01-01: false
  2020-12-01: true
  1. In V/income/medicaid_optional_senior_or_disabled_countable_income.py, replace lines 63-75 with the code below. Keep lines 76-79, or apply C7. Add "https://www.law.cornell.edu/regulations/california/22-CCR-50555.2" and "https://www.dhcs.ca.gov/services/medi-cal/eligibility/letters/Documents/20-18.pdf#page=2" to reference.
        # 22 CCR 50555.2 deducts person-paid premiums, Part B only when the
        # person pays it. From 12/2020 (ACWDL 20-18, 20-24) CA disregards Part B
        # "regardless of how the premium is paid": swap the person-paid Part B
        # in the shared aggregate for the full base premium.
        # NOTE: medicare_enrolled (Part A and/or Part B) stands in for Part B
        # enrollment, as in medical_expense_health_insurance_premiums.
        medicare_enrolled = person("medicare_enrolled", period)
        paid_premiums = person("medical_expense_health_insurance_premiums", period)
        paid_part_b = person("medicare_part_b_premium", period) * medicare_enrolled
        base_part_b = person("base_part_b_premium", period) * medicare_enrolled
        disregards_part_b = p.disregards_medicare_part_b_premium[state].astype(bool)
        own_premiums = where(
            disregards_part_b,
            max_(paid_premiums - paid_part_b, 0) + base_part_b,
            paid_premiums,
        )
        premiums = where(
            couple_computation,
            person.marital_unit.sum(own_premiums),
            own_premiums,
        )
  1. Append to T/income/medicaid_optional_senior_or_disabled_countable_income.yaml:
- name: California deducted other health insurance premiums before 12/2020.
  period: 2019
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: false
    ssi_marital_unearned_income: 12_000
    health_insurance_premiums_without_medicare_part_b: 1_800
    state_code: CA
  output:
    # ACWDL 00-57 MC 176 lines 16-17: (1,000 - 230 - 150) x 12 = 7,440.
    medicaid_optional_senior_or_disabled_countable_income: 7_440
  1. The CA 2020 case (T income file lines 255-257) keeps 9,240. Change its comment to: "2020 reads January 1, 2020: the 230 disregard applies, the Part B disregard does not, and the MSP pays Part B, so no person-paid premium is deducted."
  • A simpler variant is acceptable: deduct other premiums from 2015 and no Part B before 12/2020, with a NOTE. Below the pre-12/2020 limit, the MSP pays Part B for almost everyone.
  • The new titles also fix the page range of the current title, which stops at page 5. Examples 4 and 5 are on FILE pages 6-8.

C7. Incorrect formula: the zero floor comes before the deemed spouse income is added, so the applicant's excess premium is lost and eligibility flips.

  • Code: V/income/medicaid_optional_senior_or_disabled_countable_income.py:77-79 floors the applicant's own countable income minus premiums at 0. Lines 89-96 then add spousal_deemed. When the premiums are more than the applicant's own countable income, the excess does not reduce the deemed income.
  • Sources:
    • ACWDL 20-24 Example 2 (FILE #page=4, continued on page 5): the spouses' gross income "is combined, and then the deductions are applied".
    • ACWDL 20-24 Example 5 (FILE #page=7, pages 7-8): the letter combines the incomes, then subtracts Part B and the premiums.
    • 20 CFR 416.1163(d)(2): the deemed income is combined with the applicant's own income before the exclusions.
    • Repo CLAUDE.md:145: "Follow the exact order of operations specified in regulations".
  • The test proves a flip. CA 2026: the applicant is 70, a Medicare enrollee, with $100 a month of unearned income. The spouse is 60, not aged, blind or disabled, with $2,520 a month. The limit is 29,863.20.
    • Head: countable income [30,240, 0], is_optional_senior_or_disabled_income_eligible [false, false], is_optional_senior_or_disabled_for_medicaid [false, false].
    • Fix: [28,765.20, 0], [true, true], [true, false]. Monthly: 100 + 2,520 - 20 - 202.90 = 2,397.10.
    • The letter's own method also gives "eligible": 100 + 2,520 - 20 - 202.90 - 600 (Maintenance Need Allowance) = 1,797.10, which is under the 1-person limit of 1,835.40. So head is wrong under the model's method and under the letter's method.
  • Our round-1 C3 sketch put the floor in this position. This item corrects our sketch.
  • Fix: replace lines 76-97 with the code below. Run on a copy of head: the two PR test files 41 passed, Medicaid income 178, eligibility/categories 255, partner healthcare 70. ruff format reports no change.
        deducts_premiums = p.deducts_health_insurance_premiums[state].astype(bool)
        deducted_premiums = where(deducts_premiums, premiums, 0)

        spousal_deemed = person(
            "medicaid_optional_senior_or_disabled_income_deemed_from_ineligible_spouse",
            period,
        )
        is_eligible_individual = person("is_ssi_eligible_individual", period)
        is_eligible_spouse = person("is_ssi_eligible_spouse", period)
        is_eligible = is_eligible_individual | is_eligible_spouse

        # ACWDL 20-24 Examples 2 and 5 combine the non-applicant spouse's
        # income with the applicant's income before the deductions, as
        # 20 CFR 416.1163(d)(2) does, so subtract the premiums from the
        # combined income and floor the result.
        countable = where(
            couple_computation,
            max_(personal_countable - deducted_premiums, 0) / 2,
            max_(personal_countable + spousal_deemed - deducted_premiums, 0),
        )
        return where(is_eligible, countable, 0)
  • Append this case. It fails at head (30,240 and false) and passes with the fix:
- name: California deducts the applicant's Part B premium from income that includes deemed spouse income.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        is_tax_unit_head: true
        medicare_enrolled: true
        ssi_unearned_income: 1_200
      person2:
        age: 60
        is_tax_unit_spouse: true
        ssi_unearned_income: 30_240
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: CA
  output:
    # ACWDL 20-24 Examples 2 and 5 deduct premiums from the combined income.
    # Monthly, 100 + 2,520 - 20 - 202.90 = 2,397.10, so 28,765.20 a year,
    # under the 29,863.20 limit. Flooring the applicant's own income first
    # would lose 122.90 a month and give 30,240.
    medicaid_optional_senior_or_disabled_countable_income: [28_765.2, 0]
    is_optional_senior_or_disabled_income_eligible: [true, true]

To keep the boolean output free of a margin, move the last output line into a copy of the case without absolute_error_margin.

  • The C6 edit (lines 63-75) and the C7 edit (lines 76-97) do not overlap. Each edit ran alone on a copy. Run the suites again after you apply both.

Should Address

SA4. Pattern violation: the premium step ignores the direct health_insurance_premiums input.

  • Code: V/income/medicaid_optional_senior_or_disabled_countable_income.py:67-70 reads only health_insurance_premiums_without_medicare_part_b.
  • The repo defines health_insurance_premiums as "an alternative to the applicable decomposed non-pretax inputs" (policyengine_us/variables/household/expense/health/health_insurance_premiums.py:14-22). The shared aggregate medical_expense_health_insurance_premiums.py:20-28 uses the direct input when it is nonzero. SNAP, medically needy, HUD and the itemized deduction read that aggregate. This variable is the only consumer that reads the decomposed input alone.
  • Probe: CA 2026, no Medicare, $12,000 of unearned income, health_insurance_premiums: 1_200. Head gives 11,760. ACWDL 20-24 gives (1,000 - 20 - 100) x 12 = 10,560. With Social Security of $1,856 to $2,005 a month and $150 a month of premiums, the person is pathway-eligible with the decomposed input and ineligible with the direct input.
  • Exposure: in the default dataset, no CA aged, blind or disabled person has a nonzero direct input, so the microsimulation effect is zero. Household and API users who use the common field get the wrong result. The CA partner signature sends this field (0 today).
  • Do not use where(direct != 0, direct, part_b + other). For an enrollee whose Part B the MSP pays, it drops the base Part B: it gives 10,560, where the rule gives 8,125.20 and head gives 9,325.20. The other order counts a self-paid Part B twice.
  • Fix: apply the C6 code. It takes the shared aggregate, removes the person-paid Part B, and adds the base Part B. With decomposed inputs it gives the head result. Append these cases (they fail at head and pass with the C6 code):
- name: California deducts premiums entered as total health insurance premiums.
  period: 2026
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: false
    ssi_marital_unearned_income: 12_000
    health_insurance_premiums: 1_200
    state_code: CA
  output:
    # ACWDL 20-24: (1,000 - 20 - 100) x 12 = 10,560.
    medicaid_optional_senior_or_disabled_countable_income: 10_560

- name: California keeps the Part B disregard when the MSP pays Part B and other premiums are entered as a total.
  period: 2026
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    social_security_retirement: 12_000
    health_insurance_premiums: 1_200
    state_code: CA
  output:
    msp_part_b_premium_coverage: 2_434.80
    # ACWDL 20-24: (1,000 - 20 - 202.90 - 100) x 12 = 8,125.20.
    medicaid_optional_senior_or_disabled_countable_income: 8_125.20

- name: California does not count a self-paid Part B premium twice when it is part of the total premium input.
  period: 2026
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    social_security_retirement: 22_200
    health_insurance_premiums: 3_634.80
    state_code: CA
  output:
    medicare_part_b_premium: 2_434.80
    # ACWDL 20-24: (1,850 - 20 - 202.90 - 100) x 12 = 18,325.20.
    medicaid_optional_senior_or_disabled_countable_income: 18_325.20
  • Our round-1 C3 sketch read only the decomposed input. C3 stays RESOLVED.

SA5. Microsimulation default: medicare_enrolled (Part A and/or Part B) stands in for Part B enrollment, so Part A-only enrollees get the Part B disregard.

  • Code: V/income/medicaid_optional_senior_or_disabled_countable_income.py:65-66. medicare_enrolled is computed (adds = ["takes_up_medicare_if_eligible"], documented as "Part A and/or Part B"). In the default dataset, take-up is a data column (94.3% of CA people aged 65 or older). The model has no Part B enrollment variable. The repo's Part B chain uses the same gate (medicare_part_b_premium, msp_part_b_premium_coverage, medical_expense_health_insurance_premiums), so the PR follows the convention.
  • Direction: the model gives the $2,434.80 Part B disregard (2026) to Part A-only enrollees. This overstates the deduction and therefore CA pathway eligibility.
  • Magnitude (2026, default dataset; a second run sets the base Part B premium to 0):
    • CA pathway eligibles: 2.20 million (weighted). Of these, 143,209 (11 records, 6.5%) depend on the Part B deduction, and 70,058 of them have no other Medicaid category.
    • KFF / CMS 2024: about 10% of CA Medicare beneficiaries have Part A only.
    • Estimated overstatement: about 14,000 people (about 0.65% of CA pathway eligibles), of whom about 7,000 have no other Medicaid. The base is 11 records, so the estimate can be off by a factor of about 2.
    • Offsetting bias (older than the PR): the dataset has no months_receiving_social_security_disability, so the microsimulation reaches is_medicare_eligible only at age 65. About 518,500 CA disabled beneficiaries under 65 get no Part B disregard, which understates about 12,000 people at the same rate. The net effect is close to zero, and its sign is uncertain.
  • The bias does not flip eligibility for a meaningful share of the population, so it is not critical. No code change is needed in this PR.
  • Fix:
    1. Keep the # NOTE: in the C6 code. If you do not apply C6, put the same NOTE above line 63.
    2. Open a tracked issue: a Part B enrollment input (or a take-up split), and an imputation of months_receiving_social_security_disability so that enrollees under 65 reach is_medicare_eligible. Cite the issue number in the PR body.

SA6. Missing edge case: no test pins the new zero floor.

  • Code: V/income/medicaid_optional_senior_or_disabled_countable_income.py:77-79. A mutation that removes max_(..., 0) passes all 41 cases in the two PR test files.
  • Fix: append this case to the income file. It passes at head and after C7. Without the floor it gives -4,474.80. The floor binds with either premium source alone, so the case stays a floor test after C6, SA4 and SA5. The C7 case does not detect a removed floor, so add both.
- name: California countable income does not go below zero when premiums exceed income.
  period: 2026
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    health_insurance_premiums_without_medicare_part_b: 3_000
    ssi_marital_unearned_income: 1_200
    state_code: CA
  output:
    # (100 - 20) x 12 = 960, less 2,434.80 Part B and 3,000 premiums, floored at 0.
    medicaid_optional_senior_or_disabled_countable_income: 0

SA7. Missing edge cases: no test pins the dated values that the PR wrote for MO (2015-2025), AR (2015-2016) and CA (2020-12-01).

  • MO: P/assets/limit/individual.yaml:101-111 and couple.yaml:101-111. The only MO cases are for 2026 and 2027. These mutations pass all 41 PR cases: the round-1 schedule (3,000 / 6,000 from 2015, so the SA2 fix undone); the 2017-07 step one year early; and the 2018-07, 2019-07, 2020-07, 2021-07, 2022-07 and 2025-07 steps one year late. The 2026 limit is not pinned at all.
  • AR: P/assets/limit/uses_msp_limit.yaml, AR 2015-01-01: true. A linkage that starts in 2016 or 2017 passes. The AR 2016 case (T asset file line 296) is a "false" case, and the omitted AR key resolves to 0 (G3).
  • CA: the flag's 2020-12-01 date. A flag that starts on 2022-01-01 passes, because the 2021 case (T income file line 233) has no Medicare.
  • Effect of each mutation: every AR applicant fails in 2015-2016; CA 2021 countable income changes by 1,782; a MO limit is wrong by a full step.
  • Fix: append to T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml. Each case passes at head and fails on the mutation that it pins.
- name: Missouri applies the 1,000-dollar individual resource limit in 2017.
  period: 2017
  input:
    age: 70
    ssi_countable_resources: 1_500
    state_code: MO
  output:
    # 13 CSR 40-8.020(4)(A): 1,000 before July 1, 2017. 1,500 is over it.
    is_optional_senior_or_disabled_asset_eligible: false

- name: Missouri couple at the 2018 couple resource limit is ineligible.
  period: 2018
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 2_000
      person2:
        age: 68
        ssi_countable_resources: 2_000
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: MO
  output:
    # 13 CSR 40-8.020(4)(B): ineligible "at or in excess of" the limit.
    # 4,000 combined is not under the 4,000 couple limit effective July 1, 2017.
    is_optional_senior_or_disabled_asset_eligible: [false, false]

- name: Missouri applies the July 2018 individual resource limit in 2019.
  period: 2019
  input:
    age: 70
    ssi_countable_resources: 2_500
    state_code: MO
  output:
    # 2,500 is under the 3,000 limit effective July 1, 2018.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2019 individual resource limit in 2020.
  period: 2020
  input:
    age: 70
    ssi_countable_resources: 3_500
    state_code: MO
  output:
    # 3,500 is under the 4,000 limit effective July 1, 2019.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2020 individual resource limit in 2021.
  period: 2021
  input:
    age: 70
    ssi_countable_resources: 4_500
    state_code: MO
  output:
    # 4,500 is under the 5,000 limit effective July 1, 2020.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2021 individual resource limit in 2022.
  period: 2022
  input:
    age: 70
    ssi_countable_resources: 5_020
    state_code: MO
  output:
    # 5,020 is under the 5,035 limit effective July 1, 2021 (IM-70).
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2022 individual resource limit in 2023.
  period: 2023
  input:
    age: 70
    ssi_countable_resources: 5_200
    state_code: MO
  output:
    # 5,200 is under the 5,301.85 limit effective July 1, 2022 (IM-77).
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2024 individual resource limit in 2025.
  period: 2025
  input:
    age: 70
    ssi_countable_resources: 5_800
    state_code: MO
  output:
    # 5,800 is under the 5,909.25 limit effective July 1, 2024.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri applies the July 2025 individual resource limit in 2026.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 6_000
    state_code: MO
  output:
    # 6,000 is under the 6,068.80 limit effective July 1, 2025.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Arkansas applicant under the 2015 Medicare Savings Program individual resource limit is eligible.
  period: 2015
  input:
    age: 70
    ssi_countable_resources: 7_000
    state_code: AR
  output:
    # 7,000 is under the 2015 Medicare Savings Program limit of 7,280.
    is_optional_senior_or_disabled_asset_eligible: true

Append to the income file:

- name: California deducts the 2021 Medicare Part B premium.
  period: 2021
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    ssi_marital_unearned_income: 12_000
    state_code: CA
  output:
    # The December 2020 premium deduction first applies in 2021 annual
    # calculations. The 2021 base premium is 148.50 a month.
    # (1,000 - 20 - 148.50) x 12 = 9,978.
    medicaid_optional_senior_or_disabled_countable_income: 9_978
  • After C6, the 2020-12-01 date moves to disregards_medicare_part_b_premium. The CA 2021 case reads that date.
  • These cases close the MO part of G7. Use 6,000 (true) for 2026, not 6,200. Optional, not run: a 2024 case under 5,726 would also pin the 2023-07 step.

SA8. Source conflict not registered: for a Missouri couple at exactly the limit, RSMo 208.010.2(7) and 13 CSR 40-8.020(4)(B) disagree.

  • Code: the deciding line is V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:44 (below_state_limit = assets < state_limit). The test is T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml:124-147. The PR cites both texts in P/assets/limit/couple.yaml:17-20.
  • RSMo 208.010.2(7): a married couple "may own resources not to exceed" the couple amount, so the limit itself qualifies. Subdivision 2(4) has had the same couple wording since 1998. 13 CSR 40-8.020(4)(B) (FILE #page=2) makes a household ineligible "at or in excess of" the limit. For a couple at the limit, no reading makes both texts true. RSMo 536.014(2) says that a rule in conflict with state law is not valid.
  • Round 1 read the statute as strict because 2(4) says "or more". That wording applies to individuals only. For individuals, the agency's strict reading is a defensible tie-break.
  • Repo rule (CLAUDE.md:148, CONTRIBUTING.md "Source conflicts", refined after round 1): a regulation that flatly contradicts its statute goes to the project lead, who decides it, and the ruling goes in the register. Every such choice is recorded and the deciding line is marked. The PR body says "Source conflicts: none registered".
  • Precedent: us-co:premium-assistance-ptc-recipients (a state statute against a state regulation) was ruled in October 2026 to follow the statute.
  • Probe: MO 2026 couple at 12,137.55 gives [false, false] at head and [true, true] with <= for MO couples. One cent over gives [false, false] in both. Individuals do not change. With <=, only the T:124 case fails.
  • Effect: only couples whose resources equal the limit to the cent. The microsimulation effect is about zero.
  • This is should-address now. It becomes critical if the project lead rules for the statute and the PR does not change.
  • Fix:
    1. Ask the project lead for a ruling. Add this entry to source_conflicts/candidates.yaml, between us-mo:medicaid-published-dollar-limit-rounding and us-mo:negative-federal-agi-combined-return. It passes policyengine_us/tests/code_health/test_source_conflicts.py on a copy. Change the PR body line to "Source conflicts: open, us-mo:mhabd-couple-resource-limit-boundary (project lead ruling requested)".
- id: us-mo:mhabd-couple-resource-limit-boundary
  program: medicaid
  years:
    from: 2015
    type: calendar
  question: Is a Missouri aged, blind or disabled couple whose countable resources equal the couple limit eligible,
    as the statute's "not to exceed" says, or ineligible, as the rule's "at or in excess of" says?
  sources:
  - kind: statute
    citation: RSMo 208.010.2(7) (208.010.2(4) before fiscal year 2018)
    url: https://revisor.mo.gov/main/OneSection.aspx?section=208.010
  - kind: regulation
    citation: 13 CSR 40-8.020(4)(A)1.B and (4)(B)
    url: https://www.sos.mo.gov/CMSImages/AdRules/csr/current/13csr/13c40-8.pdf#page=2
  readings:
    statute: A married couple living together "may own resources not to exceed" the couple amount, so resources
      equal to the limit qualify.
    other: A household with countable assets "at or in excess of" the limit is ineligible.
  example: A 2026 Missouri couple with $12,137.55 of countable resources is eligible under the statute and ineligible
    under the rule.
  pe_us:
    follows: conflicting-source
    code:
    - policyengine_us/variables/gov/hhs/medicaid/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py
    prs:
    - https://github.com/PolicyEngine/policyengine-us/pull/10364
  state: open
  note: The project lead decides whether the rule's strict test, which contradicts the statute's couple wording,
    is followed (RSMo 536.014 voids a rule in conflict with state law).
  1. If the lead rules for the statute: add P/assets/limit/couple_limit_inclusive.yaml (bool by state, all 51 keys 2015-01-01: false except MO: 2015-01-01: true, reference RSMo 208.010.2(7)), as in P/income/limit/requires_income_below_limit.yaml. Replace lines 44-45 with the code below. Rename T:124 to "Missouri couple at the couple resource limit is eligible.", set the output to [true, true], and add the same household at 6_137.56 with [false, false].
        # RSMo 208.010.2(7): a married couple "may own resources not to
        # exceed" the couple amount.
        couple_may_equal_limit = is_couple & p.assets.limit.couple_limit_inclusive[
            state
        ].astype(bool)
        within_state_limit = where(
            couple_may_equal_limit, assets <= state_limit, assets < state_limit
        )
        return where(uses_msp_limit, within_msp_limit, within_state_limit)
  1. If the lead keeps the rule: no code change. In rulespec-us, add the entry with follows set to the regulation, under proposed until the PR merges.
  2. In both cases, after the entry is on rulespec-us main: run python scripts/update_source_conflicts.py --commit <sha>, put # Source conflict us-mo:mhabd-couple-resource-limit-boundary alone on the line directly above the deciding line, and delete the backlog entry. A marker added earlier fails the guard. Round 1 suggested the form MO:<id>; the guard needs us-mo:<id>.

Suggestions

  • G12. The base Part B premium has no value before 2021. policyengine_us/parameters/gov/hhs/medicare/part_b/base_premium.yaml starts on 2021-01-01, so core returns $148.50 for 2015-2020. At baseline the flag masks this. After C6, a person-paid Part B before 12/2020 reads medicare_part_b_premium, which uses this value. That affects only MSP-ineligible enrollees, whose income is already over the pre-12/2020 limit. A reform that turns on the CA Part B disregard for 2020 deducts 1,782 a year, but ACWDL 20-24 Example 1 uses $144.60 a month. Fix: add the 2020 value to the federal file with the CMS 2020 fact sheet, or add a comment to the flag.
  • G13. Document the Example 5 decline in the code. The PR body says that the non-applicant spouse's premium is "Not modeled", but the code has no note. Add above line 63: # NOTE: ACWDL 20-24 Example 5 also deducts a non-applicant spouse's premiums. Spousal income here follows SSI deeming (20 CFR 416.1163), so that premium is not modeled. Optional: pin Example 5 at 2026 premiums. Applicant 70 with Medicare and ssi_unearned_income: 18_000; spouse 60 with ssi_unearned_income: 12_000, employment_income: 8_400 and health_insurance_premiums_without_medicare_part_b: 1_200. The model gives deemed income [15_810, 0], countable income [31_135.2, 0] and is_optional_senior_or_disabled_income_eligible [false, false], which is the letter's result. The case passes at head and after C7.
  • G14. Show that a state other than CA deducts no premium. The non-CA cases detect a premium step that runs for every state only because people aged 65 or older are Medicare enrollees by default. Append this case (it passes at head; with the step on for every state it gives 8,125.20):
- name: North Carolina does not deduct Medicare Part B or other health insurance premiums.
  period: 2026
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    health_insurance_premiums_without_medicare_part_b: 1_200
    ssi_marital_unearned_income: 12_000
    state_code: NC
  output:
    # Only California deducts premiums: (1,000 - 20) x 12 = 11,760.
    medicaid_optional_senior_or_disabled_countable_income: 11_760

Out of scope / follow-up (pre-existing, not caused by this PR):

  • CT couple disregard: the model pools it, but UPM 5030.15 A applies it to each member. UPM 5030.15 A subtracts the disregard from "the unit member's" own unearned income, and UPM 5045.10 E sums the members' results. The model subtracts 2 x 562 = 1,124 once from the couple's combined unearned income (V/income/_apply_medicaid_optional_senior_or_disabled_exclusions.py:18-21). It deducts up to $562 a month too much when one spouse has less than $562. Probe, CT 2026 couple: 21,400 + 0 gives 3,956 each and eligible in the model, but 7,328 each and ineligible under the UPM. The formula is older than the PR, and the PR's 1,124 is correct. But the comment at P/income/disregard/couple.yaml:45-47 (our round-1 G4 text) and the test name at T income file line 184 describe a per-member rule that the code does not apply. CONTRIBUTING.md:126 requires a fix or an issue. Fix: open an issue, then replace the comment with:
  # UPM 5030.15 A subtracts the individual disregard from each unit member's
  # own unearned income. The model subtracts this couple value (2 x the
  # individual standard from 2024) from the couple's combined unearned income,
  # so it deducts too much when a spouse has less unearned income than the
  # individual disregard (issue #NNNN). It rises each January 1.

Rename the T:184 case to "Connecticut couple with each spouse above the individual disregard gets the full 2026 couple disregard." A per-member fix (the sum over members of min(own monthly unearned income, 562)) ran on a copy: 433 Medicaid tests pass, and 18,000 + 3,600 gives 5,628 each. Use a by-state bool parameter, not a state string.

  • Carried from round 1: AR ARSeniors is aged-only; the asset files have different period metadata; the KFF titles in P/assets/limit/couple.yaml:9-10 and P/income/limit/{individual,couple}.yaml:9; an inclusive default operator with a strict switch for MO; ParameterNotFoundError before 2015-01-01; the CT 2023 income limits were not checked; the CT couple values before 2024 (PR body); issue #9734; the CA asset limit change on 2027-07-01.
  • The PR body lists as "Not modeled": CA's whole-dollar rounding of countable income (at most $0.50 a month) and CA's Maintenance Need Allowance method for spouses.

Investigated and cleared:

  • Whole-cent rounding: legally neutral. It changes a result only within half a cent of a fractional limit, which only MO has. The readers is_optional_senior_or_disabled_for_medicaid and is_209b_ssi_recipient_for_medicaid pass.
  • Premium step outside CA: a 51-state comparison with and without the step differs only in CA rows from 2021. The cost is negligible, and there is no dependency cycle.
  • ACWDL 20-24 Examples 2 and 5: the model gives the letter's eligibility result.
  • CA partner case: $1,110 a month in 2026, still eligible.
  • pre_tax_health_insurance_premiums: already removed from SSI wages, so adding them would count them twice.
  • CPS premiums do not appear to include Part B, so the step does not count Part B twice.

PDF Audit Summary

Category Count
Confirmed correct 34 of 35 references and values checked (22 MO dated values, OK C-1 2015 and 2023, OAC 2001, ACWDL 20-24 anchors, 42 USC 1396d(p), quotes in tests)
Mismatches (code-path confirmed + visually verified) 1: CA flag 2015-01-01: false (C6)
Not checked 1: the 50 other keys of the new flag (no source cited; outside the six PR states)
Unmodeled items 2: ACWDL 20-24 Example 5 spouse premium (G13); CA whole-dollar rounding
Pre-existing issues 2: CA $20 before 12/2020 (G11); CT pooled couple disregard (follow-up)

Validation Summary

Check Result
Regulatory Accuracy 2 critical (C6, C7); 1 should address (SA8)
Reference Quality 34 of 35 correct (the wrong one is C6). 1 suggestion (G5, partial)
Code Patterns 2 should address (SA4; SA5, microsimulation default); 1 suggestion (G12). No hard-coded values
Formatting (params & vars) 0 issues
Test Coverage 2 should address (SA6, SA7); 2 suggestions (G13, G14) and the G7 remainder. The PR tests kill 7 of 14 mutations; the proposed cases kill the other 7
PDF Value Audit 1 mismatch (C6); 34 confirmed
CI Status Passing: 32 of 32 checks
Local tests Medicaid eligibility/categories 255 passed; Medicaid income 178 passed; Medicare 301 passed
Partner contract tests No partner file touched. 70 healthcare partner tests pass at head and on the C6 and C7 fix copies
Branch 3 ahead, 31 behind main; MERGEABLE
Other reviews None

Review Severity: REQUEST_CHANGES

Next Steps

  1. Suggested order: C6 with SA4 (one code edit at lines 63-75, the flag split and 5 cases). Then C7 (lines 76-97) and SA6. Then SA7. Ask the project lead about SA8 now, because the ruling can come in parallel. SA5 needs only the NOTE (already in the C6 code) and an issue.
  2. The C6 and C7 edits were each verified alone. After you apply both, run policyengine-core test policyengine_us/tests/policy/baseline/gov/hhs/medicaid -c policyengine_us and the Medicare tests.
  3. C6 and C7 change CA countable income for some households. Run the partner tests under policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/healthcare again. Do not edit a partner file without the partner review gate.
  4. Open the follow-up issues and cite their numbers in the PR body: the CA rules before 12/2020 (G11, and Part B if you take the simpler C6 variant); Part B enrollment and SSDI months (SA5); the CT per-member disregard (Out of scope).
  5. Process note, not counted: the PR body still has no axiom: line (round-1 Next Steps 4). The repo CLAUDE.md section "Axiom Parity (required for policy changes)" asks for one line on every policy change: axiom: <legal id> encoded-correct | <rulespec PR> encoded | <rulespec issue> queued | n/a: <reason>. An external contributor who cannot do the Axiom work uses axiom: needed.
  6. To auto-fix issues: run the fix-pr workflow for this PR.

…-2020 CA disregard, MO couple limit

- Split the CA flag: person-paid health insurance premiums from 2015
  (22 CCR 50555.2, MC 176 line 17); full Part B disregard from 2020-12-01
  (ACWDL 20-18, 20-24), read from the shared premium aggregate.
- Floor countable income after adding deemed spouse income.
- Before 12/2020, CA applies the $20 deduction and then the $230/$310
  disregard (ACWDL 00-57, MC 176 lines 8 and 16).
- Missouri couples at the couple limit qualify (RSMo 208.010.2(7));
  candidate us-mo:mhabd-couple-resource-limit-boundary, ruling pending.
- Tests pinning MO 2017-2026, AR 2015, CA 2020/2021 dates, the floor,
  direct premium input and an NC no-deduction case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450

hua7450 commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

@MaxGhenis, as project lead, could you rule on a statute-against-regulation conflict? Under CONTRIBUTING.md#source-conflicts, a regulation that flatly contradicts its statute goes to you.

Question. A Missouri aged, blind or disabled couple's countable resources equal the couple limit exactly. Are they eligible?

  • Statute: RSMo 208.010.2(7) says a married couple living together "may own resources not to exceed" the couple amount. Resources equal to the limit qualify. Subdivision 2(4) has used the same couple wording since 1998.
  • Regulation: 13 CSR 40-8.020(4)(B) makes a household ineligible "at or in excess of" the limit.
  • For a couple at the limit, no reading makes both texts true. RSMo 536.014(2) says a rule that conflicts with state law is not valid.

Example. In 2026, a couple with $12,137.55 of countable resources is eligible under the statute and ineligible under the rule.

What the PR does now. It follows the statute for couples (<=, flag couple_limit_inclusive, MO true from 2015) until you rule. Individuals keep the strict test. The individual clause ("or more") supports the agency's reading. The candidate is us-mo:mhabd-couple-resource-limit-boundary in source_conflicts/candidates.yaml, with state: open and follows: statute.

Precedent. us-co:premium-assistance-ptc-recipients, a state statute against a state regulation, is recorded in source_conflicts/candidates.yaml as ruled in October 2026 to follow the statute.

After your ruling, the entry goes into the rulespec-us register, and the # Source conflict marker goes on the deciding line. If you rule for the regulation, I will revert the couple flag to the strict test. The microsimulation effect either way is about zero: it reaches only couples whose resources equal the limit to the cent.

@hua7450

hua7450 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator Author

Thanks, @PavelMakarchuk. Round 2 is applied in d038f14. Item by item:

Critical

  • C6: done, as written.
    • deducts_health_insurance_premiums is CA true from 2015. Its references are 22 CCR 50555.2, MC 176 line 17 and ACWDL 20-24 pp. 2-3.
    • The new disregards_medicare_part_b_premium is CA from 2020-12-01. Its references are ACWDL 20-18 pp. 1-2 and 20-24 pp. 3-8.
    • The premium code is yours: the shared aggregate, minus person-paid Part B, plus base Part B, with the SA5 NOTE.
    • One value differs from your case: the 2019 case expects $7,200, not $7,440. G11 is now encoded (below), so MC 176 line 8 takes the $20 off first: (1,000 − 20 − 230 − 150) × 12 = 7,200.
    • The CA 2020 case comment is updated. Its value is now $9,000, because of G11.
  • C7: done. The floor comes after the deemed spouse income is added, using your code. Your case is in, with the eligibility output in a copy of the case that has no error margin. The premiums and the G11 disregard come off together as one deductions amount.

Should address

  • SA4: done through the C6 code. All three of your cases are in ($10,560, $8,125.20 and $18,325.20). I did not use where(direct != 0, ...).
  • SA5: the NOTE is in the code. I opened issue Medicare: add a Part B enrollment input and impute SSDI months so Part B rules reach the right people #10457 for a Part B enrollment input (or a take-up split) and an SSDI-months imputation. It links issue Medicare eligibility: add the ESRD and ALS routes; document the SSDI-months input #9396, which covers the input's documentation and the ESRD and ALS routes.
  • SA6: done. The floor case is in.
  • SA7: done, with two changes:
    • The 2018 Missouri couple case now expects eligible at $4,000, because of SA8 below.
    • I added a 2024 case ($5,500 under $5,726) for the 2023-07 step. Every Missouri dated value from 2017 to 2027 now has a case.
    • The AR 2015 and CA 2021 ($9,978) cases are in.
  • SA8: applied the statute reading for couples, pending your sign-off.
    • couple_limit_inclusive covers all 51 keys, MO true from 2015. Missouri couples use <=, and individuals keep <.
    • The 2026 couple at $12,137.55 is now eligible, and a new case at $12,137.56 is ineligible.
    • The us-mo:mhabd-couple-resource-limit-boundary candidate is in candidates.yaml with follows: statute and state: open.
    • The sign-off request is in the comment above (it replaces the earlier ruling request). The register entry and the # Source conflict marker wait for your answer and the rulespec-us main entry. If you choose the regulation, I will revert the couple flag.

Suggestions

  • G5:
    • The OK C-1 now points to a Wayback copy of the 7/1/2026 edition, captured 2026-09-03, with Schedule VI on page 2.
    • The 10/2026 Appendix J, the AR 2026 chart and the CHOICES guide of 5/29/2026 have no Wayback copy. The body lists that under "Not confirmed".
  • G7 remainder:
    • Added an SC couple one dollar over the MSP couple limit ($14,911, ineligible). That is the one new class.
    • I left out the AR couple at the limit, the SC couple at the limit and the OK individual at $9,950. They repeat classes already pinned by the OK couple at $14,910 and the AR and SC individuals at $9,950, and we keep one case per class.
  • G11: now in scope and encoded.
    • The CA disregard value is the $20 deduction in every year.
    • The new additional_disregard.{individual,couple} ($230 / $310, CA until 2020-12-01) comes off after the SSI exclusions, as on MC 176 line 16. Its references are ACWDL 00-57 pp. 1 and 3.
    • The unused $20 still moves to earned income before the half, as on line 12.
    • New cases: 2020 earned income ($2,730) and a 2020 couple ($9,720 each). The old placement of $230 in place of the $20 also let the unused $230 shift to earned income. That would have given $4,230 for the earned case.
    • Side effect: an applicant with an ineligible spouse before 12/2020 now gets the individual $230 after deeming. The old code effectively gave the couple's $310, because the deemed amount used the couple disregard. MC 176 line 16 gives $230 to a qualified individual.
  • G12: I took the comment option, as a NOTE in the premium code. Adding 2015-2020 values to gov.hhs.medicare.part_b.base_premium changes every Part B consumer before 2021 (the MSP, SNAP and SPM medical expenses). That belongs in its own PR.
  • G13: done. The NOTE is in, plus the Example 5 case (deemed $15,810; countable $31,135.20).
  • G14: done. The NC case is in.

Out of scope

Not run locally, per our practice. CI will run the suites.

Partner case: the California partner senior is still $1,110 a month in 2026. The pre-2021 changes do not reach it, and no partner file changes.

Axiom line: the pe-parity issue for this head is being drafted. The body line follows.

@hua7450

hua7450 commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

@PavelMakarchuk Since you're handling these Medicaid PRs, could you sign off on the Missouri reading in SA8 (us-mo:mhabd-couple-resource-limit-boundary)?

  • RSMo 208.010.2(7): a married couple "may own resources not to exceed" the couple amount, so a couple exactly at the limit is eligible.
  • 13 CSR 40-8.020(4)(B): a household "at or in excess of" the limit is ineligible.
  • The PR follows the statute for couples (couple_limit_inclusive, MO true from 2015), as in the Colorado ruling us-co:premium-assistance-ptc-recipients. Individuals keep the strict test.

A one-word answer is enough: "statute" or "regulation". This replaces the ruling request above.

…y-values

# Conflicts:
#	source_conflicts/candidates.yaml
- New by-state flag individual_limit_inclusive (CA) and CA true in
  couple_limit_inclusive: resources may not exceed the limit (22 CCR
  50420(a), WIC 14005.40(c)(5), 42 USC 1396a(m)(1)(C)).
- CA asset limit $130,000 / $195,000 from 2022-07-01 (ACWDL 21-31),
  which the ABD pathway lacked.
- Tests: CA 2026 single at and one cent over the limit, a couple at
  $195,000, and 2023 at $130,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450

hua7450 commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator Author

@PavelMakarchuk, one more fix since your re-review, in e3bc1ef:

  • California resources may equal the limit. At 0533ef8 the ABD asset test gave California a strict <, so a person at exactly $130,000 in 2026 was ineligible. WIC 14005.40(c)(5) ("do not exceed the maximum levels"), 22 CCR 50420(a) ("The property reserve shall not exceed the following limits") and 42 U.S.C. 1396a(m)(1)(C) ("do not exceed") are all inclusive.
    • A new by-state flag, individual_limit_inclusive (CA), joins couple_limit_inclusive, which is now CA and MO. Missouri individuals keep <.
    • There is no source conflict, so there is no candidate entry.
  • California's July 2022 step. The ABD asset files lacked the $130,000 / $195,000 limit from 2022-07-01 (ACWDL 21-31). The MSP and medically needy files already had it, and now these do too.
  • Tests: CA 2026 at $130,000 (eligible) and $130,000.01 (ineligible), a couple at $195,000, and 2023 at $130,000.
  • Follow-up issue Medicaid aged, blind and disabled pathway: let resources equal the state asset limit unless a state source says otherwise #10466: check the other states' wording at the limit and default the comparison to <=.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450 added a commit to hua7450/policyengine-us that referenced this pull request Oct 27, 2026
PolicyEngine#10364 adds the same California 2022-07-01 rows, ACWDL 21-31 reference
and 2023 case to the senior_or_disabled asset files. Remove them here to
avoid duplicate edits; keep the 2027 January 1 case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Minn. Stat. 256B.056, subd. 1b (since 2001): the SSI $20 general income
disregard "is included in the standard and shall not be allowed as a
deduction from income" for aged, blind and disabled people. MN disregard
is 0 from 2015, so none moves to earned income; the $65 and one-half
earned exclusions still apply. Tests at and $1 over the 2026 limit, and
an earned-income case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450

hua7450 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

@PavelMakarchuk, one more small addition in 60ef70c: Minnesota's ABD disregard is now $0, because Minn. Stat. 256B.056, subd. 1b says the SSI $20 "is included in the standard and shall not be allowed as a deduction from income". It has cases at and $1 over the 2026 limit, plus an earned-income case. No re-review needed beyond your current pass.

@hua7450

hua7450 commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Ruling on us-mo:mhabd-couple-resource-limit-boundary: statute. I'm the project lead for these Medicaid fixes, so this settles it, and it replaces the sign-off requests above.

  • RSMo 208.010.2(7) lets a married couple "own resources not to exceed" the couple amount, so a couple exactly at the limit is eligible.
  • It governs over 13 CSR 40-8.020(4)(B) ("at or in excess of").
  • The PR already follows the statute for couples (couple_limit_inclusive, MO true from 2015), so the model needs no change.

The candidate will record this comment as the ruling. The register entry and the code marker follow the merge.

hua7450 and others added 3 commits October 27, 2026 15:17
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main's CI fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program Review

This is round 3 of the review of PR #10364 (author hua7450). It covers the author's five commits since round 2 (d038f14d37, e3bc1ef97a, d3314169a7, 60ef70cb9c, 216aa268a1) and every round-2 finding. The merges of main change no PR variable, parameter or test file. IDs: C = critical, SA = should address, G = suggestion. Round-2 IDs (C1-C7, SA1-SA8, G1-G14) keep their numbers. New findings start at C8, SA9 and G15.

Paths: P/ = policyengine_us/parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/, V/ = policyengine_us/variables/gov/hhs/medicaid/, T/ = policyengine_us/tests/policy/baseline/gov/hhs/medicaid/. "T asset file" = T/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.yaml; "T income file" = T/income/medicaid_optional_senior_or_disabled_countable_income.yaml. Line numbers refer to the reviewed head.

Source Documents

Branch Status

The branch is 11 commits ahead and 6 behind main. GitHub reports it as MERGEABLE, blocked only by the requested changes. The merges of main since round 2 change no PR variable, parameter or test file. At 60ef70cb9c, a test merge with main was clean, and 482 Medicaid cases passed on it. The staleness did not affect any finding.

What round 3 fixed

The author applied the round-2 fixes as proposed, apart from two defensible declines (G5, G7 remainder), and added three correct changes. Thank you.

  • C6, C7, SA4, SA6: the flag split, the shared premium aggregate with the Part B swap, and the floor after the deemed income are in, with all proposed cases. The 2019 case expects 7,200, not our 7,440. That is right: MC 176 line 8 takes the $20 first, and our sketch left line 8 out.
  • SA5, G12, G13: the three NOTEs are in. Issue #10457 tracks Part B enrollment and SSDI months.
  • SA7, G14: one Missouri case for each dated value from 2017 to 2027, plus the AR 2015, CA 2021 and NC cases.
  • G11: now encoded, in the MC 176 order. The side effect that the author flagged (an applicant whose spouse is not aged or disabled gets $230, not $310) is correct.
  • California assets: resources equal to the limit qualify (22 CCR 50420(a), WIC 14005.40(c)(5)), and the $130,000 / $195,000 step of 2022-07-01 is in (ACWDL 21-31). Both are correct.
  • Minnesota: the $0 disregard follows Minn. Stat. 256B.056, subd. 1b and the DHS manual.
  • SA8: couples follow the statute. The candidate is in the backlog and records the ruling for couples.
  • References: 16 values and 15 parameter references checked from page images. All are correct, except the Missouri items in C8.
  • CI: 33 of 33 checks pass. The PR's three test files hold 105 cases.

Two new critical items come from our own round-2 text. C9 is a gap in our round-2 C6 code, which the author applied word for word. C8 follows our round-2 note that the strict test for Missouri individuals was "a defensible tie-break". That note was wrong from fiscal year 2018. Neither item is an author error.

Round-2 status

ID Status Evidence
C1 RESOLVED (code); pin lost Whole-cent rounding at V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:22. With <= for MO couples, no case detects its removal (SA9a).
C2 RESOLVED <= for the MSP branch (line 45). The AR, OK and SC at-limit cases are unchanged.
C3 RESOLVED Replaced by the C6 and C7 code.
C4 RESOLVED The MO references and dated values are unchanged.
C5 RESOLVED The OK C-1 2015 and 2023 editions and OAC 2001 are unchanged.
C6 RESOLVED P/income/deducts_health_insurance_premiums.yaml:24-26 (CA true from 2015) and P/income/disregards_medicare_part_b_premium.yaml:22-27 (CA from 2020-12-01). All five references corroborate. Probe: 7,200 in 2015, 2019 and 2020, and 9,960 in 2021, as MC 176 gives. New gap in the same code: C9.
C7 RESOLVED Floor after the deemed income at V/income/medicaid_optional_senior_or_disabled_countable_income.py:119-123. Cases at T income file lines 398 and 426 give [28,765.20, 0] and eligible. A floor before the deemed income fails them.
SA1 RESOLVED The 13 CSR href is still #page=2.
SA2 RESOLVED Values unchanged, and SA7 now pins them.
SA3 RESOLVED The couple and back-year MSP cases are unchanged.
SA4 RESOLVED The shared aggregate at line 80. Cases at T income file lines 357, 370 and 384 give 10,560, 8,125.20 and 18,325.20. The direct and decomposed inputs now give the same result in the round-2 flip window.
SA5 RESOLVED NOTE at lines 72-73. Issue #10457 is open and cited in the body.
SA6 RESOLVED Floor case at T income file line 450. The couple-branch floor is not pinned (G15).
SA7 RESOLVED MO cases at T asset file lines 352-458, AR 2015 at line 468, CA 2021 at T income file line 343. Every round-2 date mutation now fails a case. The 2018 couple at $4,000 now expects eligible, as the ruling requires.
SA8 PARTIAL Couples use <= through P/assets/limit/couple_limit_inclusive.yaml. The candidate at source_conflicts/candidates.yaml:3732 has state: ruled. Open: individuals (C8), the candidate text and the register steps (SA8 below).
G1 RESOLVED The January 1 comments are kept, also in the new files.
G2 RESOLVED No change.
G3 RESOLVED No change.
G4 RESOLVED The CT comment now matches the code and cites issue #10458.
G5 PARTIAL (defensible) The OK C-1 7/1/2026 now cites a Wayback copy, which is byte-identical to the copy we read. The 10/2026 Appendix J, the AR 2026 chart and the CHOICES guide of 5/29/2026 still have no capture, and the body says so. Anyone can request a capture with Save Page Now, so this stays a suggestion.
G6 RESOLVED No change.
G7 RESOLVED; remainder DECLINED (defensible) SC couple at $14,911 (T asset file line 478). The declined cases repeat classes that the AR and SC individuals at $9,950 and the OK couple at $14,910 already pin.
G8 RESOLVED No change.
G9 RESOLVED Every new case name ends with a period.
G10 RESOLVED No change.
G11 RESOLVED CA disregard is 20 in all years (MC 176 line 8). additional_disregard is 230 / 310 until 2020-12-01 and comes off after the exclusions and after deeming (MC 176 line 16; lines 97-105). Probes give 9,000, 2,730 and 10,020 each, as MC 176 does. The $230 for an applicant with an ineligible spouse is correct; its pin is SA10b.
G12 RESOLVED (comment option, as offered) NOTE at lines 74-75. The pre-2021 read is $13.00 a month too high in 2019 and $3.90 in 2020, only for enrollees who pay their own Part B.
G13 RESOLVED NOTE at lines 76-78. Example 5 case at T income file line 463 (deemed 15,810; countable 31,135.20). It asserts no eligibility (G16).
G14 RESOLVED NC case at T income file line 494.
CT per member (round-2 out of scope) RESOLVED (tracked) Issue #10458 matches our analysis. The comment at P/income/disregard/couple.yaml and the case name at T income file line 184 are corrected.
Round-2 Next Steps 5 RESOLVED (process note) The body has axiom: TheAxiomFoundation/rulespec-us#1933 queued, and the issue is open.

Ruling on SA8

Ruling: statute. RSMo 208.010.2(7) "not to exceed" governs. Couples are inclusive from 2015; individuals are inclusive from July 1, 2017 (fiscal year 2018). Record this comment as the ruling in the register.

The basis:

  • 208.010.2(7), enacted by H.B. 1565 (2016), applies to MO HealthNet aged, blind and disabled claimants "starting in fiscal year 2018". An individual "owns or possesses resources not to exceed" the individual amount, and a couple "may own resources not to exceed" the couple amount. The section calls both "These resource limits", so both have one boundary.
  • H.B. 1565 also added these claimants to the exception in 208.010.2(4): "the provision of this subsection shall not apply". So the "or more" individual clause in (4) does not reach them after August 28, 2016.
  • Fiscal year 2018 began July 1, 2017 (Mo. Const. art. IV, sec. 23). 13 CSR 40-8.020 took effect the same day. RSMo 536.014(2) says a rule "in conflict with state law" is not valid.
  • Before July 1, 2017 there was no conflict. 208.010.2(4) (to August 27, 2016) and 13 CSR 40-2.030(12), 11/30/15 edition (to June 30, 2017), made a single person ineligible at "one thousand dollars ($1000) or more" and a couple only when resources "exceeds a value of two thousand dollars". The head already gives that result for 2015-2017.
  • This matches the Colorado ruling, us-co:premium-assistance-ptc-recipients.

The ruling commit 216aa268a1. It edits only source_conflicts/candidates.yaml:3732-3765. For couples it matches the ruling: follows: statute, couples inclusive from 2015, state: ruled, the couple flag file in pe_us.code, and no personal name in the note. For individuals it does not: the note says "Individuals keep the strict test" and "the model needs no change". The note and the PR body also cite comment 6451309562 as the ruling record. That comment was posted from the author's account and covers couples only. This review is the ruling record. Still missing: the individual flag from 2017-07-01 and its cases (C8), the Missouri references on both flags (C8), the candidate text, the rulespec-us entry under proposed, and, after the merge, the markers (SA8).

Verified YAML. It ran on a copy of head with the C8 variable comment: the C8 cases and the three PR test files pass, the 750 Medicaid baseline cases pass, and the parameter-key and reference-URL code-health tests pass (114). A # after a space starts a YAML comment, so the titles with #page are quoted, as in the PR's other files.

In P/assets/limit/individual_limit_inclusive.yaml, put these four entries first in reference, and replace the MO block (lines 65-66):

    - title: Missouri Revised Statutes 208.010.2(7)
      href: https://revisor.mo.gov/main/OneSection.aspx?section=208.010
    - title: Missouri Revised Statutes 208.010.2(4) (version effective October 11, 2013 to August 28, 2016)
      href: https://revisor.mo.gov/main/OneSection.aspx?section=208.010&bid=10947
    - title: "Missouri H.B. 1565 (2016), truly agreed and finally passed, section 208.010.2(4) and (7) #page 3-4"
      href: https://house.mo.gov/billtracking/bills161/billpdf/truly/HB1565T.PDF#page=3
    - title: "13 Missouri Code of State Regulations 40-2.030(12) (11/30/15 edition) #page 9-10"
      href: https://web.archive.org/web/20170107023023/http://s1.sos.mo.gov/cmsimages/adrules/csr/current/13csr/13c40-2.pdf#page=9
MO:
  # Before fiscal year 2018, a single claimant with resources of 1,000 "or
  # more" is ineligible: RSMo 208.010.2(4) before August 28, 2016, and
  # 13 CSR 40-2.030(12) (11/30/15 edition) until June 30, 2017.
  2015-01-01: false
  # From fiscal year 2018 (July 1, 2017), a claimant "owns or possesses
  # resources not to exceed" the individual amount (RSMo 208.010.2(7)).
  # Annual calculations read January 1, so this value first applies in 2018.
  2017-07-01: true

In P/assets/limit/couple_limit_inclusive.yaml, insert the last three entries above (2013 version, H.B. 1565, 13 CSR 40-2.030(12)) after the existing 208.010.2(7) entry at lines 9-10, and replace the MO block (lines 67-70):

MO:
  # A married claimant living with a spouse "may own resources not to exceed"
  # the couple amount: RSMo 208.010.2(4) before August 28, 2016, and
  # 208.010.2(7) from fiscal year 2018 (July 1, 2017). In between,
  # 13 CSR 40-2.030(12) (11/30/15 edition) made a couple ineligible only when
  # resources "exceed[] a value of two thousand dollars".
  2015-01-01: true

Critical (Must Fix)

C8. Regulatory mismatch: from fiscal year 2018, Missouri individuals keep the strict test, but RSMo 208.010.2(7) lets an individual's resources equal the limit. The Missouri flags also lack references for 2015-2017.

  • Code:
    • P/assets/limit/individual_limit_inclusive.yaml:65-66: MO 2015-01-01: false, with no comment and no Missouri reference. V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:49-56 then uses assets < state_limit for a Missouri individual.
    • P/assets/limit/couple_limit_inclusive.yaml:9-10, 67-70: the only Missouri reference is 208.010.2(7) at the current page, which supports 2018 on. The comment "(208.010.2(4) before fiscal year 2018)" is wrong from August 28, 2016 to June 30, 2017, when only 13 CSR 40-2.030(12) applied.
  • Sources: see "Ruling on SA8".
  • The test proves a flip. At head, a 2018 individual with $2,000 and a 2026 individual with $6,068.80 are ineligible. Under the ruled reading, both are eligible.
  • Why critical: the head value contradicts the ruled source, and a test shows the flip. Round 2 said that SA8 "becomes critical if the project lead rules for the statute and the PR does not change". A non-default dated value also needs a reference that supports it.
  • Effect: only individuals whose resources equal the limit to the cent. The microsimulation effect is about zero.
  • Fix:
    1. Apply the two flag blocks and the references in "Ruling on SA8".
    2. Replace the comment at V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:46-48:
        # RSMo 208.010.2(7): a Missouri claimant "owns or possesses resources
        # not to exceed" the individual amount, and a married couple "may own
        # resources not to exceed" the couple amount. California resources may
        # not exceed the limit (22 CCR 50420(a); WIC 14005.40(c)(5)).
  1. Append these cases to the T asset file. At head, the two "eligible" individual cases fail. With the fix, all five pass. The 2017 cases pin the years before the conflict: "inclusive from 2015" fails the first, and "couples strict in 2017" fails the second.
- name: Missouri individual at the 1,000-dollar limit is ineligible in 2017.
  period: 2017
  input:
    age: 70
    ssi_countable_resources: 1_000
    state_code: MO
  output:
    # 13 CSR 40-2.030(12) (11/30/15 edition): resources of 1,000 "or more"
    # make a single person ineligible before July 1, 2017.
    is_optional_senior_or_disabled_asset_eligible: false

- name: Missouri couple at the 2,000-dollar couple limit is eligible in 2017.
  period: 2017
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 1_000
      person2:
        age: 68
        ssi_countable_resources: 1_000
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: MO
  output:
    # 13 CSR 40-2.030(12) (11/30/15 edition): a couple is ineligible only when
    # resources "exceed[] a value of two thousand dollars".
    is_optional_senior_or_disabled_asset_eligible: [true, true]

- name: Missouri individual at the July 2017 individual resource limit is eligible in 2018.
  period: 2018
  input:
    age: 70
    ssi_countable_resources: 2_000
    state_code: MO
  output:
    # RSMo 208.010.2(7), from fiscal year 2018: resources "not to exceed"
    # 2,000.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri individual at the 2026 individual resource limit is eligible.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 6_068.8
    state_code: MO
  output:
    # RSMo 208.010.2(7): resources "not to exceed" the individual amount.
    is_optional_senior_or_disabled_asset_eligible: true

- name: Missouri individual one cent over the 2026 individual resource limit is ineligible.
  period: 2026
  input:
    age: 70
    ssi_countable_resources: 6_068.81
    state_code: MO
  output:
    is_optional_senior_or_disabled_asset_eligible: false
  1. Correct the PR body and issue #10466 (SA8, step 4).

C9. Regulatory mismatch: from 12/2020, the Part B swap drops the income-related amount (IRMAA) that the person pays, so eligibility can flip.

  • Code: V/income/medicaid_optional_senior_or_disabled_countable_income.py:81-88. paid_part_b is the net medicare_part_b_premium, which includes IRMAA. The swap max_(paid_premiums - paid_part_b, 0) + base_part_b removes all of it and adds back only the base premium.
  • Sources:
    • ACWDL 20-18 (FILE #page=2): counties disregard "the amount of the individual's Medicare Part B premium ... regardless of the state's payment of the premium". The letter removes the payment condition. It does not cap the amount at the standard premium. Its examples use $144.60 because those people have no IRMAA.
    • ACWDL 20-24 (FILE #page=3): "All deductions used in the ABD FPL program prior to the expansion will continue to apply", including health insurance premiums under 22 CCR 50555.2. 50555.2(c) deducts Part B in the months that the beneficiary pays it.
    • 42 USC 1395r(i)(1): for income over the threshold, "the monthly premium shall be increased" by the adjustment amount. So IRMAA is part of the individual's Part B premium.
  • The test proves a flip. CA 2026, Social Security of $2,100 a month, medicare_irmaa_magi of $150,000 (Part B 202.90 + 202.90 IRMAA). Head: 2,100 - 20 - 202.90 = 1,877.10 a month, over the 1,835.40 limit, so ineligible. Fix: 2,100 - 20 - 405.80 = 1,674.20, so eligible.
  • Not a regression against main, which deducts no premium for CA. The round-2 head gives the same wrong result. This is our round-2 C6 code, not an author error.
  • Reach: in the microsimulation, an IRMAA payer's current income is far above the CA limit, so the effect is about zero. The flip needs a household or API user who enters prior-year income, such as a recent retiree.
  • Fix: replace the comment at lines 70-71 and the code at lines 82-88. Optional: add "https://www.law.cornell.edu/uscode/text/42/1395r#i" to reference. ruff format reports no change.
        # "regardless of how the premium is paid": swap the person-paid Part B
        # in the shared aggregate for the individual's full Part B premium, the
        # base premium plus any IRMAA (42 USC 1395r(i)), whoever pays it.
        full_part_b = (
            person("gross_medicare_part_b_premium", period) * medicare_enrolled
        )
        disregards_part_b = p.disregards_medicare_part_b_premium[state].astype(bool)
        own_premiums = where(
            disregards_part_b,
            max_(paid_premiums - paid_part_b, 0) + full_part_b,
            paid_premiums,
        )
  • The one-token variant max_(base_part_b, paid_part_b) is not enough: when the MSP pays the base and the person pays the IRMAA, it disregards 202.90 instead of 405.80 (the second case below).
  • Run on a copy with the fix: the three cases below, the three PR test files, the Medicaid baseline folder, and the healthcare, CA signature and amplifi partner tests (853) pass. Each case fails at head.
  • Append the first two cases to the T income file, and the third to T/eligibility/categories/is_optional_senior_or_disabled_income_eligible.yaml:
- name: California deducts the full Part B premium, IRMAA included, that the person pays in 2026.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        medicare_enrolled: true
        social_security_retirement: 22_200
        health_insurance_premiums_without_medicare_part_b: 1_200
    tax_units:
      tax_unit:
        members: [person1]
        medicare_irmaa_magi: 150_000
    households:
      household:
        members: [person1]
        state_code: CA
  output:
    # Part B is 202.90 base plus 202.90 IRMAA a month (42 USC 1395r(i)).
    # ACWDL 20-18 p2 disregards "the amount of the individual's Medicare
    # Part B premium": (1,850 - 20 - 100 - 405.80) x 12 = 15,890.40.
    medicare_part_b_premium: 4_869.6
    medicaid_optional_senior_or_disabled_countable_income: 15_890.4

- name: California disregards the full Part B premium when an MSP pays the base and the person pays the IRMAA in 2026.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        medicare_enrolled: true
        social_security_retirement: 14_400
    tax_units:
      tax_unit:
        members: [person1]
        medicare_irmaa_magi: 150_000
    households:
      household:
        members: [person1]
        state_code: CA
  output:
    # The MSP pays the 202.90 base; the person pays the 202.90 IRMAA.
    # The disregard applies "regardless of the state's payment" (ACWDL 20-18
    # p2): (1,200 - 20 - 405.80) x 12 = 9,290.40.
    msp_part_b_premium_coverage: 2_434.8
    medicare_part_b_premium: 2_434.8
    medicaid_optional_senior_or_disabled_countable_income: 9_290.4
- name: California applicant who pays IRMAA is income eligible once the full Part B premium is disregarded in 2026.
  period: 2026
  input:
    people:
      person1:
        age: 70
        medicare_enrolled: true
        social_security_retirement: 25_200
    tax_units:
      tax_unit:
        members: [person1]
        medicare_irmaa_magi: 150_000
    households:
      household:
        members: [person1]
        state_code: CA
  output:
    # 2,100 - 20 - 405.80 = 1,674.20 a month, under 138% FPL (1,835.40).
    # With only the 202.90 base premium, 1,877.10 is over the limit.
    is_optional_senior_or_disabled_income_eligible: true

Should Address

SA8 (carried). Source conflict: record the ruling in the candidate, the register and the PR body.

  • Status: couples are done. The steps below remain. The code change for individuals is C8.
  • Steps:
    1. Replace the candidate at source_conflicts/candidates.yaml:3732-3765 with this entry. Replace <URL of this review> with the link to this review. The new id covers individuals too. You can keep the old id if you widen the question. The entry passes the guard's id, field and privacy rules on a copy.
- id: us-mo:mhabd-resource-limit-boundary
  program: medicaid
  years:
    from: 2018
    type: state-fiscal
  question: From fiscal year 2018, is a Missouri aged, blind or disabled claimant or couple whose countable resources
    equal the limit eligible, as the statute's "not to exceed" says, or ineligible, as the rule's "at or in excess of"
    says?
  sources:
  - kind: statute
    citation: RSMo 208.010.2(7)
    url: https://revisor.mo.gov/main/OneSection.aspx?section=208.010
  - kind: statute
    citation: RSMo 536.014(2)
    url: https://revisor.mo.gov/main/OneSection.aspx?section=536.014
  - kind: regulation
    citation: 13 CSR 40-8.020(4)(B)
    url: https://www.sos.mo.gov/CMSImages/AdRules/csr/current/13csr/13c40-8.pdf#page=2
  readings:
    statute: A claimant "owns or possesses resources not to exceed" the individual amount and a married couple "may
      own resources not to exceed" the couple amount, so resources equal to either limit qualify.
    other: A household with countable assets "at or in excess of" the limit is ineligible.
  example: In 2026, an individual with $6,068.80 or a couple with $12,137.55 of countable resources is eligible under
    the statute and ineligible under the rule.
  pe_us:
    follows: statute
    code:
    - policyengine_us/parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/assets/limit/individual_limit_inclusive.yaml
    - policyengine_us/parameters/gov/hhs/medicaid/eligibility/categories/senior_or_disabled/assets/limit/couple_limit_inclusive.yaml
    prs:
    - https://github.com/PolicyEngine/policyengine-us/pull/10364
  state: ruled
  note: Ruled on 2026-10-27 by the project lead to follow the statute for individuals and couples (ruling record
    <URL of this review>). RSMo 536.014(2) makes a rule that is in conflict with state law not valid. Missouri
    fiscal year 2018 began July 1, 2017. Before then, RSMo 208.010.2(4) (before August 28, 2016) and 13 CSR
    40-2.030(12) set a strict single-person test and an inclusive couple test, and the sources did not disagree.
  • The entry drops "(4)(A)1.B" as the regulation side, because 13 CSR 40-8.020 was not in force before July 1, 2017. It also drops "(208.010.2(4) before fiscal year 2018)".
  1. Open the rulespec-us known-source-conflicts.yaml entry now, with proposed items while the PR is open:
    • id: mhabd-resource-limit-boundary, jurisdiction: us-mo, program: medicaid, the question above, and years: {from: 2018, type: state-fiscal}.
    • pending_texts with a verbatim excerpt of RSMo 208.010.2(7), 13 CSR 40-8.020(4)(B) and RSMo 536.014(2).
    • A reading, one alternative (rule-at-or-in-excess), a basis, and outcome: computed. Keep dollar figures out of the prose fields unless a money_case computes them.
    • Entry-level status: open, as every PolicyEngine-ruled entry in the register has. The ruling goes on each implementation: ruling: {by: PolicyEngine, date: '2026-10-27', record: <URL of this review>}.
    • proposed: one item per flag file, with repo: PolicyEngine/policyengine-us, the PR URL, the path and follows: reading.
  2. After the merge:
    • In rulespec-us, move proposed to implementations at the merge commit. Add tests that pin "Missouri couple at the couple resource limit is eligible." and "Missouri individual at the 2026 individual resource limit is eligible.".
    • In a follow-up policyengine-us PR, run python scripts/update_source_conflicts.py --commit <rulespec-us commit>. Delete the candidate in the same commit. Put # Source conflict us-mo:mhabd-resource-limit-boundary alone on the line directly above 2017-07-01: true in the individual file and above 2015-01-01: true in the couple file.
    • Do not add the markers earlier. test_every_marker_names_a_registered_entry_that_lists_its_file reads only implementations, so a marker fails while the PR is open. #10176 and then #10441 used the same order.
  3. Correct the text:
    • PR body: "Missouri individuals keep the strict test (<), the agency's reading in 13 CSR 40-8.020(4)(B)" and "individual_limit_inclusive (CA)" become: Missouri individuals' resources may equal the limit from July 1, 2017 (RSMo 208.010.2(7)), and the flag covers CA and MO. In "Out of scope", the strict test stays "outside AR, OK, SC, CA and MO".
    • PR body, "Source conflicts": link this review as the ruling, use the new id, and delete "the model needs no change".
    • Issue #10466: it says Missouri's rule stays strict "for individuals" and lists Missouri for couples only. Under the ruling, both Missouri units are inclusive from July 1, 2017.

SA9. Missing edge cases: four PR-authored values in the asset test have no case that pins them.

  • Each change below passes all 105 PR cases (and all 750 Medicaid baseline cases), and each can change an eligibility result. The four cases pass at head and with the C8 and C9 fixes, and each fails on its change.
    • (a) Whole-cent rounding (round-1 C1). V/eligibility/categories/is_optional_senior_or_disabled_asset_eligible.py:22. Round 2 pinned it with the MO couple at the limit under <. Under <=, that couple's 6,000 + 6,137.55 is stored as 12,137.5498, under the limit with or without the rounding. Change: remove np.round, or sum in float32 without it. Without the rounding, the split couple below reads 12,137.5503 and is ineligible.
    • (b) CA individual_limit_inclusive from 2015. Every CA at-limit case is in 2023 or 2026. Change: start the flag on 2022-07-01. A 2019 applicant at $2,000 becomes ineligible.
    • (c) CA couple_limit_inclusive from 2015. The same gap for couples at $3,000.
    • (d) CA step of 2022-07-01 (P/assets/limit/{individual,couple}.yaml:53-54). No case reads CA in 2022, so only the late side is pinned (T asset file line 547). Change: date the step 2022-01-01. Every 2022 applicant with $2,000.01 to $130,000 becomes eligible.
  • Append to the T asset file:
- name: Missouri couple whose split resources sum to the 2026 couple limit is eligible.
  period: 2026
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 5_000.1
      person2:
        age: 68
        ssi_countable_resources: 7_137.45
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: MO
  output:
    # 5,000.10 + 7,137.45 = 12,137.55, the couple limit; RSMo 208.010.2(7)
    # "not to exceed". The inputs are stored as float32, and their sum is
    # 12,137.5503, so only the whole-cent rounding keeps the couple eligible.
    is_optional_senior_or_disabled_asset_eligible: [true, true]

- name: California applicant at the 2,000-dollar individual resource limit is eligible in 2019.
  period: 2019
  input:
    age: 70
    ssi_countable_resources: 2_000
    state_code: CA
  output:
    # 22 CCR 50420(a): the property reserve "shall not exceed" 2,000.
    is_optional_senior_or_disabled_asset_eligible: true

- name: California couple at the 3,000-dollar couple resource limit is eligible in 2019.
  period: 2019
  input:
    people:
      person1:
        age: 70
        ssi_countable_resources: 1_500
      person2:
        age: 68
        ssi_countable_resources: 1_500
    tax_units:
      tax_unit:
        members: [person1, person2]
        tax_unit_is_joint: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: CA
  output:
    # 22 CCR 50420(a): 3,000 combined equals the two-person limit.
    is_optional_senior_or_disabled_asset_eligible: [true, true]

- name: California applicant one cent over the 2,000-dollar individual resource limit is ineligible in 2022.
  period: 2022
  input:
    age: 70
    ssi_countable_resources: 2_000.01
    state_code: CA
  output:
    # Annual calculations read January 1, so 2022 uses the 2,000 limit; the
    # 130,000 limit of July 1, 2022 (ACWDL 21-31) first applies in 2023.
    is_optional_senior_or_disabled_asset_eligible: false

SA10. Missing edge cases: three PR-authored branches or values in countable income have no case that pins them.

  • Each change below passes all 105 PR cases, and each can change an eligibility result. The three cases pass at head and with the C8 and C9 fixes, and each fails on its change.
    • (a) Person-paid Part B before 12/2020 (V/income/medicaid_optional_senior_or_disabled_countable_income.py:87). Every pre-2021 CA case has no Medicare or an MSP that pays Part B, so paid_part_b is 0 in all of them. Change: drop the person-paid Part B in this branch. Flip window: in 2019, with $100 a month of other premiums, Social Security from $1,430 to $1,535 a month is income eligible at head and ineligible with the change. In that band the MSP pays nothing, because income is over the QI limit. The case sets medicare_part_b_premium directly, so it does not read the pre-2021 base premium (G12).
    • (b) The $230 for an applicant with an ineligible spouse (G11). Change: give the couple disregard to an applicant with deemed spouse income. This moves some 2019 deemed households across the two-person FPL. Do not assert eligibility in this case: MC 176 lines 19 and 21 (the spouse's maintenance need level, the one-person FPL) are not modeled (G17).
    • (c) Minnesota couple disregard of 0. The three MN cases are single people. Change: a $20 couple disregard. A 2026 MN couple with combined unearned income of $1,805 to $1,820 a month is ineligible at head and eligible with the change.
  • Append to the T income file:
- name: California deducted a Part B premium that the person paid before 12/2020.
  period: 2019
  absolute_error_margin: 0.01
  input:
    age: 70
    medicare_enrolled: true
    social_security_retirement: 17_400
    medicare_part_b_premium: 1_626
    health_insurance_premiums_without_medicare_part_b: 1_200
    state_code: CA
  output:
    # 22 CCR 50555.2(c) and MC 176 line 17: Part B counts in the months that
    # the person pays it (135.50 a month in 2019). The person's income is over
    # the QI limit, so no Medicare Savings Program pays it.
    # (1,450 - 20 - 230 - 100 - 135.50) x 12 = 11,574, under the 12,490 limit.
    # Without the Part B deduction, 13,200 would be over it.
    medicaid_optional_senior_or_disabled_countable_income: 11_574

- name: California gives an applicant whose spouse is not aged or disabled the 230-dollar individual disregard in 2019.
  period: 2019
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        is_tax_unit_head: true
        medicare_enrolled: false
        ssi_unearned_income: 15_000
      person2:
        age: 50
        is_tax_unit_spouse: true
        ssi_earned_income: 6_000
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: CA
  output:
    # The spouse is not aged or disabled, so the applicant is a qualified
    # individual, not part of a qualified couple (ACWDL 00-57 p1; MC 176
    # line 16): 230, not 310. The spouse's 500 a month is over the 2019
    # deeming threshold (1,157 - 771 = 386), so it counts.
    # MC 176 lines 8-16: (1,250 - 20) + (500 - 65) / 2 = 1,447.50; less 230
    # = 1,217.50 a month. Lines 19 and 21 (the spouse's MNL, the 1-person
    # FPL) are not modeled, so this case asserts no eligibility.
    ssi_couple_computation_applies: [false, false]
    medicaid_optional_senior_or_disabled_income_deemed_from_ineligible_spouse: [2_610, 0]
    medicaid_optional_senior_or_disabled_countable_income: [14_610, 0]

- name: Minnesota applies no 20-dollar disregard to a couple.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        is_tax_unit_head: true
        ssi_unearned_income: 15_600
      person2:
        age: 68
        is_tax_unit_spouse: true
        ssi_unearned_income: 7_800
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: MN
  output:
    ssi_couple_computation_applies: [true, true]
    # Minn. Stat. 256B.056, subd. 1b: 1,950 x 12 / 2 = 11,700 each.
    # A 20-dollar couple disregard would give 11,580.
    medicaid_optional_senior_or_disabled_countable_income: [11_700, 11_700]
  • Optional: an eligibility copy of case (a) without the margin (is_optional_senior_or_disabled_income_eligible: true), as the PR does at T income file line 426.

Suggestions

  • G15. Pin the floor in the couple branch. V/income/medicaid_optional_senior_or_disabled_countable_income.py:121. Removing max_(..., 0) there passes all 105 cases. No eligibility result can change: every reader compares the value with a limit of 0 or more, so 0 and a negative value give the same result. Optional case for the T income file:
- name: California couple countable income does not go below zero when premiums exceed income.
  period: 2026
  absolute_error_margin: 0.01
  input:
    people:
      person1:
        age: 70
        is_tax_unit_head: true
        medicare_enrolled: true
        ssi_unearned_income: 1_200
      person2:
        age: 68
        is_tax_unit_spouse: true
        medicare_enrolled: true
    marital_units:
      marital_unit:
        members: [person1, person2]
    households:
      household:
        members: [person1, person2]
        state_code: CA
  output:
    ssi_couple_computation_applies: [true, true]
    # (100 - 20) x 12 = 960, less 2 x 2,434.80 Part B, floored at 0.
    medicaid_optional_senior_or_disabled_countable_income: [0, 0]
  • G16. Assert the Example 5 result. The case at T income file line 463 says "over the limit, which is the letter's result", but asserts no eligibility. Add a copy of its input without absolute_error_margin, named "California applicant in ACWDL 20-24 Example 5 is not income eligible.", with is_optional_senior_or_disabled_income_eligible: [false, false]. It passes at head.
  • G17. Open an issue for California's spouse method. MC 176 line 19 deducts the maintenance need level ($600 for one person) for an ineligible spouse, and line 21 compares with the FPL for the people evaluated. ACWDL 20-24 page 3 keeps that method. The model uses SSI deeming and the two-person FPL for every married applicant (V/eligibility/categories/medicaid_optional_senior_or_disabled_income_limit.py:25, 43, on main before this PR).
    • The G11 fix exposes the gap. Example: in 2019, an applicant with Social Security of $1,250 a month and a spouse aged 50 who earns $1,000. Round-2 head: 1,407.50 a month, eligible against 1,409.17. Head: 1,467.50, ineligible. MC 176: 867.50 against 1,040.83, eligible.
    • The gap goes both ways: an applicant whose spouse has no income is ineligible in the model and eligible under MC 176. Keep the $230, because line 16 requires it.
    • Open an issue for all years. Cite it in the body's "Not modeled" item and in the Example 5 NOTE at lines 76-78.
  • G18. Minnesota: title, body and sources.
    • Retitle the PR to "... (CA, CT, MN, MO, AR, OK, SC)". In the first body paragraph, change "six states" to "seven states" and "the other five states" to "the other six states". Issue #10356 lists six states, so note Minnesota as an addition.
    • The first reference in P/income/disregard/{individual,couple}.yaml (KFF) lists $20 for Minnesota. Add the agency source: title: Minnesota DHS Health Care Programs Eligibility Policy Manual 2.3.3.3.2.2, MA-ABD Disregards and Deductions, href: https://hcopub.dhs.state.mn.us/epm/2_3_3_3_2_2.htm.
    • Add Minnesota to issue #10466. Minn. Stat. 256B.056, subd. 3(a) says "must not individually own more than $3,000", but the MN asset test stays strict.
  • G19. Statute citations and reference titles.
    • P/income/additional_disregard/{individual,couple}.yaml: add WIC 14005.40(c)(2)(A), the statute that sets "two hundred thirty dollars ($230) for an individual or, in the case of a couple, three hundred ten dollars ($310)" (https://leginfo.legislature.ca.gov/faces/codes_displaySection.xhtml?lawCode=WIC&sectionNum=14005.40).
    • P/income/disregards_medicare_part_b_premium.yaml: add WIC 14005.401, the authority that ACWDL 20-18 page 1 names.
    • The WIC 14005.40 href opens leginfo's list of two versions, and the "do not exceed" clause was (c)(3) before July 9, 2019. In both inclusive files, use title: California Welfare and Institutions Code 14005.40(c)(5) (version operative January 1, 2026; (c)(3) before July 9, 2019).
    • Optional: in P/assets/limit/{individual,couple}.yaml:11, use the ACWDL 21-31 subject, "Increases to the Asset Limits for Non-Modified Adjusted Gross Income Medi-Cal Programs".
  • G20. Cite the rule in force in the 2017 Missouri case. The comment at T asset file line 359 cites 13 CSR 40-8.020(4)(A), a 2017 rule that describes the earlier limits. Use: # 13 CSR 40-2.030(12) (11/30/15 edition): 1,000 "or more" before July 1, 2017. 1,500 is over it. The value is right.

Investigated and cleared

  • The $230 for an applicant with an ineligible spouse before 12/2020. ACWDL 00-57 page 1 ties the $310 to "qualified couples", and "Qualified individuals/couples need to be aged or disabled". MC 176 line 16, ACWDL 02-38 Q1 and Q7, and ACWDL 20-24 pages 2-3 agree. The round-2 effective $310 came from the deeming helper, which uses disregard.couple. It also made an $80-a-month jump at the deeming threshold, which head removes. 20 CFR 416.1163(d)(2) sets no state disregard.
  • Missouri couples in 2017. No statute covered these couples from August 28, 2016 to June 30, 2017. 13 CSR 40-2.030(12) did ("exceeds a value of two thousand dollars"), and 13 CSR 40-8.020 began on July 1, 2017. So MO couple_limit_inclusive true from 2015 is right for 2017. A 2017 false would be wrong.
  • California assets. The inclusive test in the $2,000 / $3,000 years follows 22 CCR 50420(a) in every year. The 2022-07-01 step matches ACWDL 21-31 pages 2-4, and a probe confirms that it first applies in 2023. WIC 14005.62 words the $130,000 as a "disregard", and DHCS applies it as the limit (ACWDL 21-31 page 1).
  • Minnesota. The statute and EPM 2.3.3.3.2.2 agree on $0. KFF's $20 is a secondary survey, so there is no source conflict. The income test at the limit uses <=, as EPM 2.3.3.3.1 does.
  • Code. No dependency cycle. A batch of 432 households (CA, MO, MN, NC, CT, SC, AR and OK; 7 years) matches 108 households computed one at a time, with 0 mismatches. Period use, entities and the monthly-to-annual conversion are correct. ruff format and ruff check pass. No hard-coded values.
  • Inner floor and date shifts. max_(paid_premiums - paid_part_b, 0) binds only for an inconsistent input (a direct total below the modeled Part B, which the direct input is documented to include). Moving a CA step by one day changes nothing, because annual calculations read January 1.
  • Register guard. 103 tests passed at 60ef70cb9c, and CI passes at head. The SA8 candidate passes the id, field and privacy rules.
  • Partner contract tests. No partner file changes. The CA partner senior is still $1,110 a month in 2026.

Out of scope / follow-up

  • DHCS links. Every dhcs.ca.gov/.../letters/Documents/*.pdf href now redirects (302) to a /file/<name>-pdf/ page behind a bot check. The repo has 24 such hrefs, so this is repo-wide. The cited content is stable in Wayback.
  • WIC 14005.62 becomes inoperative on July 1, 2027. This bears on draft PR #10375, not on this PR.
  • Issue #10466: the strict test in the other states. The false defaults in the two inclusive flags have no source; 42 USC 1396a(m)(1)(C) says "do not exceed".
  • Carried from earlier rounds: AR ARSeniors is aged-only; the asset files have different period metadata; the KFF titles; ParameterNotFoundError before 2015-01-01; the CT 2023 income limits and the CT couple values before 2024; issue #9734; CA whole-dollar rounding; issues #10457, #10458 and #10470; base Part B values before 2021 (a separate PR, as the author proposed).

PDF Audit Summary

Category Count
Confirmed correct 15 of 16 values (CA 2022 limits, CA inclusive flags, CA additional disregard and Part B dates, CA $20, MN $0, OK C-1 2026, MO couple flag) and 14 of 15 references
Mismatches (code-path confirmed + visually verified) 1: MO individual flag false from 2017-07-01 (C8). 1 reference supports only 2018 on (MO couple flag, C8)
Formula mismatch 1: IRMAA dropped from the Part B disregard (C9)
Not checked The 49 default keys of each inclusive flag (issue #10466)
Unmodeled items CA spouse method, MC 176 lines 19 and 21 (G17); Example 5 spouse premium; CA whole-dollar rounding

Validation Summary

Check Result
Regulatory Accuracy 2 critical (C8, C9); 1 should address (SA8)
Reference Quality MO flag references missing or partial (C8); 2 suggestions (G18, G19); G5 partial
Code Patterns 0 issues; no hard-coded values
Formatting (params & vars) 0 issues
Test Coverage 2 should address (SA9, SA10: 7 untested PR values or branches); 3 suggestions (G15, G16, G20)
PDF Value Audit 1 value mismatch (C8); 15 confirmed
CI Status Passing: 33 of 33 checks
Local tests PR test files 105 passed. The 17 proposed cases: 12 pass at head, and the 5 C8 and C9 cases fail by design. On a copy with the C8 and C9 fixes: 122 passed (the 17 cases and the 105 PR cases), Medicaid baseline 750 passed, code-health 114 passed
Partner contract tests No partner file touched. 853 Medicaid and partner cases pass on the C9 fix copy
Branch 11 ahead, 6 behind main; MERGEABLE
Other reviews None

Review Severity: REQUEST_CHANGES

Two critical items (C8, C9) and three should-address items (SA8, SA9, SA10) are open.

Next Steps

  1. To auto-fix issues: run the fix-pr workflow for this PR.
  2. Suggested order: C8 with SA8 (the flag YAML, the variable comment, five cases, the candidate, the body and issue #10466). Then C9 (one code edit and three cases). Then SA9 and SA10 (seven cases). The suggestions can follow in the same commit or later.
  3. The C8 and C9 edits do not overlap, and they ran together on one copy. After you apply them, run policyengine-core test policyengine_us/tests/policy/baseline/gov/hhs/medicaid -c policyengine_us and the Medicare tests.
  4. C9 changes CA countable income for IRMAA payers. Run the partner tests under policyengine_us/tests/policy/baseline/partners/analytics_coverage/edge_cases/federal/healthcare again. Do not edit a partner file without the partner review gate.
  5. Open the rulespec-us entry with proposed items now (SA8, step 2). Add the markers only in a follow-up PR after the merge (SA8, step 3).
  6. Process note, not counted: the axiom: line is now in the body.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Medicaid aged, blind and disabled pathway: outdated disregards and asset limits in CA, CT, AR, MO, OK and SC

2 participants