Skip to content

Combine an SSI couple's income by each month's facility status - #10820

Merged
hua7450 merged 2 commits into
PolicyEngine:mainfrom
hua7450:ssi-part-year-couple-income
Nov 12, 2026
Merged

hua7450 merged 2 commits into
PolicyEngine:mainfrom
hua7450:ssi-part-year-couple-income

Conversation

@hua7450

@hua7450 hua7450 commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Combines an SSI couple's income by each month's facility status, so a part-year medical facility stay no longer changes SSI in the other months of the year.

Fixes #10818

Changes since review (2026-11-12)

  • Part-year cases for the annual readers. medicaid_optional_senior_or_disabled_countable_income.yaml and md_paa_countable_income.yaml (Case 4) each add a 2025 couple aged 72 and 70, with 12,000 and 6,000 of unearned income, in which the second spouse is in a Medicaid facility from July. Both annual formulas apply December's status to the whole year, so each spouse counts their own income in all twelve months: (1,000 - 20) x 12 = 11,760 and (500 - 20) x 12 = 5,760. Summing the monthly ssi_marital_unearned_income instead would combine 1,500 a month through June and give 14,760 and 11,760, so reverting either formula to the monthly variables now fails its test.
  • Couple eligibility test. A NOTE in ssi_couple_computation_applies.py, after the comment that the model does not cover intent to return, says the model also omits SSI's couple eligibility test during a temporary absence (POMS SI 02005.050 B.3) and names Apply SSI's couple eligibility test when one spouse is temporarily absent in a facility #10851.
  • Facility month wording. The helper's docstring, both marital variables and the YAML comments now say a month one spouse spends "throughout" in a facility, as 20 CFR 416.414(a)(1) defines it. Both variables now cite 416.414(a)(1). The POMS case name now says the couple is paid as a couple through the month one spouse enters a Medicaid facility and as individuals from the first full month.

What was wrong

ssi_marital_earned_income and ssi_marital_unearned_income were YEAR-defined and read the monthly ssi_couple_computation_applies for the year, which Core returns at its last month. ssi_countable_income and ky_ssp_countable_income are monthly and halve the pooled income by the month's own status. In a year with a part-year stay, the months whose status differs from December's got half of each spouse's own income, or each spouse got the couple's whole income.

The pool and the split began to disagree when #10332 made ssi_countable_income monthly. The two variables have read the status for the year since #8004, and the status has been monthly since #8375.

Law

  • 20 CFR 416.420: "Benefits shall be determined for each month."
  • 20 CFR 416.1802(b)(1): "If you apply for or receive SSI benefits and have an eligible spouse as defined in § 416.1801(c), we will count your combined income and calculated the benefit amount for you as a couple."
  • 20 CFR 416.414(a)(1): "By throughout a month we mean that you are in the medical treatment facility as of the beginning of the month and stay the entire month."
  • 20 CFR 416.414(b)(3), for an eligible couple with one spouse temporarily absent in a medical treatment facility: "The benefit payment for each spouse is figured by subtracting each individual's own countable income in the appropriate month (see § 416.420) from his or her portion of the benefit rate shown in paragraphs (b)(3) (i) and (ii)."
  • POMS SI 02005.050 D.1, a couple whose husband enters a Title XIX facility on June 10, 2000: "June 2000 This is the last month for which payment is computed for a couple." "Payment for July is computed as if Allen and Martha are individuals."

Regulation text is from the eCFR as of 2026-10-30.

Changes

  • ssi_marital_earned_income and ssi_marital_unearned_income are MONTH-defined. Each calls a new helper, _ssi_couple_income in _ssi_spouses.py, which reads the couple status and the income for the same period.
  • md_paa_countable_income and medicaid_optional_senior_or_disabled_countable_income are YEAR-defined and apply one couple status to the whole year. They now call the same helper for the year, so they pool annual income by that status, as before. Their results are unchanged in every one-household run below, and a part-year case in each test file now pins that: a stay from July gives each spouse their own income for the whole year, as on main. A NOTE in each says they still treat a part-year stay as lasting all year or not at all.
  • ssi_countable_income and ky_ssp_countable_income are unchanged and now receive each month's pooled income.
  • 16 single-person cases in medicaid_optional_senior_or_disabled_countable_income.yaml set ssi_marital_* as inputs, which the annual formula no longer reads. They now set ssi_unearned_income or ssi_earned_income, with the same amounts and expected values.

Results

One-household simulations on main (23a7c9a) and on this branch. The 2026 household: spouse A, 68, with 1,000 a month of unearned income; spouse B, 62, disabled, with 1,500 a month, in a Medicaid facility from July.

Case Month and output main This PR
2026 household, stay July through December June uncapped_ssi 255.50 and 5.50 -494.50 each
2026 household, stay July to November July uncapped_ssi -1,486 and -2,450 14 and -1,450
POMS SI 02005.050 D.1, stay July through December June ssi 194.50 and 294.50 94.50 each (POMS: 94.50 each)
POMS SI 02005.050 D.1, stay July to November July ssi 0 and 0 0 and 333 (POMS: 0 and 332)
2026 household in Kentucky, stay through December June ky_ssp_countable_income 500 and 750 1,250 each

POMS uses the 512 individual rate first set for 2000; SSA's rate table and the model have 513. Households with no stay, or a stay all year, are unchanged. So are the annual Medicaid and Maryland amounts in every run.

Tests

  • ssi_marital_unearned_income.yaml and ssi_marital_earned_income.yaml: one case each, for June and July around a stay that runs through December.
  • ssi_countable_income.yaml: the POMS SI 02005.050 D.1 example, June and July 2000, for countable income and SSI.
  • medicaid_optional_senior_or_disabled_countable_income.yaml and md_paa_countable_income.yaml (Case 4): one part-year case each, for the 2025 couple with a stay from July, expecting 11,760 and 5,760 for the year and the couple status for June and July.
  • The PR's five test files pass locally at 27687ea37f: 77 cases. Other suites were not run locally; CI validates. ruff format --check and ruff check pass on the whole repository.
  • Pooling two monthly twelfths can differ from one-twelfth of the annual pool by float rounding: at most 0.006 a year per spouse in a check of 2 million random income pairs, and none for incomes that are whole dollars a month. An existing couple case with uneven incomes and the default 0.001 margin could show it.

Not covered

  1. Couple eligibility test during a temporary absence (Apply SSI's couple eligibility test when one spouse is temporarily absent in a facility #10851). POMS SI 02005.050 B.3: "Determine SSI eligibility by subtracting the couple's combined countable income from the full FBR for a couple. If they are eligible, determine the amount of their SSI payment based on each person's own income." The model applies no such test in a facility month, before or after this PR, and has no input for intent to return. So in the 2026 household it pays A 14 a month from July, although the couple's 2,480 is above the 1,491 couple rate. The POMS example passes the test (580 against 769), so its cases do not depend on it.
  2. Annual readers. Maryland PAA and the Medicaid optional pathway still apply December's status to the whole year. Read monthly SSI support in Maryland and California benefits #10809 makes Maryland PAA monthly; once both merge, its both_eligible should read period, and Case 4 in md_paa_countable_income.yaml will change with it.
  3. Budget month. The model uses each month's own income, not the second prior month's (416.420(a)). Income is the same every month in these cases.

Overlap

Files

changelog.d/ssi-part-year-couple-income.fixed.md
policyengine_us/variables/gov/ssa/ssi/eligibility/income/marital/ssi_marital_earned_income.py
policyengine_us/variables/gov/ssa/ssi/eligibility/income/marital/ssi_marital_unearned_income.py
policyengine_us/variables/gov/ssa/ssi/eligibility/resources/deemed/_ssi_spouses.py
policyengine_us/variables/gov/ssa/ssi/ssi_couple_computation_applies.py
policyengine_us/variables/gov/states/md/dhs/fia/paa/md_paa_countable_income.py
policyengine_us/variables/gov/hhs/medicaid/income/medicaid_optional_senior_or_disabled_countable_income.py
policyengine_us/tests/policy/baseline/gov/ssa/ssi/income/ssi_marital_earned_income.yaml
policyengine_us/tests/policy/baseline/gov/ssa/ssi/income/ssi_marital_unearned_income.yaml
policyengine_us/tests/policy/baseline/gov/ssa/ssi/ssi_countable_income.yaml
policyengine_us/tests/policy/baseline/gov/hhs/medicaid/income/medicaid_optional_senior_or_disabled_countable_income.yaml
policyengine_us/tests/policy/baseline/gov/states/md/dhs/fia/paa/md_paa_countable_income.yaml

axiom: TheAxiomFoundation/rulespec-us#2191 queued

🤖 Generated with Claude Code

ssi_marital_earned_income and ssi_marital_unearned_income were YEAR-defined
and read the monthly ssi_couple_computation_applies for the year, which Core
returns at December. ssi_countable_income and ky_ssp_countable_income split
by the month's own status, so a part-year medical facility stay gave wrong
SSI in the months whose status differs from December's (20 CFR 416.414(b)(3),
416.1802(b)(1), 416.420).

Make both variables MONTH-defined through a shared helper,
_ssi_couple_income. The annual Maryland PAA and Medicaid optional pathway
formulas call the same helper for the year, so their results are unchanged;
their Medicaid test inputs move from ssi_marital_* to the underlying SSI
income inputs. Add cases for the months before and during a stay, including
the POMS SI 02005.050 D.1 example.

Fixes PolicyEngine#10818

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

hua7450 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Requesting your review. This fixes #10818, which came up while clearing your reviews of #10633 and #10640.

  • ssi_marital_earned_income and ssi_marital_unearned_income read the couple status for the whole year, which Core returns at December. In a year with a part-year facility stay, the months whose status differed from December's pooled the couple's income wrongly. They are now monthly and follow each month's status (20 CFR 416.420, 416.414(b)(3)).
  • The two annual readers, Maryland PAA and the Medicaid optional pathway, keep one status for the year, as before.
  • Once this merges, 10633's msp_fpg.yaml Case 4 and 10640's Case 44 can go back to their original form.
  • It overlaps with Include farm and partnership earnings in SSI and correct loss netting #10532 and Read monthly SSI support in Maryland and California benefits #10809.

All checks pass. It hasn't had a local review round, and no tests were run locally.

@hua7450
hua7450 requested a review from DTrim99 November 12, 2026 16:15
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Combine an SSI couple's income by each month's facility status

Reviewed at head 0ca225faa6 (10 files, +186/-49). First review.

Summary

The fix is correct in law and in period handling. ssi_marital_earned_income and ssi_marital_unearned_income become MONTH-defined and read the couple status and the income for the same month, so the pool now agrees with the monthly split in ssi_countable_income and ky_ssp_countable_income. Couples with no facility month, or a stay all year, are unchanged apart from float rounding. All 16 new expected values check by hand, and the POMS SI 02005.050 D.1 example is reproduced.

One item blocks approval under the mutation rule: the two annual readers were rewritten to keep pooling by the year's single status, and no test has a part-year stay for either, so reverting those hunks fails nothing.

Open: 0 critical, 1 should, 5 suggestions.

Law read independently before the code (eCFR as of 2026-10-30, POMS pages as published today):

  • 42 U.S.C. 1382(e)(1)(B)(ii): "in the case of an individual who has an eligible spouse, if only one of them is in such a facility throughout such month, at a rate not in excess of the sum of— (I) the rate of $360 per year (reduced by the amount of any income ... of the one who is in such facility), and (II) the applicable rate specified in subsection (b)(1) (reduced by the amount of any income ... of the other)".
  • 20 CFR 416.414(a)(1): "By throughout a month we mean that you are in the medical treatment facility as of the beginning of the month and stay the entire month."
  • 20 CFR 416.414(b)(3): "The benefit payment for each spouse is figured by subtracting each individual's own countable income in the appropriate month (see § 416.420) from his or her portion of the benefit rate".
  • 20 CFR 416.1147(b)(1) and (d)(1): "we compute your benefits as if you were separately eligible individuals (see § 416.414(b)(3)). This begins with the first full calendar month that one of you is in the medical institution."
  • 20 CFR 416.432(a): after a couple dissolves, "the benefit amount for each person shall be determined individually for each month beginning with the first month after the month in which the dissolution occurs."
  • 20 CFR 416.420: "Benefits shall be determined for each month." 20 CFR 416.1802(b)(1): "we will count your combined income and calculated the benefit amount for you as a couple."
  • POMS SI 00501.154 A.1: "couple computation rules apply to two individuals for a month, if, as of the first moment of that month" they are eligible, married and in the same household.
  • POMS SI 02005.050 B.3 and D.1 (quoted below).

So the month of entry is still a couple month, the first full calendar month is the first separate month, and each spouse's own income (with their own $20 exclusion, as D.1 shows: 400 - 20 and 200 - 20) is counted from then. The code matches this, given that the monthly facility input means "in the facility throughout the month".

Critical (must fix)

None.

Should address

1. The annual readers' part-year treatment is not pinned by any test.
md_paa_countable_income.py:29-44 and medicaid_optional_senior_or_disabled_countable_income.py:36-59 now call _ssi_couple_income(person, period, ...) for the year instead of reading ssi_marital_*. The NOTEs explain why: the monthly variables' annual totals would not match the single annual status in a part-year stay. That is the right call, but nothing tests it. Neither test folder has a part-year stay: every Maryland PAA and Medicaid optional-pathway facility case covers the full year, including the one Medicaid case that keys the living arrangement by month, which starts in January. Reverting either hunk to person("ssi_marital_unearned_income", period) passes every test and silently changes part-year results, which the PR body says are unchanged.

Fix: add one part-year case to medicaid_optional_senior_or_disabled_countable_income.yaml and one to md_paa_countable_income.yaml, named as the documented annual approximation. For example (traced by hand at this head, not run), take the file's existing North Carolina 2025 facility case and change it to: person1 aged 72 with ssi_unearned_income: 12_000, person2 aged 70 with ssi_unearned_income: 6_000, and person2's two facility inputs true from 2025-07.

  • At this head: December's status is separate, so each counts their own income: (1,000 - 20) x 12 = 11,760 and (500 - 20) x 12 = 5,760.
  • With the hunk reverted: January to June pool 1,500 a month, giving 15,000 - 240 = 14,760 and 12,000 - 240 = 11,760.

The same household in Maryland distinguishes the two for PAA. #10809 will then change the Maryland case on purpose when it makes PAA monthly.

Suggestions

1. Record the couple eligibility test deferral in the repository ("Not covered" 1).
42 U.S.C. 1382(a)(2)(A) tests an individual with an eligible spouse on income "(together with the income of such spouse)". 20 CFR 416.1801(c) keeps spouses an eligible couple "during temporary absences as defined in § 416.1149". POMS SI 02005.050 B.3: "Determine SSI eligibility by subtracting the couple's combined countable income from the full FBR for a couple. If they are eligible, determine the amount of their SSI payment based on each person's own income."

The model applies no such test, so the 2026 household's spouse A is paid 994 - 980 = 14 in each facility month although the couple's 2,480 exceeds the 1,491 couple rate. This predates the PR: main pays the same 14 when the stay covers December. It does widen with this PR. For the stay that ends in November, main paid A nothing for July to November (for the wrong reason), and this head pays 14 a month. A stay that ends with a return home is the clearest temporary absence, so that is the case where B.3 most plainly gives zero.

It needs an intent-to-return input, so it is a reasonable deferral. It is recorded only in the PR body and rulespec-us#2191. Please open a policyengine-us issue and name it in the comment in ssi_couple_computation_applies.py, which already says intent to return is not modeled.

2. Say "first full calendar month" where the rule is stated.

  • ssi_countable_income.yaml:195: "paid as a couple until one spouse enters a Medicaid facility and as individuals from that month". In the POMS example Allen enters on June 10 and June is "the last month for which payment is computed for a couple". Suggest "through the month one spouse enters a Medicaid facility and as individuals from the first full month".
  • _ssi_spouses.py:120-121, ssi_marital_earned_income.py:20-21, ssi_marital_unearned_income.py:20-21, and the YAML comments at ssi_marital_earned_income.yaml:91 and ssi_marital_unearned_income.yaml:92: "a month one spouse spends in a medical treatment facility" should read "throughout", as 416.414(a)(1) defines it.
  • Consider adding 416.1147(d)(1) or 416.414(a)(1) to the two variables' references, since (b)(3) alone does not give the timing.

3. Optional coverage for the other rows of the Results table.
The three new cases all use a stay that runs through December. A case whose stay ends (for example 2026-12: false) would pin rows 2 and 4, and a June assertion in ky_ssp_countable_income.yaml (1,250 each) would pin row 5. Kentucky's formula is unchanged, so this is optional.

4. Cost of the spouse test.
_ssi_couple_income calls _ssi_spouse_index on every monthly call, so a year of the two variables runs the household-position loop 24 times where main ran it twice. The microsimulation job passed (49m36s), so this is only worth a thought if the pairing is later cached in a YEAR variable.

5. rulespec-us#2191 still describes the PR as a draft. Update it when the merged commit is recorded there.

Verified correct

Period handling:

  • ssi_couple_computation_applies is a MONTH bool. Core reads a monthly bool over a year at the year's last month and reads a YEAR float over a month as one-twelfth. So on main the YEAR-defined pool used December's status, and at this head a monthly call reads that month's status and one-twelfth of each spouse's annual income.
  • The spouse row is taken for period.this_year, the same test ssi_couple_computation_applies uses. Rows without a spouse index row 0, masked by the couple status, as before.
  • An annual call of the helper reads December's status and annual income, which is exactly what the two annual readers got from the old YEAR variables. The explicit sums replace add() over the same three terms; ssi_in_kind_support_and_maintenance still sums its twelve months.
  • No facility month: every month's status equals December's, so (a + b)/12 becomes a/12 + b/12. Full-year stay: same reasoning. Only float rounding can differ, as the PR body says.
  • The tests' 2026-07: true (and 2000-07: true) carries forward to December and not back to June, because the system carries the latest input forward only. So the stay does run through December as the comments say, and each new case fails on main.
  • Remaining readers of the two variables are ssi_countable_income and ky_ssp_countable_income, both monthly. No parameter list or reform names them. test_ssi_monthly_ism_properties.py and Kentucky Case 5 set them as inputs and still work with a MONTH variable.

Hand-checked expectations (all 16 new values):

Case Arithmetic Expected On main
ssi_marital_earned_income 2026-06 12,000/12 = 1,000; spouse 0 + 1,000 [1,000, 1,000] [1,000, 0]
ssi_marital_earned_income 2026-07 one spouse in facility, own income [1,000, 0] same
ssi_marital_unearned_income 2026-06 1,000 + 1,500 [2,500, 2,500] [1,000, 1,500]
ssi_marital_unearned_income 2026-07 own income [1,000, 1,500] same
ssi_countable_income 2000-06 (400 + 200 - 20) / 2 [290, 290] [190, 90]
ssi_countable_income 2000-07 400 - 20; 200 - 20 [380, 180] same
ssi 2000-06 769/2 - 290 = 384.50 - 290 [94.5, 94.5] [194.5, 294.5]
ssi 2000-07 30 - 380 < 0; 513 - 180 [0, 333] same
  • POMS SI 02005.050 D.1 gives 94.50 each for June and 0 and 332 for July. The 1-dollar gap is the rate: POMS SI 02001.020 note 3 says "Benefits paid in January 2000 through July 2001 were based on Federal Benefit Rates of $512.00 and $530.00, respectively. Pursuant to Public Law 106-554 ... monthly payments for 2000 and 2001 were effectively based on the higher $513.00 and $531.00 amounts." The parameter has 513 and 769, and the test comment explains this accurately.
  • The 16 renamed inputs in medicaid_optional_senior_or_disabled_countable_income.yaml are all single people, for whom the helper returns their own income, and no expected value changes. Spot checks: California (1,000 - 20) x 12 = 11,760; South Carolina (1,000 - 50) x 12 = 11,400; California with Part B (1,000 - 20 - 202.90) x 12 = 9,325.20; Missouri 13,821/12 = 1,151.75, less 20, floored to 1,131, x 12 = 13,572; Connecticut (1,000 - 562) x 12 = 5,256.
  • PR body Results table, reproduced by hand: June -494.50 each (745.50 - 1,240) against 255.50 and 5.50 on main; July 14 and -1,450 against -1,486 and -2,450; Kentucky 1,250 each against 500 and 750.

Other checks:

  • Every regulation and POMS quotation in the PR body matches the source.
  • References are a tuple of two eCFR paragraph anchors, both valid.
  • Changelog fragment changelog.d/ssi-part-year-couple-income.fixed.md is present and typed correctly.
  • Tests are in variable-named YAML files with plain periods. No Python test is added and no partner test file is touched.
  • No new state-gated key variable, so the New Hampshire Add New Hampshire state supplementation for 2026 #10156 pattern does not arise.
  • The added comments contain nothing the source-conflict ratchet matches, and no statute-versus-manual choice is taken.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2191 queued is valid. The issue is OPEN, labelled pe-parity, and names this PR at head 0ca225faa6. It gives module paths, corpus citations, the verbatim regulation and POMS text, required outputs, and companion cases whose expected values come from POMS SI 02005.050 D.1 and the regulations, with the blockers recorded. It matches the head's behavior, including the three stated limits.

CI status

All 37 checks pass at this head, including Baseline (ssa-usda), all four state shards, Household API Partners, Microsimulation and PR validation. Three workflow placeholders are skipped. No local run was needed; expectations were traced by hand.

Branch status

1 commit ahead of main and 3 behind. git merge-tree against main is clean, and main has not touched any of the 10 files since the merge base. Open PRs that overlap: #10532 (ssi_marital_earned_income.py, md_paa_countable_income.py, ssi_marital_earned_income.yaml) and draft #10809 (md_paa_countable_income.py). Whichever merges second will conflict.

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 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.

Requesting one change, a test gap. The two annual readers (Maryland PAA and the Medicaid optional pathway) were rewritten to keep pooling by the year's single status, and no test has a part-year stay for either, so reverting those hunks to read ssi_marital_* passes every test while changing part-year results. One part-year case in each file closes it; the report above gives a worked example (Should Address 1).

The fix itself is right in law and in period handling: the month of entry is still a couple month, the first full calendar month is the first separate month, all 16 new expected values hold by hand, and the POMS SI 02005.050 D.1 example is reproduced. The five suggestions are optional. CI is green at 0ca225faa6.

…issue

Add a part-year facility case to the Medicaid optional pathway and to
Maryland PAA countable income: each applies December's status to the
whole year, so reverting either to the monthly marital variables fails.
Note in ssi_couple_computation_applies that the couple eligibility test
during a temporary absence is not modeled (POMS SI 02005.050 B.3,
issue 10851). Say "throughout" where 416.414(a)(1) defines a facility
month, and cite it beside 416.414(b)(3).

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

hua7450 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Ready for another look at 27687ea37f. The two annual readers each have a part-year case now (Should Address 1), ssi_couple_computation_applies names #10851 for the couple eligibility test, and the facility-month wording follows 416.414(a)(1). The five changed test files pass locally.

@hua7450
hua7450 requested a review from DTrim99 November 12, 2026 19:33
@DTrim99

DTrim99 commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Combine an SSI couple's income by each month's facility status

Reviewed at head 27687ea37f (12 files). Round 1 was at 0ca225faa6: request changes, 0 critical, 1 should, 5 suggestions. One fix commit since, no merge of main.

Summary

The fix commit does what the reply says and nothing else. Each annual reader now has a part-year case, both expected values check by hand, and each case fails if its reader goes back to the monthly ssi_marital_unearned_income. The couple eligibility test deferral is recorded in #10851 and named in the code. The facility-month wording follows 20 CFR 416.414(a)(1). No formula changed.

Open: 0 critical, 0 should, 4 suggestions (2 new, 2 carried over and optional).

How each claim below was checked: expected values and the revert results by hand tracing of the head's formulas; wording, references, issues and the ratchet by reading; that the cases pass at this head by CI. No test was run locally for this round.

Prior findings

Finding Status Evidence
Should 1: the annual readers' part-year treatment is not pinned Fixed New case in medicaid_optional_senior_or_disabled_countable_income.yaml (North Carolina 2025) and Case 4 in md_paa_countable_income.yaml. Head gives 11,760 and 5,760; with either reader's hunk reverted the same case gives 14,760 and 11,760, far outside the 0.01 margin (hand trace below). CI ran both green. The earned-income line of each hunk is pinned only through the shared helper: see new suggestion 1.
Suggestion 1: record the couple eligibility test deferral Fixed #10851 is OPEN. It quotes 42 U.S.C. 1382(a)(2)(A), 20 CFR 416.1801(c) and POMS SI 02005.050 B.3, gives the 994 - 980 = 14 example and says why it needs a temporary-absence input. ssi_couple_computation_applies.py:33-37 names it. All three quotations match the sources (reading).
Suggestion 2: say "throughout" and "first full calendar month" Fixed _ssi_spouses.py:119-122, both marital variables' comments and both YAML comments now say "spends throughout", and the helper and variables add "from the start of the month to its end (416.414(a)(1))". The POMS case is renamed "through the month one spouse enters a Medicaid facility and as individuals from the first full month". Both variables cite #p-416.414(a)(1).
Suggestion 3: optional cases for a stay that ends, and a Kentucky June assertion Open (optional) Not added and not mentioned in the reply. Kentucky's formula is unchanged, so this stays optional.
Suggestion 4: cost of the spouse test on every monthly call Open (optional) No change. Microsimulation passed in 52m43s at this head (49m36s in round 1).
Suggestion 5: rulespec-us#2191 called the PR a draft Fixed The issue now says "open and ready for review, head 27687ea37f".

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

1. Give one spouse earned income in the two part-year cases.
Both new cases have unearned income only, so they pin _ssi_couple_income(person, period, "ssi_unearned_income") in each reader. The earned line beside it (md_paa_countable_income.py:35, medicaid_optional_senior_or_disabled_countable_income.py:42-44) could go back to person("ssi_marital_earned_income", period) and both cases would still pass, because earned income is 0 either way. The round 1 example had the same gap, and the helper's annual behavior is pinned through the unearned call, so this is a suggestion and not a reason to hold the PR.

One added input closes it in both files: ssi_earned_income: 12_000 on person1 (traced by hand, not run).

  • At this head: person1 has 1,000 - 20 = 980 unearned and (1,000 - 65) / 2 = 467.50 earned, so 1,447.50 x 12 = 17,370. Person2 stays at 5,760. Expected [17_370, 5_760].
  • With only the earned line reverted: January to June give person2 the couple's 1,000 of earnings, 6,000 for the year, or 500 a month: (500 - 65) / 2 = 217.50, plus 480, x 12 = 8,370, not 5,760.

2. Two test names still date the change from entry.
ssi_marital_earned_income.yaml:65 and ssi_marital_unearned_income.yaml:65 are named "... combined only in the months before one spouse enters a medical facility". The month of entry is still combined (POMS SI 02005.050 D.1: June, for a June 10 entry). The comments under them are now right. Suggest "... before one spouse's first full month in a medical facility".

3. Carried over, optional: a case whose stay ends before December, and a June assertion in ky_ssp_countable_income.yaml (1,250 each).

4. Carried over, optional: the spouse test runs on every monthly call of the two variables.

Verified

Scope of the fix commit (reading the diff between the two heads, 102 added and 7 removed lines in 9 files):

  • Non-test files touched, all four comment or reference only:
    • ssi_marital_earned_income.py: one reference added (416.414(a)(1)), comment reworded.
    • ssi_marital_unearned_income.py: the same.
    • _ssi_spouses.py: the _ssi_couple_income docstring reworded.
    • ssi_couple_computation_applies.py: a five-line NOTE added.
  • No formula line, parameter, period or entity changed. md_paa_countable_income.py and medicaid_optional_senior_or_disabled_countable_income.py are untouched since round 1.
  • Test files: two new cases, three comment rewordings and one case renamed. No existing expected value changed.

Hand trace of the two new cases (the only new expectations):

Output Arithmetic at this head Expected Reader hunk reverted
ssi_couple_computation_applies 2025-06 (both files) Both aged 72 and 70, one two-person marital unit, so the claim is joint. Neither is in a facility: the January false carries forward to June. [true, true] same
ssi_couple_computation_applies 2025-07 (both files) Person2 has both facility inputs true from July, so exactly one spouse is in a Medicaid facility. [false, false] same
medicaid_optional_senior_or_disabled_countable_income, North Carolina 2025 The annual read takes December's status, separate, so each counts their own income. North Carolina: 20 individual disregard, no additional disregard, no premium deduction. (12,000 / 12 - 20) x 12 = 980 x 12; (6,000 / 12 - 20) x 12 = 480 x 12. No deeming: neither spouse is ineligible. [11,760, 5,760] Monthly sum: 6 x 1,500 + 6 x 1,000 = 15,000 and 6 x 1,500 + 6 x 500 = 12,000. (1,250 - 20) x 12 = 14,760; (1,000 - 20) x 12 = 11,760.
md_paa_countable_income, Maryland 2025 The same, with SSI's 20 exclusion. both_eligible is December's status, false, so nothing is halved, and the deemed amount is 0. [11,760, 5,760] 15,000 - 240 = 14,760; 12,000 - 240 = 11,760.
  • Period rules behind the trace, read in policyengine-core's source (a 3.33 copy; the lock pins 3.34.2, which CI ran): a monthly bool read for a year returns the last month's value, a monthly amount read for a year is summed over twelve months, and an annual amount read for a month is one-twelfth. A monthly input carries forward to later months and not back.
  • Both cases also pass on main, where the YEAR-defined variables used December's status. So they pin "unchanged", as the PR body says, and fail only if a reader is pointed at the monthly variables.
  • In-kind support, parental deeming and the student exclusion are 0 for this household (two adults, no shelter or food support inputs).
  • The comments in the two cases state the same arithmetic, including the reverted amounts.

Wording against the regulations (reading):

  • 20 CFR 416.414(a)(1): "By throughout a month we mean that you are in the medical treatment facility as of the beginning of the month and stay the entire month." The code's "spends throughout ..., from the start of the month to its end" matches.
  • 20 CFR 416.1147(d)(1): "This begins with the first full calendar month that one of you is in the medical institution." The renamed POMS case matches, and so does POMS SI 02005.050 D.1 (June is "the last month for which payment is computed for a couple").
  • Source-conflict ratchet: the reworded and added comments match none of its patterns. The six variable files have 0 matching lines at this head, at the round 1 head and on main, and none is in KNOWN_UNMARKED. The new NOTE records a deferral that the statute and POMS agree on, not a choice between sources.
  • The added reference is a single eCFR paragraph anchor in the same form as the two beside it, and it supports the "throughout" wording.

Other:

  • The PR body's "Changes since review" section matches the commit. Its count of 77 cases in the five test files is right (50 + 5 + 5 + 13 + 4).
  • Changelog fragment unchanged. Test periods are plain. No Python test and no partner test file is touched.

Axiom

axiom: TheAxiomFoundation/rulespec-us#2191 queued is still valid. The issue is OPEN, labelled pe-parity, was updated after the fix commit and names head 27687ea37f. It now lists #10851 under the couple eligibility test, describes the two part-year cases with the same 11,760 and 5,760, and keeps them out of oracle parity as an annual approximation. Its companion cases are unchanged from round 1 and still come from the regulations and POMS SI 02005.050 D.1.

CI

All 37 checks pass at 27687ea37f (run 43279242854, started after the fix commit), including every Baseline and state shard, Household API Partners, Microsimulation and PR validation. None is pending or skipped. This is what shows the two new cases and the other 75 pass; no local run was made.

Branch

2 commits ahead of main and 18 behind. git merge-tree against main (8873d31) is clean, and main has not touched any of the 12 files since the merge base.

Whichever of #10532 and #10809 merges after this will need a rebase. These are notes, not findings.

Verdict: APPROVE

@DTrim99 DTrim99 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.

Approving. Each annual reader now has a part-year case; both expected values (11,760 and 5,760) hold by hand, and each case gives 14,760 and 11,760 if its reader goes back to the monthly variable. The couple eligibility test is tracked in #10851 and named in the code, and the facility-month wording follows 20 CFR 416.414(a)(1). No formula changed in the fix commit.

Four optional suggestions are in the report above; the most useful is giving one spouse earned income in the two new cases, which would pin the earned line of each reader too. CI is green at 27687ea37f. #10532 and #10809 will need a rebase after this merges.

@hua7450
hua7450 merged commit 38a3927 into PolicyEngine:main Nov 12, 2026
37 checks passed
MaxGhenis added a commit that referenced this pull request Nov 13, 2026
Keep both sides' cases in md_paa_countable_income.yaml: Case 4 from #10820
and #10856, and this branch's four monthly in-kind support cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Nov 13, 2026
md_paa_countable_income became monthly on this branch, and #10820 made
_ssi_couple_income combine a couple's income by each month's status. The
formula still halved that income by December's status, so in a year with
a part-year facility stay each spouse counted the couple's whole income
in the months before it. both_eligible now reads the same month.

Case 4 now expects 973.75 each in June, 1,447.50 and 480 in July, and
14,527.50 and 8,722.50 for 2025.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

SSI pools a married couple's income by December's facility status, so a part-year facility stay gives wrong SSI in the other months

2 participants