Skip to content

Apply 416.1163(e)(2) to deemed SSI and identify SSI couples by marriage, not tax roles - #10141

Open
MaxGhenis wants to merge 56 commits into
mainfrom
ssi-couples-and-deeming-cap
Open

MaxGhenis wants to merge 56 commits into
mainfrom
ssi-couples-and-deeming-cap

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10138.

Summary

We apply 20 CFR 416.1163(e)(2) before the SSI zero floor. Under spousal income deeming, a claimant receives the lesser of the couple computation and the individual benefit after their own countable income. State readers of uncapped_ssi receive the same federal shortfall.

SSI calculations identify each person's co-resident spouse through the existing resource-deeming helper. Joint claims, combined income and resources, spouse-deemed income and facility counts use that spouse. Explicit marital units preserve marriages for tax dependents and separate filers. We exclude a third adult's income and resources from the identified couple's budget.

The public variable ssi_marital_both_eligible remains available and uses the spouse test. The marital earned-income and unearned-income labels describe the two spouses' combined income. is_ssi_eligible_spouse retains its tax-unit-spouse label, and both qualifying spouses carry ssi_claim_is_joint.

For the individual comparison, we value in-kind support with the regular individual presumed maximum value and reapply income exclusions. SSA describes the individual computation in POMS SI 01320.150 B.1.b, SI 01320.400 C.2 and SI 00835.300.

For example, a claimant whose only income comes from in-kind support, with a spouse earning $500 monthly in 2026, receives $662.67 monthly: $994 − ($351.33 − $20). Using the reduced deeming PMV would pay $755.50.

California and other state readers

CAPI and the per-person state supplement use the identified spouse for category counts, facility separation, CAPI membership, countable income and SSI/SSP receipts. An unrelated recipient's income, payment or eligibility cannot enter the mixed couple's calculation.

Household Result
A disabled citizen, age 17, claimed by another adult, has a CAPI-eligible spouse with a $500 monthly pension. 2025; no cooking facilities. The spouse receives $9,255.60 of annual CAPI, compared with $10,269.72 on main.
Aged citizen spouses each receive $500 monthly beside an unrelated CAPI-eligible adult. 2025; marital units omitted; no cooking facilities. The couple receives $10,386.96 of annual supplement, matching explicit units after rounding each spouse's payment.

When exactly one spouse occupies a Title XIX facility, CAPI computes the spouses separately even when only the California facility input identifies the stay. An unrelated facility resident cannot separate the couple or contribute a payment-standard share.

We set exhausted mixed-couple CAPI benefits to zero when floating-point cancellation leaves a residual within the stored payment standard's precision. This prevents a residual from creating CAPI income eligibility.

For joint computations, the optional senior or disabled Medicaid income calculation combines the identified spouses' premiums and qualifying dependent premiums. The medically needy and 209(b) income calculations likewise exclude unrelated adults from couple budgets. California's Working Disabled income test excludes unrelated adults from spouse income.

We replace marital-unit sums with spouse totals in the changed state supplement readers. Colorado, Missouri, Oregon, South Dakota, Virginia and Iowa retain an explicitly declared marriage across households in specified state calculations.

Iowa and Oregon

Iowa's shared-room and prior-SSI inputs default to false. Same-month entry alone does not establish a shared room, and SSI awarded after separation does not establish prior receipt. See 441 IAC 51.4(3) and 6-B, PDF p.60.

For co-resident legacy situations, current or reported SSI approximates pre-entry receipt. Separated spouses need the explicit prior-SSI fact. Iowa retains the qualifying entry-month couple budget while excluding unrelated adults.

Oregon pays legally married spouses who each receive SSI and community-based care services, whether they occupy one facility or two. See OAR 461-155-0630(3) and the spouse definition in OAR 461-001-0000(62).

Validation

  • We moved all 43 mixed-household cases into 34 variable-named YAML files and preserved their expected values. The Makefile and workflow selectors route each moved case once.
  • We removed five overlapping explicit co-resident property examples. YAML retains the expected payments, and generated cases compare omitted and explicit marital units.
  • We retained six separated-spouse examples. They add or remove an unrelated adult in either spouse's household while preserving the marriage and independent tax units. They also cover pensions, resources, eligibility and care conditions outside the generated ranges.
  • At 312e3ff4ca, 47 YAML files covering SSI, Medicaid, CAPI and state supplements passed 850 cases, including 660 cases in the 34 destination files. The property file passed 80 cases in Rest, Python shard 1, process core-1.
  • The CAPI income-eligibility file passed seven cases, including the three zero-benefit households and the positive-cent case.
  • Before trimming examples, run 41978111346 recorded 203.827 seconds across the property's 80 cases in Rest, Python shard 4, process core-2. That contributed about 12% of the group's 1,656.749 pytest seconds.
  • At 312e3ff4ca, run 42153104620 recorded 93.159 seconds across the property's 80 cases, including fixtures. Its shared core-1 process ran 977 tests, took 29:06.43 including collection and peaked at 8,475,532 KiB (8.083 GiB).
  • The earlier shared core-2 process ran 340 tests, took 28:39.95 including collection and peaked at 6,723,600 KiB. The two process measurements cover different groups of files.
  • CI records memory for that shared process, so a file-specific peak and isolated import/collection time remain unavailable.
  • CI: the latest Pull request run, at 7e5c7a2cde (https://github.com/PolicyEngine/policyengine-us/actions/runs/42650265254), concluded success: all 36 jobs passed.

Merge of main at ba9bfdea75 (head 7e5c7a2cde). Only nj_medical_expense_deduction.yaml conflicted. Main (#10667) keeps the 72-year-old dependent's income and self-employed deduction on the dependent's own return. With this branch, MSP and the SSI buy-in pay the dependent's Part B premium, so the deduction is 0 (main: 220). Main's new Colorado AND-CS readers (#10161) now use this branch's couple test, as Old Age Pension does: state_is_married for the $20 disregard split and state_couple_total for spouse deeming. co_state_supplement.yaml Case 42 pins a couple beside a claimed third adult. At this head, one file per process: 99 files passed 1,931 tests with none failing, and 56 partner files that name SSI, Medicaid or supplement variables passed 252, none edited.

Not in this PR

With omitted marital units, spouses who file separately receive different couple budgets: only the spouse who claims a child includes that child's term. California's dependent-child premium case produces $23,880 of annual countable income per spouse with omitted marital units and $29,880 with explicit units. Main has the same difference.

Remaining SSI-methodology readers include is_optional_senior_or_disabled_income_eligible, medicaid_optional_senior_or_disabled_income_limit, is_optional_senior_or_disabled_asset_eligible, msp_asset_eligible, msp_countable_income, ms_hmw_resource_eligible, ms_wd_resource_eligible, ms_wd_countable_earned_income, ms_wd_countable_unearned_income, ms_wd_fpg and ms_wd_premium. They retain marital-unit calculations.

We still omit SSI's citizenship condition when classifying income couples (#10481). Spousal income deeming still omits the $30 medical-facility exception and related separation rules (#10455).

We still omit a non-applicant spouse's premiums under California Medicaid deeming. We also omit that spouse's maintenance need deduction (#10543). When only California's facility input separates a CAPI couple, the own-income calculation still omits in-kind support and the student earned-income exclusion.

A lone married pair with one spouse inferred as the other's tax dependent needs explicit tax roles. Parent and dependent facts exclude known nonspouses; unlabelled default groups can still infer a marriage.

Microsimulation impact

We compared main at the merge base ba9bfdea75 with this branch at 7e5c7a2cde on the default dataset for 2026. We estimate that SSI falls by $29.3m in total. Six sample people change, all down, by up to $2,957 each, and together they carry a weight of about 22,500. Their households' other benefits rise by $8.7m, so household benefits fall by $20.6m, and household net income falls by $20.6m. is_ssi_ineligible_spouse holds for 8.8 million weighted people, against 179.6 million on main, and is_ssi_spousal_deeming_applies doesn't change. Federal and state income tax, Medicaid eligibility and enrollment, the California and Colorado supplements, Colorado's Old Age Pension, Medicare Part B premiums and New Jersey's medical expense deduction don't change.

impact: on the default dataset in 2026, SSI falls by $29.3m for 6 sample people (22,500 weighted), and household net income falls by $20.6m. Federal and state income tax and Medicaid eligibility don't change.

axiom: TheAxiomFoundation/rulespec-us#1706 queued (20 CFR 416.1163(d)-(f), 416.1801(c), 416.1806, 416.1160(d); same corpus-release blocker as #1626). Resource deeming: TheAxiomFoundation/rulespec-us#1626 queued.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 12, 2026 05:12
SSI under spousal income deeming was capped at the individual Federal
benefit rate. 20 CFR 416.1163(e)(2) makes it the lesser of the couple
computation and the individual rate less the claimant's own countable
income, so a claimant with income of their own could get more with
deeming than without it, and lower income could lower SSI. Add
uncapped_ssi_without_spousal_deeming and take the lesser in
ssi_if_takes_up; deem nothing while the $30 rate applies (416.1163(f)(5)).

Joint claims and the ineligible spouse whose income is deemed were tied
to tax-unit head and spouse roles, which go to the two oldest adults of a
tax unit. Use the co-resident spouse test that resource deeming already
uses (416.1801(c), 416.1806, 416.1160(d)), require both members of a
joint claim to meet the citizenship condition, and combine couple income
and resources over the two spouses rather than the marital unit.

Fixes #10138.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 12, 2026 09:36
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Local results at 2924cd90da (macOS, not Linux CI):

  • policyengine_us/tests/core/test_ssi_spousal_deeming_invariants.py: 8 passed. 19 s user CPU, 1.0 GB peak RSS.
  • test_ssi_resource_deeming_invariants.py and test_ssi_state_supplement_medicaid_dependency.py: 24 passed. 73 s user CPU, 4.9 GB peak RSS (mostly the dependency module).
  • All 33 YAML files under tests/policy/baseline/gov/ssa/ssi, run one process at a time: pass, after the three is_ssi_spousal_resource_deeming_applies.yaml updates described in the body. The new cases in ssi_if_takes_up.yaml (10), ssi_claim_is_joint.yaml (10) and the deemed-income file (7) also pass.
  • The same invariant module, with the new variable replaced by the oracle's no-deeming amount, fails 7 of 8 tests on main.

A real microsimulation of the impact (merge base b2aaef3391 against this head, 2026, default dataset) is queued under the shared heavy lock and will be added to the body.

MaxGhenis and others added 3 commits October 12, 2026 08:09
Review round 1 found three regressions. Requiring the citizenship
condition in ssi_claim_is_joint broke California CAPI's couple rate and
Medicaid's senior/disabled countable income, which read the joint claim
as the SSI couple methodology, so the joint claim and ineligible-spouse
status test only aged, blind or disabled status again (still through the
co-resident spouse test). The resource test keeps #10040's treatment of a
citizenship-ineligible spouse through _ssi_resource_couple.

Alabama's supplement read uncapped_ssi > 0 and paid when SSI was zero, so
the lesser-of rule moves into uncapped_ssi before the floor. The
416.1163(f)(5) facility rule covered only the payment and is dropped for
a follow-up. The invariant test drops the citizenship axis, adds mixed
household sizes, and suppresses Hypothesis's slow-data check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test omitted tax and marital units, so the model grouped all nine
disabled people together and made person0 and person1 a head-and-spouse
couple, which the co-resident spouse test now reads as a joint claim.
The test checks citizenship status, so each person is single.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 2 found that a situation omitting marital units, with three
aged adults, now pays more in MA and AK. The joint claim now covers only
the head-and-spouse couple, but both supplements still summed the whole
marital unit. _ssi_couple_total adds the co-resident spouse's value, which
equals the marital-unit sum for units of one or two people. MA's maximum
and payment, AK's payment, and AK's claim type now use it, so the
household gets the same supplements as with explicit marital units
($100.86, $100.86, $128.82 in MA; $264, $264, $362 in AK).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 9 commits October 28, 2026 00:13
One textual conflict, in ssi_amount_if_eligible.py: main corrected the
medical treatment facility citation from 42 USC 1382(e)(1)(A) to (e)(1)(B)
(#10283), while this branch moved the living-arrangement adjustment into
_ssi_living_arrangement_amount.py. Keep the branch's helper call and carry
main's citation into the helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exercise five state supplement budgets across two households and correct Connecticut and Pennsylvania source comments.
Brings in #10601, which pays California's state supplement per person.
Resolves five conflicts: keeps main's joint parameter in the SSI
supplement helper with the PR's spouse-test couple total; takes main's
adds-based ca_state_supplement; keeps main's rate helper in the CAPI
standard and finds the couple through SSI's spouse test there; and keeps
the PR's spouse-test reads in the CAPI payment standard and amount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6KTnAzzn57sRN2SseD
#10601 found couples in the state supplement rates and the per-person
supplement through tax roles and marital-unit sums. This PR's couple test
pairs spouses by marriage, so the head-or-spouse guards there would drop
the couple rate for spouses a third adult claims, and a default marital
unit could add an unrelated member. Use _ssi_couple_total in the rate
counts, the per-person supplement's spouse, split-couple and couple-income
reads, and drop the head-or-spouse gate on the couple rate.

Adds cases for a CAPI spouse married to a claimed minor, two blind and two
CAPI-eligible spouses claimed by a third adult, and a mixed couple whose
claimed CAPI spouse's income sets the citizen's countable income. Updates
the PR's Case 17 for #10601's per-person rounding, and renumbers main's
second Case 18 to Case 21.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6KTnAzzn57sRN2SseD
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 0eed55f172215a25e8919ebb6e70908f5a8733bd (merge 2b0cfa402a of main 0315452583, then 0eed55f172). The branch was at 5bc46f232f when I started, the head the item names; nobody else pushed while I worked.

One case is left failing on purpose: main's Case 33 in ca_state_supplement_person.yaml. It's a case main added, so per item 4 I stopped on it rather than edit it. Details under item 4.

1. Merge (merge commit 2b0cfa402a, no rebase or force-push)

Main's tip was still 0315452583, four commits past the merge base ce0869c919. The five conflicts:

File Resolution
ssa/ssi/_ssi_state_supplement.py:42-44 Main's joint=None parameter and if joint is None: guard, plus the PR's _ssi_couple_total(person, period.this_year, couple_rate / 2)
states/ca/cdss/state_supplement/ca_state_supplement.py Main's file (adds = ["ca_state_supplement_person"])
states/ca/cdss/capi/_ca_capi_ssi_ssp_standard.py:1-9 Main's imports (ca_in_title_xix_facility, ca_state_supplement_rates) plus _ssi_couple_total and _ssi_spouse_index. The function bodies follow item 2. The PR's ca_capi_in_title_xix_facility is gone, and nothing on the merged tree references it.
states/ca/cdss/capi/ca_capi_payment_standard.py:32-36 The PR's capi_members
states/ca/cdss/capi/ca_capi_person.py:75-79 The PR's couple_with_spouse and couple_income

ca_state_supplement.yaml merged without a text conflict, but it needed two fixes (in 0eed55f172):

  • Case 17 (the PR's). It asserted ca_state_supplement_payment_standard, which Compute the California state supplement per person #10601 deleted. I removed that output line.
  • Duplicate name. Main's new "Case 18, 2026 mixed couple's supplement rounds up…" clashed with the PR's Case 18, so I renamed it Case 21. Its inputs and outputs are unchanged.

2. The couple test in #10601's code (0eed55f172)

Place Now
ca_capi_couple_computation (_ca_capi_ssi_ssp_standard.py:12-25) _ssi_couple_total(person, period.this_year, in_facility.astype(int)) == 1, with no head_or_spouse on the return. The docstring's last sentence now says the facility count covers the person and the spouse SSI's test identifies.
ca_capi_spouse_ssi_ssp (:28-40) add(person, period, ["ssi", "ca_state_supplement_person"])[safe_spouse] when _ssi_spouse_index >= 0 and that spouse isn't ca_capi_eligible_person. The pro rata SPM-unit share is gone.
ca_capi_ssi_ssp_standard (:63-64) _ssi_couple_total(person, period.this_year, couple_rate / 2)
ca_state_supplement_rates (_ca_state_supplement_rates.py:34-36, 79-86, 107) _ssi_couple_total for blind_count and aged_or_disabled_count. It returns couple ungated. The docstring now says the joint counts cover the person and the identified spouse, whatever their tax roles.
ca_state_supplement_person (ca_state_supplement_person.py:69-82, 100-102) fails_test has no head_or_spouse. spouse_capi_eligible, split_couple and couple_income use _ssi_couple_total. The PR's comment on the default marital unit is carried over.
ca_capi_person.py:35-37 Deleted "The same goes for a dependent who shares the couple's unit…", because a dependent can't reach separated: ssi_couple_computation_applies requires ssi_claim_is_joint, which requires _ssi_spouse_index >= 0 (ssi_claim_is_joint.py, formula). An unmarried dependent has no identified spouse.

git grep -nE 'marital_unit|head_or_spouse' -- policyengine_us/variables/gov/states/ca/cdss returns no lines. child in ca_state_supplement_rates still reads is_tax_unit_dependent to pick the category, as the item says; the pattern doesn't match it.

Other readers in the four new commits:

3. Witnesses, worked by hand before running

2025 values: FBR 967 / 1,450. California state shares: aged 239.94 + food 128.87 = 368.81 individual (1,335.81 total); 607.83 + 257.74 = 865.57 couple (2,315.57 total); disabled minor 97.27. 2026 FBR is 994 / 1,491; two-blind couple 833.35; blind individual 324.32. CAPI offsets are 0.

Married minor: ca_capi_person.yaml Case 23 (new).

  • The minor is 17, disabled, a citizen, and claimed by the 45-year-old. The LPR is 68 with 39 quarters and a $500 pension. Explicit marital units, no food preparation.
  • Countable income is (500 − 20) / 2 = 240 each.
  • Minor's SSI: 967 − 240 = 727/mo (8,724). This is the PR's SSI logic, which the merge doesn't touch, and it matches round 10.
  • Minor's ca_state_supplement_person, split couple:
    • individual standard 967 + 97.27 = 1,064.27;
    • mixed countable income max(0, 480 − (2,315.57 − 1,064.27)) = 0;
    • 1,064.27 − 727 − 0 = 337.27/mo (4,047.24).
  • So ca_capi_spouse_ssi_ssp for the LPR is 727 + 337.27 = 1,064.27.
  • LPR's ca_capi_person: payment standard 2,315.57 (the run gives 27,786.84 a year) − couple income 480 − 1,064.27 = 771.30/mo, 9,255.60 a year. Round 10's 13,302.84 left the 337.27 out.
  • Run: SSI [8,724, 0, 0], supplement [4,047.24, 0, 0], CAPI [0, 9,255.60, 0]. All match.
  • For reference, main gives SSI 11,604, supplement 1,167.24 and CAPI 10,269.72.

Two dependents married to each other (explicit marital unit, a third adult claims both):

  • ca_state_supplement_person.yaml Case 36 (new), both blind, 2026-01.
    • SSI 1,491 / 2 = 745.50 each.
    • The couple rate counts both as blind: max(833.35, 607.83) = 833.35, or 416.675 each, which SSA rounds up to 416.68.
    • As individuals each would take 324.32.
  • ca_capi_payment_standard.yaml Case 21 (new), both CAPI-eligible LPRs, 2026-01.
    • ca_capi_couple_computation is true, so the payment standard is (1,491 + 607.83) / 2 = 1,049.415 each.
    • As individuals each would take 1,233.94.
  • Main's Case 33 has the same shape and moves too; see item 4.

A claimed child where marital units are omitted: main's cases are unchanged and pass:

  • ca_state_supplement_person.yaml Cases 34 and 35;
  • ca_capi_person.yaml Cases 20, 21 and 22.

Extra case for mutant 5: ca_state_supplement_person.yaml Case 37 (new), 2025. This is Case 19's shape: a citizen with $500 of Social Security, married to a CAPI-eligible LPR whom a third adult claims. The LPR's pension is $1,500.

  • Countable income is (2,000 − 20) / 2 = 990 each, so the couple gets no SSI.
  • The citizen's countable income is max(480, 1,980 − 979.76) = 1,000.24, so the supplement is 1,335.81 − 1,000.24 = 335.57/mo (4,026.84).

4. Expected values

  • The PR's Case 17 in ca_state_supplement.yaml moves from 10,386.84 to 10,386.96.
    • Each spouse's per-person supplement is (2,315.57 − 1,450) / 2 = 432.785 with no excess income.
    • federal_supplement_payment rounds that up to 432.79, and 2 × 432.79 × 12 = 10,386.96.
    • The old SPM-unit formula computed 865.57 unrounded. I updated the value and the comment.
  • No other case from the PR moves. Cases 18 and 19 keep 7,449.72 / 4,269.72 and 7,449.72.
  • I stopped on main's Case 33 in ca_state_supplement_person.yaml ("married parents who are their child's tax dependents take the individual share", 2026-01) and did not edit it.
    • Main pins SSI [0, 994, 994] and supplement [0, 239.94, 239.94].
    • The merged tree gives SSI [0, 745.50, 745.50] and supplement [0, 303.92, 303.92].
    • The SSI moves under the PR's existing SSI spouse test, which pairs the explicit marital unit whatever the tax roles: 1,491 / 2 = 745.50.
    • The supplement then takes the couple rate: 607.83 / 2 = 303.915, which rounds up to 303.92.
    • Main's own note on the case reads: "When SSI identifies couples by marriage (PR 10141), the head-or-spouse guards … must go too, or these parents would get no supplement". It gives 303.92 as the couple level.
    • So I'd update it to these values and rename it "…take the couple share". That's for the hub to decide.
    • CI fails on this case until then.
  • Partner file: partners/analytics_coverage/edge_cases/state/ca/state_supplement.yaml passes 4 of 4 unchanged, so nothing there moves. I didn't edit it.

5. Mutants

Each mutant ran in its own process over the eight YAML files in ca/cdss/state_supplement and ca/cdss/capi, one file per process. I restored the file with git checkout after each, and git status was clean afterward. Case 33 fails under every mutant just as it does unmutated, so it isn't listed below.

Mutant Result Killed by
blind_count from person.marital_unit.sum(blind & head_or_spouse) killed ca_state_supplement_person.yaml Case 36 (303.92 instead of 416.68)
couple * head_or_spouse on ca_state_supplement_rates' return killed ca_state_supplement_person.yaml Cases 36 (0) and 37 (0), ca_capi_payment_standard.yaml Case 21 (745.50), ca_capi_person.yaml Case 23 (4,062.18), ca_state_supplement.yaml Case 19 (CAPI 563.70)
& head_or_spouse on ca_capi_couple_computation's return killed ca_capi_payment_standard.yaml Case 21 (1,233.94 instead of 1,049.415), ca_capi_person.yaml Case 23 (supplement 1,167.24), ca_state_supplement.yaml Case 19 (CAPI 10,269.72), ca_state_supplement_person.yaml Case 36 (324.32)
Main's ca_capi_spouse_ssi_ssp killed ca_capi_person.yaml Case 23 (13,149.72 instead of 9,255.60)
couple_income = person.marital_unit.sum(ssi_countable_income * head_or_spouse) killed ca_state_supplement_person.yaml Case 37 (10,269.72 instead of 4,026.84), ca_state_supplement.yaml Case 18 (1,446.84 instead of 7,449.72)
spouse_capi_eligible from person.marital_unit.sum(capi_eligible & head_or_spouse) > 0 killed ca_state_supplement.yaml Cases 17 (14,899.44) and 19 (7,329.72 instead of 7,449.72), ca_state_supplement_person.yaml Case 37 (4,149.72)

6. Tests at 0eed55f172, one file per process, 4 at a time

1,045 passed and 1 failed across 45 files. The one failure is Case 33.

Group Results
#10601's 12 test files that still exist ca_capi.yaml 10, ca_capi_person.yaml 23, ca_state_supplement.yaml 19, ca_state_supplement_eligible_person.yaml 8, ca_state_supplement_person.yaml 36 passed + 1 failed, integration.yaml 6, the three San Bernardino general relief files 10 / 14 / 15, snap_unearned_income_state_ssi_supplements.yaml 4, test_snap_child_support_treatment_invariants.py 23, the partner file 4
This PR's changed test files (from the 126-file listing) test_ssi_spousal_deeming_invariants.py 80 (I installed hypothesis, which the dev extras list), test_calculate_chunked_validation.py 80, ia_ssa.yaml 121, ssi_state_supplement_three_adult.yaml 37, ca_capi_payment_standard.yaml 21, ca_capi_three_adult.yaml 6; the rest all pass
Source conflicts test_source_conflicts.py 103

Each test I added is checked against the expected value I worked out by hand.

CI

Pull request run 41971221928 at 0eed55f172 concluded failure: 26 jobs succeeded, 1 was skipped, and 1 failed. The failure is Baseline states-shard-1, where 360 passed and 1 failed: main's Case 33 in ca_state_supplement_person.yaml, the case under item 4, with SSI 745.50 against the pinned 994. Microsimulation passed.

Judgment calls

  • The merge commit already holds item 2's changes for _ca_capi_ssi_ssp_standard.py. The item 1 table said to resolve that file's bodies by item 2. The other item 2 reads are in 0eed55f172.
  • ca_state_supplement_rates now returns a couple rate for everyone, dependents included. For a non-joint person it is the person's-category couple rate, which ssi_state_supplement reads only under spousal deeming, so it pays only when SSI deeming applies. No test moved from this.
  • ca_capi_spouse_ssi_ssp reads the spouse's ca_state_supplement_person directly. For a split couple that is the supplement with the individual standard, which is the SSI/SSP the spouse is actually paid.
  • I renamed main's second Case 18 to Case 21, a name change only.
  • Body edits:
    • added the merge paragraph;
    • removed the pro rata sentence;
    • pointed the Limits bullet at ca_state_supplement_person, with 10,386.96;
    • added this head's counts and Case 33 to Validation;
    • added the six mutant rows, and marked the old ca_state_supplement mutant row as pre-Compute the California state supplement per person #10601.
  • impact: and axiom: are untouched. On main at 0315452583, Case 19's citizen still gets 4,425.72, so the body's "main: $4,425.72" stands.

Couldn't check

  • I fetched no law this round. Item 2 changes no rate or legal reading, only which members count as the couple.
  • I ran no population impact.

Proposed follow-up

  • Title: "California SSP: update Case 33 now that SSI pairs claimed spouses."
  • Main's Case 33 pinned the individual share as a known limitation of the tax-role couple test. Under this PR the parents get SSI 745.50 and a supplement of 303.92 each, the couple level the case's own note gives. This only applies if the hub doesn't make the update in this PR.

Generated by Claude Code

SSI now pairs these parents through their marital unit, so main's Case 33
moves from the individual share to the couple share. Its note named this PR
as the change that would lift the limitation.

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

MaxGhenis commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Round 11 review at head 8fd9ce678ea65dae05d4b1c4f3393d193ef45383 (unchanged since the brief). Main has since moved to b0976dcf8d (#10579, #10308, tax only); the PR shares no file with those commits and still merges cleanly.

Firm so far:

  • Merge: comparing the PR's net diff per file before (ce0869c919..5bc46f232f) and after (0315452583..8fd9ce678e), ignoring index and hunk lines, only 11 files moved, all named in the brief's items. Nothing under .github/ or partners/.
  • git grep -nE 'marital_unit|head_or_spouse' -- policyengine_us/variables/gov/states/ca/cdss returns nothing.
  • Witnesses: Cases 23, 36, 37, 21, 17 and 33 match my hand values.
  • SSI: deemed benefit is capped at the individual rate instead of the no-deeming amount, and couples are identified by tax roles #10138 reproduces on main (SSI 9,402 vs 6,438) and is fixed at head.
  • P2 (body): the California table's first row says "down from $13,302.84". That is the round-10 PR head, not main; main gives $10,269.72.
  • CI: every Baseline shard passes, including states-shard-1; Contrib and Microsimulation still running.

Still running: test files one per process, and eight mutants (the hand-off's six plus two of mine).


Generated by Claude Code

@DTrim99

DTrim99 commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Apply 416.1163(e)(2) to deemed SSI and identify SSI couples by marriage, not tax role

Summary

The core change is right, and the tests check it well. uncapped_ssi now takes the lesser of the couple computation and the individual rate less the claimant's own countable income. That matches 20 CFR 416.1163(e)(2) word for word. The cap applies before the zero floor, so state readers of the negative shortfall see the same rule. The regulation's own examples (416.1163(g) Examples 2 and 3) are encoded as tests, and #10138's case reproduces: $6,438 a year, against $9,402 on main.

SSI couples are now found with the existing co-resident spouse test, _ssi_spouse_index, which resource deeming has used since before this PR. Tax-unit roles no longer decide them. This applies to joint claims, the ineligible spouse, combined couple income and resources, and the facility split. So nothing is reinvented. A married couple who are both someone else's tax dependents now get the couple computation, and a dependent spouse's income is now deemed. Both follow 416.1801(c), 416.1806 and 416.1160(d). Spouses in different households are no longer a joint claim (416.1801(c)(3)).

About 40 state readers drop marital_unit sums for the spouse total. The 37 cases in the three-adult YAML and a property test cover that. A few of the readers keep a declared marriage across households for named state rules (CO, MO, OR, SD, VA, IA).

No partner test files changed. CI is 36/36 green at the head, and the branch merges cleanly with main.

The open items are about where the tests live and what the PR records, not about the policy logic:

  • two scenario-named test files;
  • an unreported cost for the new Python property file;
  • two inaccurate PR-body statements (a suggestion);
  • rulespec-us#1706 companion cases that contradict the head's known, tracked limits.

Critical

None.

Should address

S1. New tests sit in scenario-named files, not variable-named ones.

policyengine_us/tests/policy/baseline/gov/ssa/ssi/ssi_state_supplement_three_adult.yaml (new, 1,558 lines, 37 cases) tests variables from about 30 state programs: CO OAP, CT, DC, DE, FL, HI, IN, KY, ME, MI, MN, MO, MT, NE, NJ, NV, NY, OH, OK, OR, PA, RI, SD, UT, VA, VT, WA and WI. It sits under gov/ssa/ssi. policyengine_us/tests/policy/baseline/gov/states/ca/cdss/capi/ca_capi_three_adult.yaml (new, 6 cases) is named the same way.

CLAUDE.md, "Test design and CI cost", says to add mixed-household cases "to the corresponding variable-named YAML files" and to "Avoid generic policy test files such as regression.yaml or vectorized.yaml".

The three-adult file still carries header comments naming the per-state files its cases came from, such as # co/ssa/oap/co_oap_three_adult.yaml, # ct/dss/ssp/third_adult.yaml and # or/odhs/ssp/third_adult.yaml. None of those files exists.

Fix: move each case into the variable-named file of the variable it asserts, for example co_oap_*.yaml, ct_ssp_resource_eligible.yaml, dc_ossp_payment_amount.yaml and ca_capi_person.yaml/ca_capi_payment_standard.yaml. Then drop the stale path headers. Check the Makefile/workflow selectors so the moved cases still run exactly once.

S2. The new Python property test's CI cost isn't reported.

policyengine_us/tests/core/test_ssi_spousal_deeming_invariants.py is new: 1,241 lines and 80 tests, using Hypothesis. It builds two grid simulations and then hundreds of small three-person simulations, including two per example per state for every implemented supplement.

CLAUDE.md requires, for new Python tests that build simulations, "before/after elapsed time (including setup) and peak memory", plus the affected CI group. The PR body gives only pass counts.

From the CI log of run 41978111346, the file ran from 06:33:26 to 06:36:50, about 3 min 24 s. That was in the core-2 process of Rest (Python shard 4), whose pytest run took 1,656.8 s and whose job took 30m44s. So the file is about 12% of that shard. Peak memory isn't reported.

test_state_supplements_do_not_depend_on_omitted_marital_units also overlaps the 37 YAML three-adult cases: the same states, the same omitted-versus-explicit comparison.

Fix: add the measurement to the PR body. Use the existing CI timing, and give memory or say it is unavailable. Then either justify the overlap with the YAML cases or trim the explicit @example rows that repeat them.

S3. rulespec-us#1706 contradicts the head on three of its companion cases.

The issue is OPEN, labelled pe-parity and dispatch-ready, and it records the blocker (see the axiom section). But its companion cases encode the law where this head knowingly doesn't, and the issue doesn't say so:

Fix: add a comment to #1706. List C2, D7 and C1 as known PolicyEngine divergences with their tracking issues (#10481, #10455), and map output 6 to ssi_claim_is_joint. That way a later parity run won't treat them as encoder errors. Alternatively, narrow those cases. The PR's axiom line can stay as it is.

Suggestions

G1. Two PR-body statements are inaccurate (PR-body housekeeping).

  • California table, first row. It says the LPR's CAPI is "$9,255.60 a year, down from $13,302.84". $13,302.84 is an earlier head of this PR. Main gives $10,269.72, as the author's own hand-off comment states ("For reference, main gives SSI 11,604, supplement 1,167.24 and CAPI 10,269.72"). The author's in-progress self-check flagged this too, and the body still has it.
  • Iowa section. It cites "6-B, printed p.60". File page 60 of the manual is printed "Page 56". The href's #page=60 anchor is right; the label isn't. CLAUDE.md: "PDF page numbers are file pages (1-indexed), not printed pages."

Fix: change the first to "compared with $10,269.72 on main", and the second to "6-B, PDF p.60".

G2. The no-deeming limit values in-kind support at the deemed rate.

uncapped_ssi_without_spousal_deeming.py:32 takes own income as ssi_countable_income − ssi_income_deemed_from_ineligible_spouse. Under deeming, ssi_pmv_amount.py:37 halves the PMV to 1/6 of the couple rate + $10 (POMS SI 01320.150). POMS gives the regular PMV, 1/3 of the individual rate + $20, "when there is no income to be deemed".

Take a claimant whose only income is ISM, with a spouse earning $500 in 2026:

  • the limit here is $994 − ($258.50 − $20) = $755.50;
  • the regular PMV gives $994 − ($351.33 − $20) = $662.67;
  • the difference is $92.83 a month.

This isn't a regression, because main capped at $994. Please confirm SSA's practice. Then either compute own income with the regular PMV, or add a NOTE.

G3. Link the tracked deferrals where the code makes them. Add # NOTE: … (issue 10481) beside the citizenship carve-out in ssi_claim_is_joint.py:26 and the README. Add # NOTE: 416.1163(f)(5) not modeled (issue 10455) in uncapped_ssi_without_spousal_deeming.py and is_ssi_spousal_deeming_applies.py.

G4. Housekeeping.

  • ssi_marital_both_eligible (still marital_unit.sum(eligible) == 2) now has no formula reader; only its own YAML uses it. Delete it after checking API consumers, or redefine it with the spouse test.
  • ssi_marital_earned_income and ssi_marital_unearned_income keep the label "Total SSI … income for a marital unit", though they now combine only the two spouses.

G5. The Oregon "Hub ruling" pointer points nowhere.

or_ssp.py:46, or_ssp.yaml:606-607 and test_ssi_spousal_deeming_invariants.py:774-775 cite "Hub ruling: us-or:ssp-community-care-spouses-separate-facilities". That id is in neither source_conflicts/candidates.yaml nor the vendored register, though it looks like a register id.

OAR 461-155-0630(3) reads "Spouses who each receive Supplemental Security Income (SSI) and receive services in a community-based care facility". That text doesn't say the spouses must share a facility.

Fix: if the text and the spouse definition settle the reading, cite them and drop the internal pointer. If it was a true tie, add a candidates.yaml entry (CONTRIBUTING.md#source-conflicts, step 1).

G6. Couple budgets: asymmetric dependent term and readers still on marital units.

  • _ssi_couple_budget_total (_ssi_spouses.py:116-131) adds dependents through person.tax_unit.sum.

    • With omitted marital units, a couple filing separately get different couple budgets: only the spouse whose tax unit claims the child adds the child's term.
    • A dependent child's premiums count with omitted marital units ($23,880) but not with explicit ones ($29,880). That one is inherited from main.

    Consider a spouse-symmetric dependent term, or document both.

  • The remaining marital-unit readers still treat a couple in a default unit as a non-couple: the eligibility sum in is_optional_senior_or_disabled_income_eligible, medicaid_optional_senior_or_disabled_income_limit and the asset test. The new California case's comment ("the 21,000 sum would pass the individual limit") relies on that. Link the follow-up issue the body promises.

G7. Performance (optional). _ssi_spouse_index reruns its loop over household positions at every call site: about 100 helper calls per period. The Microsimulation job passed in 40 min. A per-period cache would bound the cost as more readers adopt it.

G8. rulespec-us#1626 (optional). Add this PR's changed resource cases to the issue's companion manifest:

  • married tax dependents are now combined as a joint claim;
  • an aged spouse failing the citizenship test now has resources deemed, with the ineligible-spouse exclusions (is_ssi_spousal_resource_deeming_applies.yaml).

Verified correct

Law. I read 416.1163(d)-(f), 416.1801(c), 416.1806, 416.1802 and 416.1160(d) from eCFR.

  • (e)(2): "your benefit is the lesser of the amount computed under the rules in paragraph (d)(2) … or the amount remaining after we subtract only your own countable income from an individual's Federal benefit rate." The code matches it in uncapped_ssi.
  • ssi_if_takes_up keeps only the floor.
  • Own income equals the personal countable income, because ssi_countable_income = personal + deemed.
  • The comparison applies the same living-arrangement adjustment (Case 7).

Hand-checks. 2026 rates are $994 / $1,491; 1986 rates are $336 / $504.

  1. ssi_if_takes_up Case 5:
    • spouse $100 + $400 = $500 > $497, so deeming applies;
    • couple income: ($843 − $20) + ($400 − $65) / 2 = $990.50, and $1,491 − $990.50 = $500.50;
    • without deeming: $994 − $723 = $271;
    • SSI $271.
  2. Case 6: $478 ≤ $497, so no deeming: $994 − $559 = $435.
  3. Case 7:
    • without deeming: $994 × 2/3 − $380 = $282.67;
    • couple: $994 − $647.50 = $346.50;
    • $282.67 is the lesser.
  4. Case 8:
    • couple: $1,491 − ($1,080 + $267.50) = $143.50;
    • without deeming: $994 − $1,080 = −$86;
    • SSI $0 (main paid $143.50).
  5. Case 9 (Example 3): min($504 − $148, $336 − $80) = $256, or $3,072 a year.
  6. Case 10 (Example 2): the allocation is $168, so countable income is $64 + $168 = $232, and min($272, $336) = $272, or $3,264 a year.
  7. ssi_income_deemed_from_ineligible_spouse, the dependent-spouse case:
    • couple income: $580 + $967.50 = $1,547.50, more than $1,491;
    • deemed: $967.50;
    • SSI [$114, $0, $0].
  8. ssi_claim_is_joint Case 6: ($1,491 − $1,280) / 2 = $105.50 each; the elder gets $994 − $880 = $114.
  9. ssi_claim_is_joint Case 8: ($1,491 − $780) / 2 = $355.50 each.
  10. SSI: deemed benefit is capped at the individual rate instead of the no-deeming amount, and couples are identified by tax roles #10138: ($1,000 − $85) / 2 = $457.50, and $994 − $457.50 = $536.50, or $6,438 a year. With deeming: $1,491 − $707.50 = $783.50, capped at $536.50.
  11. California:
    • Case 33: $1,491 / 2 = $745.50; $607.83 / 2 rounds to $303.92;
    • Case 36: $833.35 / 2 rounds to $416.68;
    • Case 37: max($480, $1,980 − $979.76) = $1,000.24, and $1,335.81 − $1,000.24 = $335.57, or $4,026.84 a year;
    • Case 17: $865.57 / 2 rounds to $432.79, and × 24 = $10,386.96.
  12. Iowa retained entry: $1,491 − ($275 − $20) = $1,236, and ($275 + $1,236) / 2 = $755.50. This matches 6-B's worked example (PDF p.69, Mr. and Mrs. K).
  13. California non-MAGI:
    • ($36,000 + $24,000 − $240) / 2 = $29,880;
    • ($24,120 + $12,120 − $240) / 2 = $18,000.
  14. Oklahoma three-adult:
    • couple: ($1,571 − $1,491) / 2 = $40 each;
    • third adult: $1,034 − ($80 + $914) = $40.

Entities.

  • A couple filing separately in one two-person marital unit is still paired, as on main.
  • A married couple who are both a son's dependents is a joint claim.
  • Spouses in different households are not a joint claim.
  • With explicit units, two couples are paired separately.
  • A third adult in a default unit is excluded across SSI, CAPI, the SSPs, MT LIHEAP, MS HMW, CA WDP, MN MSA and the Medicaid spend-down readers.
  • The one-marital-unit default limitation is documented, and calculate_chunked guards it (test_calculate_chunked_validation.py).
  • The vectorized values[spouse] reads are all guarded by spouse >= 0.

Downstream readers.

  • Every reader of ssi_claim_is_joint, ssi_couple_computation_applies, is_ssi_ineligible_spouse and uncapped_ssi gets the new definitions.
  • KY reads is_ssi_eligible_individual/is_ssi_eligible_spouse, and its OR still covers every eligible member.
  • git grep -nE 'marital_unit|head_or_spouse' under ca/cdss returns nothing.
  • The remaining marital_unit readers are the ones the body names (MSP, the Medicaid limit/asset/eligibility readers, MS HMW resources, MS Working Disabled).

Iowa and the facility split. The shared-room input follows 441 IAC 51.4(3): "Partners in a marriage who both enter the same room of the residential care facility in the same month". The prior-SSI input follows 6-B PDF p.60 ("When one or both spouses are receiving SSI before one or both enters an RCF…").

Other checks.

  • No new state-gated int or enum key, so the Add New Hampshire state supplementation for 2026 #10156 pattern doesn't apply.
  • The changelog fragment is top-level: changelog.d/ssi-couples-and-deeming-cap.fixed.md.
  • No file under policyengine_us/tests/policy/baseline/partners/** changed. The CA supplement partner file passes unchanged (Household API Partners job green).

Axiom line assessment

The line reads: "TheAxiomFoundation/rulespec-us#1706 queued (20 CFR 416.1163(d)-(f), 416.1801(c), 416.1806, 416.1160(d); same corpus-release blocker as #1626). Resource deeming: TheAxiomFoundation/rulespec-us#1626 queued."

#1706 is OPEN, labelled pe-parity, and was last updated 2026-10-25. It is dispatch-ready:

  • module paths (us/regulations/20-cfr/416/1163.yaml, 1801.yaml, 1806.yaml, 1160.yaml);
  • corpus citations and pinned corpus line numbers;
  • the verbatim law;
  • 8 required outputs, with the explicit "Output 4 must never exceed output 3" property;
  • companion cases hand-computed from the CFR examples and SSA's rate table, the same sources as the YAML Cases 5-10;
  • the blocker: the pinned release us-rulespec-2026-08-08-obbb-alien-snap lacks the 2026-07-23-title-20-part-416 scope, and the run needs the signed encoder.

It matches the head on (e)(2) and on identifying spouses without tax roles. It contradicts the head on C2, D7 and output 6/C1 (S3).

#1626 is OPEN, labelled pe-parity, was last updated 2026-10-10, and records the same blocker. It also says 416.1205, .1851 and .1856 are missing from the corpus. It is consistent with the head's resource changes. Updating its manifest is optional (G8).

The line is valid apart from the S3 note.

CI status

gh pr checks 10141, rechecked at the end: 36/36 pass. That covers every Baseline shard, Contrib, Rest Python shards 1-9, Microsimulation, Household API Partners and PR validation. Run 41978111346 ran at head 8fd9ce6. Nothing is pending and nothing failed.

Branch status

  • Position. The branch is 44 commits ahead of main and 27 behind (main at cc8f4c9).
  • Merge with main. git merge-tree --write-tree PolicyEngine/main 8fd9ce678e is clean (tree 6a28187563).
    • Only one file changed on both sides since the merge base, medicaid_optional_senior_or_disabled_countable_income.py, and main's change there removes a comment (Add 2015-2023 Medicare IRMAA brackets and 2015-2020 Part B premiums #10112).
    • Main's new CO/DE/HI/SC LIHEAP tests input ssi directly, so this PR's SSI changes don't move them.
    • Main's test_source_conflicts.py change only adds an SC KNOWN_UNMARKED entry.
  • Sibling PRs. These are branch notes, not findings. Each would need a rebase after the other merges:
PR Topic Conflicted files
#10146 Child allocations 13, including _ssi_spouses.py, uncapped_ssi.py, ssi_if_takes_up.py, both spouse-deemed income files, the Medicaid deemed-income file and its YAML, ca_wdp_*, ia_ssa_federal.py, va_ag.yaml
#10640 4: is_209b_ssi_recipient_income_eligible_for_medicaid.py, is_medically_needy_for_medicaid.py and its YAML, the Medicaid deemed-income file
#10532 4: ssi_earned_income_deemed_from_ineligible_spouse.py, ssi_marital_earned_income.py, in_ssp_sapn.py, ca_state_supplement.yaml
#10639 5: meets_ssi_resource_test.py, ia_ssa_eligible.py, mn_msa_resource_eligible.py, va_ag_eligible.py, is_medically_needy_for_medicaid.yaml

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 changes per the review above: 0 critical, 3 should (scenario-named test files instead of variable-named ones; no CI cost report for the new Python property test; rulespec-us#1706 companion cases contradict the head's tracked limits without saying so), 8 suggestions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 4 commits November 5, 2026 19:29
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the regular presumed maximum value in the no-deeming comparison and preserve the claimant income exclusions. Link the tracked citizenship and facility exceptions beside their current treatment.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
8fd9ce6
METHODOLOGY_CHOICE: yes

Round 11, a delta review of the merge 2b0cfa402a, 0eed55f172 and 8fd9ce678e on top of the round 10 approval at 5bc46f232f. The head hasn't moved since the brief. Main has since moved to 8d6ad0f387 (LIHEAP for CA, DE, HI and SC, #10076, #10112, #10579, #10308). The only file it shares with this PR is medicaid_optional_senior_or_disabled_countable_income.py, where #10112 removes one comment. git merge-tree --write-tree merges the PR head with current main cleanly. The new LIHEAP code reads household SSI receipt only, with no couple reads.

METHODOLOGY_CHOICE is yes because the PR identifies SSI couples by marriage and co-residence rather than tax roles. It also keeps the round-approved readings this delta extends: the lesser-of-totals mixed-couple supplement and citizenship left out of couple status. This round doesn't reopen them.

Findings

No P1.

P2

1. The body's California table compares the married-minor witness with an old PR head, not main.

  • Where: PR body, "California" table, first row.
  • It says "The LPR's CAPI: $9,255.60 a year, down from $13,302.84". 13,302.84 is round 10's PR head, which left the minor's supplement out. A reader takes "down from" as the change against main. Main at 0315452583 gives the LPR $10,269.72 of CAPI, the minor $11,604 of SSI and a $1,167.24 supplement.
  • "whom the citizen doesn't claim" also misstates the household: a third adult claims the minor.
  • Command: a probe of ca_capi_person.yaml Case 23's household at head and on a main worktree, the same script on each. Head gives SSI [727, 0, 0]/month, supplement [337.27, 0, 0] and CAPI [0, 771.30, 0]. Main gives SSI [967, 0, 0], supplement [97.27, 0, 0] and CAPI [0, 855.81, 0].
  • Replacement row: | A 17-year-old disabled citizen whom a third adult claims, married to a CAPI-eligible LPR with a $500 monthly pension, 2025, no food preparation | The LPR's CAPI: $9,255.60 a year (main: $10,269.72). The minor's SSI is $8,724 and supplement $4,047.24 (main: $11,604 and $1,167.24) |

P3

2. Validation and mutation sections report counts at 0eed55f172, not the validation head.

  • Where: PR body, "Validation" and "Mutation checks".
  • "Validation head: 8fd9ce678e" is followed by "At 0eed55f172, 1,045 tests passed across 45 files". The mutation intro says "Each mutant restores one replaced marital_unit line", but two rows restore head_or_spouse gates, not marital_unit lines.
  • Replacement for the Validation paragraph: "At 8fd9ce678e, 1,188 tests passed and none failed across 52 files, one file per process. They cover every test file Compute the California state supplement per person #10601 changed that still exists, every test file the PR's history changes (including two federal partner files and the California supplement partner file, 4 of 4, none edited), and test_source_conflicts.py (103). test_ssi_spousal_deeming_invariants.py passed 80 and test_calculate_chunked_validation.py 80."
  • Replacement for the mutation intro: "Each mutant restores one replaced marital_unit read or head_or_spouse gate and runs the affected YAML files, one file per process. We reversed each patch afterwards."

3. The body's renaming note covers one of two renamed main cases.

  • Where: PR body, "Validation", "main's second Case 18 becomes Case 21".
  • On the merged tree, main's Case 17 ("2027 aged individual keeps the state share…") is also Case 20, after the PR's Cases 17 to 19. I diffed main's lines 642 onward against head's lines 788 onward with case numbers stripped: they're identical. Cases 1 to 16 match main byte for byte.
  • Replacement: "Main's Cases 17 and 18 become Cases 20 and 21, with inputs and outputs unchanged."

4. The PR's Case 17 in ca_state_supplement.yaml lost an assertion in the merge and has nothing per person in its place.

  • Where: policyengine_us/tests/policy/baseline/gov/states/ca/cdss/state_supplement/ca_state_supplement.yaml, Case 17 output.
  • Compute the California state supplement per person #10601 deleted ca_state_supplement_payment_standard, so 0eed55f172 dropped that line. The case now asserts only the SPM-unit total, 10,386.96. A mutant that gave one spouse 865.57 and the other 0 would pass.
  • Command: a probe of Case 17's household at head gives ca_state_supplement_person@2025 [5,193.48, 5,193.48, 0] and ca_capi_person [0, 0, 10,269.72].
  • Add after the ca_state_supplement line: ca_state_supplement_person: [5_193.48, 5_193.48, 0] # 432.79 * 12 each

5. Overlap with open work. #10146 ("Limit SSI spousal-deeming child allocations…", base main) also edits _ssi_spouses.py, _ssi_state_supplement.py, ca_capi_person.py and ca_state_supplement.py. #10532 edits ca_state_supplement.yaml. Whichever lands second needs a merge. No action is needed in this PR.

Checked and sound

The merge.

  • Method: I compared the PR's net change per file before and after the merge, git diff ce0869c919 5bc46f232f against git diff 0315452583 8fd9ce678e, with index, @@ and diff --git lines removed.
  • Only 11 files differ, all in the brief's items: the five conflict files, _ca_state_supplement_rates.py, ca_state_supplement_person.py and four California test files. The file set grew from 126 to 130. Three of the new files are California tests and the other is _ca_state_supplement_rates.py, which Compute the California state supplement per person #10601 added and 0eed55f172 edits. ca_state_supplement.py dropped out because head now equals main there.
  • Resolutions, read against both parents:
    • _ssi_state_supplement.py: main's joint=None and if joint is None:, plus _ssi_couple_total(person, period.this_year, couple_rate / 2).
    • ca_state_supplement.py: main's file, unchanged.
    • _ca_capi_ssi_ssp_standard.py: main's imports and ca_state_supplement_rates call, with the item 2 bodies. The PR's ca_capi_in_title_xix_facility is gone, and main's ca_in_title_xix_facility reads the same two inputs, the California input and SSI's medical-treatment arrangement.
    • ca_capi_payment_standard.py: the PR's capi_members through _ssi_couple_total.
    • ca_capi_person.py: the PR's couple_with_spouse and couple_income.
  • Nothing under .github/ or policyengine_us/tests/policy/baseline/partners/ differs from main.

The couple reads.

  • git grep -nE 'marital_unit|head_or_spouse' -- policyengine_us/variables/gov/states/ca/cdss returns nothing (exit 1). child in ca_state_supplement_rates still reads is_tax_unit_dependent, as intended.
  • I called _ssi_spouse_index, ca_capi_couple_computation, ca_state_supplement_rates, ca_capi_ssi_ssp_standard and ca_capi_spouse_ssi_ssp through simulation populations, at head and on a main worktree, for 2025-01:
    • Ordinary couple (citizen head with $500 of Social Security, LPR spouse with a $500 pension, explicit unit). Head and main agree on everything a reader uses: couple rate 607.83, standards 1,206.94 / 2,057.83, citizen supplement 491.94, LPR CAPI 350.89.
      • Main's ca_capi_spouse_ssi_ssp also returned the citizen's own 726.94 on the citizen's row, because its marital-unit sum includes the person. Head returns 0 there. Only CAPI-eligible rows read it (ca_capi_person is defined_for CAPI eligibility, and the payment standard's non-CAPI rows don't use it), so nothing moves.
    • Couple beside an unrelated CAPI-eligible adult, marital units omitted. Head pairs the couple and leaves the adult unpaired. The adult gets individual CAPI of 726.94; the couple gets the same values as the ordinary couple. Main put the adult into the couple's sums: couple standard 2,361.745 and CAPI 440.87 each for the LPR spouse and the adult.
      • With one tax unit and no roles, both head and main pair the two oldest adults, the documented default-group limit and not this delta.
    • Parents who are their child's tax dependents (Case 33's shape, explicit units). Head pairs them through their two-person marital unit and gives the couple rate. Main gave a couple rate of 0 and individual treatment.
      • With marital units omitted, head still gives the individual amounts (SSI 994, supplement 239.94), as main does. Nothing in that situation says they are married.
    • A spouse whose partner is another person's tax dependent. Head pairs them (joint, couple standard 2,057.83). Main's ca_capi_couple_computation was false, so the citizen got 239.94 instead of 491.94.
    • Married minor (Case 23's shape): head gives 727 / 337.27 / 771.30. Main gave 967 / 97.27 / 855.81.

The judgment calls.

  • ca_state_supplement_rates returns a couple rate for everyone. Readers of the couple rate:
    • ssi_state_supplement uses couple_total only where joint, and couple_rate otherwise only in deemed_payment, under is_ssi_spousal_deeming_applies. That needs deemed spouse income.
    • ca_capi_ssi_ssp_standard's couple standard, read by ca_capi_payment_standard only where joint.
    • ca_state_supplement_person's mixed_countable_income, used only where spouse_capi_eligible, which needs joint.
    • ca_capi_person's couple_supplement, used in deemed_benefit only under deeming & ~joint or unpaid_spouse, which needs joint.
    • So no single person's row uses the couple rate. A married dependent under spousal deeming now takes the category couple rate where main gave 0. That follows the docstring's POMS SI 01320.430 C.7 reading.
  • ca_capi_spouse_ssi_ssp reads the spouse's ca_state_supplement_person directly.
    • It can't cycle: ca_state_supplement_person reads ca_capi_eligible_person, which reads only status inputs, plus ssi and the standards function. It never reads ca_capi_person or ca_capi_spouse_ssi_ssp. Every run completed without a cycle error.
    • It counts the spouse once: it indexes one spouse row, 727 + 337.27 = 1,064.27 in Case 23.
    • When the spouse is CAPI-eligible it returns 0, so the couple takes the both-CAPI or unpaid-spouse path. The ordinary-couple probe gives 0 on the citizen row whose spouse is CAPI-eligible.

Witnesses, computed by hand before I ran them.

  • Parameters at this head: 2025 FBR 967 / 1,450, 2026 FBR 994 / 1,491. California aged 239.94 / 607.83, food 128.87 / 257.74, blind 324.32 / 747.44 / 833.35, disabled minor 97.27. CAPI offsets 0; minimum payment $1.
  • ca_capi_person.yaml Case 23, the married minor (2025, no food preparation): individual standard 967 + 97.27 = 1,064.27. Couple standard 1,450 + 607.83 + 257.74 = 2,315.57. Couple income 480, split 240 each.
    • The minor's SSI of 727 = 967 − 240 is the round-10 SSI reading, which this delta doesn't touch.
    • Supplement: 1,064.27 − 727 − max(0, 480 − 1,251.30) = 337.27 a month.
    • LPR CAPI: 2,315.57 − 480 − 1,064.27 = 771.30 a month, 9,255.60 a year.
  • ca_state_supplement_person.yaml Case 36 (2026-01): two blind spouses take max(833.35, 607.83) = 833.35, or 416.675 each. SSA rounding (floor to 0.001, then up to the cent) gives 416.68. SSI is 1,491 / 2 = 745.50.
  • Case 37 (2025): couple countable income (6,000 + 18,000 − 240) / 12 = 1,980; the couple FBR of 1,450 leaves no SSI.
    • Own countable income 480. Mixed income max(480, 1,980 − (2,315.57 − 1,335.81)) = 1,000.24.
    • Supplement 1,335.81 − 1,000.24 = 335.57 a month, 4,026.84 a year.
  • ca_capi_payment_standard.yaml Case 21 (2026-01): two CAPI LPRs, (1,491 + 607.83) / 2 = 1,049.415.
  • ca_state_supplement.yaml Case 17 (2025): each spouse (2,315.57 − 1,450) / 2 = 432.785, rounded to 432.79. 2 × 432.79 × 12 = 10,386.96, against Compute the California state supplement per person #10601's pre-rounding 865.57 × 12 = 10,386.84.
    • ca_state_supplement_person gives 5,193.48 each.
    • Case 19's table total checks: 235 + 620.81 + (2,315.57 − 980 − 855.81 = 479.76) = 1,335.57.
  • All match the YAML and pass.

Case 33.

  • Path: the parents' two-person explicit marital unit pairs them. The simulation has two marital units, so the default-group "claims" exclusion doesn't apply. ssi_claim_is_joint and ssi_couple_computation_applies are true, giving SSI 1,491 / 2 = 745.50 each.
  • The supplement: ca_capi_couple_computation is true. ca_state_supplement_rates counts two aged-or-disabled members, so the couple rate is 607.83. ssi_state_supplement's standard is _ssi_couple_total(607.83 / 2) / 2 = 303.915, and federal_supplement_payment rounds it up to 303.92.
  • Main's removed note gave "The couple level is 607.83, or 303.92 each" and said the guards must go when this PR lands. The new [0, 745.50, 745.50] and [0, 303.92, 303.92] are that level.

The partner file. partners/analytics_coverage/edge_cases/state/ca/state_supplement.yaml passes 4 of 4, and git diff 0315452583 8fd9ce678e has no file under partners/.

Mutants. I ran these on a scratch worktree at head, one mutant at a time. Each file ran in its own process: ca_state_supplement.yaml, ca_state_supplement_person.yaml, ca_capi_payment_standard.yaml, ca_capi_person.yaml and the California partner file. The unmutated run passed all five. Afterwards git checkout -- . left git status --short empty. All eight mutants die:

Mutant Killed by
blind_count from person.marital_unit.sum(blind & head_or_spouse) ca_state_supplement_person.yaml Case 36 (303.92 instead of 416.68)
couple * head_or_spouse on ca_state_supplement_rates' return ca_state_supplement_person.yaml Cases 33, 36, 37; ca_capi_payment_standard.yaml Case 21; ca_capi_person.yaml Case 23; ca_state_supplement.yaml Case 19
& head_or_spouse on ca_capi_couple_computation's return ca_state_supplement_person.yaml Cases 33, 36; ca_capi_payment_standard.yaml Case 21; ca_capi_person.yaml Case 23; ca_state_supplement.yaml Case 19
Main's ca_capi_spouse_ssi_ssp ca_capi_person.yaml Case 23 (13,149.72 instead of 9,255.60)
couple_income = person.marital_unit.sum(ssi_countable_income * head_or_spouse) ca_state_supplement.yaml Case 18; ca_state_supplement_person.yaml Case 37
spouse_capi_eligible from person.marital_unit.sum(capi_eligible & head_or_spouse) > 0 ca_state_supplement.yaml Cases 17, 19; ca_state_supplement_person.yaml Case 37
Mine: ca_capi_spouse_ssi_ssp counts only the spouse's ssi, dropping ca_state_supplement_person ca_capi_person.yaml Cases 6, 11, 12, 21, 22, 23 (Case 23: 13,302.84 instead of 9,255.60); ca_state_supplement.yaml Cases 14, 15, 19
Mine: aged_or_disabled_count in a joint computation counts only the person (2 * aged_or_disabled) ca_state_supplement_person.yaml Case 17 (aged and blind couple without cooking facilities)

Tests. I ran 52 files one per process, at most 4 at a time: every test file #10601 changed that still exists, every test file in the PR's diff from its merge base, and test_source_conflicts.py. 1,188 passed, 0 failed. Among them:

  • ca_state_supplement_person.yaml 37, ca_state_supplement.yaml 19, ca_capi_person.yaml 23, ca_capi_payment_standard.yaml 21;
  • ca_capi.yaml 10, ca_capi_three_adult.yaml 6, integration.yaml 6, ca_state_supplement_eligible_person.yaml 8;
  • ia_ssa.yaml 121, ssi_state_supplement_three_adult.yaml 37;
  • test_ssi_spousal_deeming_invariants.py 80 (with hypothesis installed), test_calculate_chunked_validation.py 80, test_source_conflicts.py 103;
  • the three partner files: California supplement 4, federal CDCC 16, federal EITC 15.

The defect. #10138 reproduces at main 0315452583. A 2026 claimant aged 67 with $12,000 of wages and a spouse aged 45 with $6,000 of self-employment income gets $9,402 of SSI with deeming on, against $6,438 at $5,900 with deeming off. At head both are $6,438.

Readers.

  • ca_state_supplement_person is read by ca_state_supplement (adds), ca_state_supplement_eligible_person, ca_capi_spouse_ssi_ssp, ca_sbd_general_relief_receives_other_cash_assistance and SNAP unearned income.
  • ca_capi_person is read by ca_capi and ca_capi_income_eligible. ca_capi and ca_state_supplement feed SPM benefits, household state benefits, Riverside LIHEAP, San Bernardino and Orange County GR, and SF CAAP.
  • All read per-person or unit amounts and inherit the corrected couple values. I found no reader that this delta makes reachable and worse.
  • The diff moves baseline output for California couples where one spouse has a dependent tax role, or where a default marital unit holds a third adult. The hub's impact pair should cover California SSP and CAPI at this head.

Body statements checked against code. These hold:

  • the five-conflict list;
  • the four 0eed55f172 reads;
  • "No marital_unit or is_tax_unit_head_or_spouse read remains under ca/cdss";
  • the dropped pro rata share;
  • table rows 2 and 3 (10,386.96; 7,449.72; 1,335.57);
  • the Case 33 paragraph;
  • the new-case list;
  • the six 0eed55f172 mutant rows I reran.

The axiom: and impact: lines are present; I left them for the hub.

CI. The Pull request run at 8fd9ce678e (https://github.com/PolicyEngine/policyengine-us/actions/runs/41978111346) concluded success: 36 of 36 jobs succeeded, including Baseline states-shard-1 (which failed on Case 33 at 0eed55f172), Microsimulation, the Contrib shards and Household API Partners.

What I couldn't check

  • Law. This session's network refused ecfr.gov, govinfo.gov, ssa.gov and leginfo.legislature.ca.gov. I quote no law, and the hand values rest on the repository's parameter values. Neither the 2025 and 2026 FBRs nor the WIC 12200 shares were confirmed against a source this round.
  • The married minor's $727 of SSI. It comes from round 10's SSI reading, which this delta doesn't touch and I didn't reopen.
  • Population impact. I ran no microsimulation.

Generated by Claude Code

MaxGhenis and others added 3 commits November 5, 2026 20:04
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eming

This branch identifies SSI couples by marriage, so SSI deems none of the
head's income to the 72-year-old dependent. The dependent's own countable
income of $1,490 passes the Medicare Savings Program test, the program pays
the $2,220 Part B premium, and the medical subtotal holds nothing. The
deduction moves from 3,140 to 3,000. The case still catches a second count
of the long-term care premiums, which would give 3,920.

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

GitHub reported a conflict with main (ba9bfde). Only
nj_medical_expense_deduction.yaml conflicted, on the 72-year-old
dependent's case. Main (#10667) moves the dependent's Schedule C income
and self-employed health insurance deduction to the dependent's own
return, so NJ AGI is 100,000. This branch stops deeming the head's
income to the dependent, so the state pays the Part B premium and the
subtotal is 0. Together the deduction is max(0, 0 - 0.02 x 100,000) = 0.

The other files both sides changed merged cleanly and keep every case:
co_oap_deeming_exempt.py, co_oap_deemed_income.yaml,
medicaid_optional_senior_or_disabled_countable_income.py and its YAML.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main's Colorado AND-CS rules (#10161) found a client's spouse through
the marital unit. In a situation that omits marital units, that unit
holds every member, so a married client got the full $20 disregard
and a third adult's income was deemed as a spouse's.

The $20 disregard split (3.549(A)(2)(c)(1)) now reads
state_is_married, and spouse deeming (3.549(C)-(D)) reads
state_couple_total, as Old Age Pension does. Both keep a declared
spouse who lives apart and count only SSI's identified spouse in a
default unit. Case 42 covers a couple beside a claimed third adult.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge gave AND-CS's new spouse readers the same couple test as Old
Age Pension, so the fragment now names both programs.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review
7e5c7a2

I picked this up at 8eca1fcf85, the head the item named. No one had pushed since then (last commit 2026-11-06), so I built on it directly. Three commits on top:

  • 18e31f3651 merges main at ba9bfdea75 with a merge commit; I didn't rebase.
  • c45fe85e79 gives Colorado's new AND-CS readers this branch's couple test and adds Case 42.
  • 7e5c7a2cde names AND-CS beside OAP in the changelog fragment's retained-spouse sentence.

Main now stands 79 commits past the merge base 4279ce2e63, not 63. The brief's count matches 6eac550480. The Medicaid LTSS screen (#9184) and eight later commits landed after it; I checked them under the same rule.

1. Merge main

One conflict: nj_medical_expense_deduction.yaml, the case "A dependent's long-term care deduction is not counted again in New Jersey". Both sides changed its expected values:

  • Main (Leave tax unit dependents' income out of Arkansas, Mississippi, New Jersey and Iowa income #10667) puts the dependent's 4,000 of Schedule C income and their 3,000 deduction on the dependent's own return, so nj_agi is 100,000 and the deduction 220.
  • This branch stops deeming the head's income to the dependent, so the state pays the dependent's Part B premium and itemized_medical_expenses is 0.
  • The resolution keeps both rules (nj_medical_expense_deduction.yaml:261-273): itemized_medical_expenses 0, nj_agi 100,000, nj_medical_expense_deduction 0. The derivation is under item 3. All 15 cases remain.

Auto-merged, both sides changed. I read each result against both parents:

  • co_oap_deeming_exempt.py: this branch's co_oap_couple_total spouse test, with main's co_adult_financial_medicaid_enrolled (Apply Colorado's AND-CS payment floor and 3.549 income rules to every client #10161 renamed co_oap_medicaid_enrolled).
  • medicaid_optional_senior_or_disabled_countable_income.py: main's Part B lines (medicare_part_b_enrolled, medicaid_eligibility_health_insurance_premiums, medicare_part_b_premium_before_buy_in, Charge the Medicare Part B premium only to people who pay it #10634), with this branch's _ssi_couple_budget_total.
  • co_oap_deemed_income.yaml: 21 cases (20 at the merge base, plus main's one; this branch's two edited outputs stand).
  • medicaid_optional_senior_or_disabled_countable_income.yaml: 49 cases (41, plus this branch's 7 and main's 1). No case name repeats in either file.

Nothing else references a variable main deleted, and the merged system builds (7,583 variables).

2. New readers

git log --oneline 4279ce2e63..origin/main -- policyengine_us/variables/gov/ssa policyengine_us/variables/gov/states policyengine_us/variables/gov/hhs/medicaid lists 36 commits. Main changed nothing under gov/ssa. I also searched main's whole diff under variables/ for new marital_unit, head_or_spouse, is_tax_unit_spouse, is_tax_unit_head and tax_unit_married reads.

Commit Reader What it reads What I did
#10161 co/ssa/state_supplement/_and_cs_client_income.py marital_unit.nb_persons() == 2 for the 3.549(A)(2)(c)(1) $20 split Changed (:24-28) to state_is_married, as co_oap_is_married does for OAP
#10161 co/ssa/state_supplement/co_state_supplement_deemed_income.py marital_unit.sum(...) - own for 3.549(C)-(D) spouse deeming Changed (:50-58) to state_couple_total, as co_oap_couple_total does for OAP
#10161 co_state_supplement_deeming_exempt.py (:66-71) marital unit, tax unit and household sums of clients Left. It only decides who needs a Medicaid enrollment check. It spans every group in which the deemed-income reader can find a spouse or parent, so it stays a superset.
#10183 co_ssa_in_kind_support_and_maintenance.py, read by co_oap_in_kind_support_and_maintenance.py ssi_couple_computation_applies to halve a couple's ISM Left. That variable already uses this branch's spouse test.
#10161 co_adult_financial_medicaid_enrolled.py, co_state_supplement.py, co_state_supplement_own_countable_income.py, co_state_supplement_countable_income.py per person, or through the two helpers above Left. No couple read of their own.
#10634 medicaid_part_b_buy_in_premium_coverage.py, medicare_part_b_premium.py the buy-in reads each person's monthly ssi and receives_ssi; the premium nets that person's MSP or buy-in coverage Left. No couple read.
#10634 medicaid_medically_needy_medical_expenses.py an adds list Left. This branch's is_medically_needy_for_medicaid still combines it through _ssi_couple_total.
#9184 (after the brief's main) Medicaid LTSS: medicaid_ltss_assistance_unit_size, medicaid_ltss_couple_countable_income, medicaid_ltss_countable_resources, medicaid_ltss_community_spouse_countable_resources marital_unit.nb_persons() == 2 and marital_unit.sum Left; its own reason is below.
#10607, #10611, #10613, #10618 CA, CT, ID and WV LIHEAP CT and ID test SSI or SSP receipt across the SPM unit. CA reads no SSI, and WV leaves SSI out of income. Left. No SSI couple.
#10038, #10053, #10195, #10196, #10198, #10200, #10203, #10663, #10666, #10667 claim of right (federal, AR, HI, IN, MA, MT, NJ, OH, WV), dependent-filer rules (federal, AZ, AR, CA), Form 4797 gains, PA forgiveness, MS capital losses is_tax_unit_head_or_spouse, is_tax_unit_head, is_tax_unit_spouse, tax_unit_married, one marital_unit.sum in PA Left. These are tax rules about who is on the return, not SSI couples.

Why LTSS keeps the marital unit. Its couple budgets apply only when medicaid_ltss_assistance_unit_size is 2.

  • Outside Delaware that size is a reported input with default 0. In Delaware it comes from explicit marital-unit inputs: same facility, same-address HCBS and the six-month election.
  • The community spouse comes from medicaid_ltss_has_community_spouse, which the documentation says is "not inferred from a tax, household, or marital unit".
  • A community spouse lives apart from the institutionalized spouse, so SSI's co-residence test would drop them.

Why state_couple_total and not _ssi_couple_total for AND-CS. I read 9 CCR 2503-5 3.549 (PDF pages 93-95) this session.

  • (C) and (D) deem "the AND-CS client's spouse" with no shared-household condition, and (F) provides for an institutionalized spouse.
  • This branch reads OAP's 3.534(B)-(C) the same way.
  • For a declared couple, the new reads give main's result. They differ where this branch's spouse test rules a pair out (a parent link, or a lone pair in which one claims the other), and where a default unit holds a third person, whose income main's sum adds.

3. Cases that move

One case moves: the conflict case in nj_medical_expense_deduction.yaml, "A dependent's long-term care deduction is not counted again in New Jersey" (2025). I updated it in the merge.

Output Main This branch before the merge Merged
itemized_medical_expenses 2,220 0 0
nj_agi 100,000 104,000 100,000
nj_medical_expense_deduction 220 3,000 0

Derivation on the merged tree. nj_medical_expense_deduction is the filers' self_employed_health_insurance_ald plus max(0, itemized_medical_expenses − excluded dependent premiums + pre-tax premiums − 0.02 × nj_agi).

  • The head has no self-employed deduction. Main's rule puts the dependent's 3,000 on the dependent's return, so the first term is 0.
  • The dependent's deduction is all long-term care (Form 7206 line 2), so the excluded medical part is min(…, 3,000 − 3,000) = 0. Pre-tax premiums are 0.
  • itemized_medical_expenses is 0. The long-term care premiums stay out on both sides. medicare_part_b_premium is max(2,220 − max(MSP 2,220, SSI buy-in 2,220), 0) = 0.
  • So the deduction is 0 + max(0, 0 − 0 − 2,000) = 0.

Why the premium is paid. I probed both trees with the case's household:

  • Merged: is_ssi_spousal_deeming_applies is false for the dependent. Countable income is 1,490 (the dependent's own earnings), and SSI is 11,604 − 1,490 = 10,114. msp_part_b_premium_coverage and main's new medicaid_part_b_buy_in_premium_coverage (Charge the Medicare Part B premium only to people who pay it #10634) are each 2,220.
  • Main: the head's income is deemed (countable income 51,490), so there's no SSI and no MSP. The premium stays at 2,220 and the deduction is max(0, 2,220 − 2,000) = 220.

The case's comment now names both payers. The resolution changes no other case.

No other case moves. All 99 files below pass with their existing expected values, among them main's 30 SSI, supplement and Medicaid files and this branch's 63. The 56 partner files that name SSI, supplement, MSP, Medicaid or Part B variables all pass, so none of their cases moves.

4. Mutants

Each mutant restores main's read in its own run. Each run covers co_state_supplement.yaml, co_state_supplement_own_countable_income.yaml and co_state_supplement_deemed_income.yaml, one process per file, and git checkout restores the files afterwards (git status empty).

Mutant Killed by Mutant gives Expected
married = person.marital_unit.nb_persons() == 2 in _and_cs_client_income.py co_state_supplement.yaml Case 42 co_state_supplement_own_countable_income [3,360, 2,160, 0] [3,480, 2,280, 0]
marital_unit.sum(gross_earned) - gross_earned and the unearned twin in co_state_supplement_deemed_income.py co_state_supplement.yaml Case 42 co_state_supplement_deemed_income [14,280, 14,280, 0] [0, 0, 0]

No case on main kills either mutant: every multi-person AND-CS case there declares its marital units. So I added Case 42 (co_state_supplement.yaml:908), computed by hand before the run:

  • A disabled couple (husband $300 a month pension, head; wife $200, spouse) and a claimed third adult with $1,200 a month, in one tax unit, 2026, with no marital units.
  • SSI pairs only the spouses: (1,491 − (300 + 200 − 20)) / 2 = 505.50 each.
  • 3.549(A)(2)(c)(1) splits the $20, so own countable income is 290 and 190. Neither spouse deems to the other, since each receives SSI (3.549(F)).
  • Grants: 994 − 505.50 − 290 = 198.50 and 994 − 505.50 − 190 = 298.50 a month, or 2,382 and 3,582 a year.
  • The married mutant gives the full $20 to each: 280 and 180. The deemed mutant deems the third adult's 1,200 less the remaining $10, or 1,190 a month, to each spouse. The third adult isn't deeming-exempt: no SSI or OAP, and 1,180 of countable income is over the 994 limit.

5. Tests

One file per process on the merged tree at c45fe85e79, at most four processes at a time. 7e5c7a2cde changes only the changelog fragment.

Group Files Passed Failed
Named by the item, or touched by a conflict 6 277 0
SSI, state supplement and Medicaid files the merge brought in (Colorado, Part B, LTSS, CT and ID LIHEAP) 30 465 0
This branch's other test files: every one its diff from the merge base changes 63 1,189 0
Partner files that name SSI, supplement, MSP, Medicaid or Part B variables (none edited) 56 252 0
Code-health guards: builtin sum, in-place writes, group variables in person adds 3 15 0
Total 158 2,198 0

The named files: test_source_conflicts.py 103, test_ssi_spousal_deeming_invariants.py 80, nj_medical_expense_deduction.yaml 15, medicaid_optional_senior_or_disabled_countable_income.yaml 49, co_oap_deemed_income.yaml 21 and co_oap_deeming_exempt.yaml 9. co_state_supplement.yaml passes 42, Case 42 included.

Per-file counts

Named or conflict-touched: code_health/test_source_conflicts.py 103, core/test_ssi_spousal_deeming_invariants.py 80, gov/states/nj/tax/income/deductions/nj_medical_expense_deduction.yaml 15, gov/hhs/medicaid/income/medicaid_optional_senior_or_disabled_countable_income.yaml 49, gov/states/co/ssa/oap/co_oap_deemed_income.yaml 21, gov/states/co/ssa/oap/co_oap_deeming_exempt.yaml 9

Brought in by the merge: core/test_co_and_cs_invariants.py 4, core/test_co_ism_invariants.py 2, core/test_co_oap_invariants.py 2, core/test_medicaid_home_equity_limit_indexing.py 22, core/test_medicaid_ltss_financial_invariants.py 60, core/test_medicaid_spousal_impoverishment_indexing.py 10, core/test_ssi_state_supplement_medicaid_dependency.py 28, gov/hhs/medicaid/eligibility/categories/medically_needy/medicaid_medically_needy_medical_expenses.yaml 3, gov/hhs/medicaid/eligibility/long_term_care/financial/is_medicaid_ltss_financial_threshold_eligible.yaml 12, gov/hhs/medicaid/eligibility/long_term_care/financial/is_medicaid_ltss_income_eligible.yaml 45, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_assistance_unit_size.yaml 9, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_csra_resource_eligible.yaml 34, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_financial_pathway.yaml 21, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_home_equity_eligible.yaml 16, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_mmmna.yaml 9, gov/hhs/medicaid/eligibility/long_term_care/financial/medicaid_ltss_special_income_limit.yaml 2, gov/hhs/medicare/eligibility/medicaid_part_b_buy_in_premium_coverage.yaml 10, gov/hhs/medicare/eligibility/medicare_part_b_enrolled.yaml 4, gov/hhs/medicare/eligibility/medicare_part_b_premium.yaml 19, gov/hhs/medicare/test_part_b_msp_offset.py 15, gov/states/co/ssa/co_ssa_in_kind_support_and_maintenance.yaml 19, gov/states/co/ssa/oap/co_oap_in_kind_support_and_maintenance.yaml 5, gov/states/co/ssa/oap/co_oap_own_countable_income.yaml 15, gov/states/co/ssa/state_supplement/co_state_supplement.yaml 42, gov/states/co/ssa/state_supplement/co_state_supplement_countable_income.yaml 4, gov/states/co/ssa/state_supplement/co_state_supplement_deemed_income.yaml 12, gov/states/co/ssa/state_supplement/co_state_supplement_deeming_exempt.yaml 12, gov/states/co/ssa/state_supplement/co_state_supplement_own_countable_income.yaml 8, gov/states/ct/dss/ceap/eligibility/ct_ceap_categorically_eligible.yaml 11, gov/states/id/dhw/liheap/eligibility/id_liheap_income_eligible.yaml 10

This branch's test files: core/test_calculate_chunked_validation.py 80, gov/hhs/medicaid/eligibility/categories/is_209b_ssi_recipient_for_medicaid.yaml 20, gov/hhs/medicaid/eligibility/categories/medically_needy/is_medically_needy_for_medicaid.yaml 40, gov/hhs/medicaid/income/deemed/from_ineligible_spouse/medicaid_optional_senior_or_disabled_income_deemed_from_ineligible_spouse.yaml 7, gov/ssa/ssi/eligibility/income/deemed/ssi_income_deemed_from_ineligible_spouse.yaml 7, gov/ssa/ssi/eligibility/resources/deemed/is_ssi_spousal_resource_deeming_applies.yaml 14, gov/ssa/ssi/eligibility/ssi_marital_both_eligible.yaml 6, gov/ssa/ssi/is_ssi_qualified_noncitizen.yaml 1, gov/ssa/ssi/ssi_claim_is_joint.yaml 9, gov/ssa/ssi/ssi_couple_computation_applies.yaml 6, gov/ssa/ssi/ssi_if_takes_up.yaml 10, gov/ssa/ssi/uncapped_ssi_without_spousal_deeming.yaml 5, gov/states/ak/dpa/ssp/ak_ssp.yaml 9, gov/states/al/dhr/ssp/integration.yaml 20, gov/states/ca/cdss/capi/ca_capi_payment_standard.yaml 24, gov/states/ca/cdss/capi/ca_capi_person.yaml 26, gov/states/ca/cdss/capi/eligibility/ca_capi_income_eligible.yaml 7, gov/states/ca/cdss/state_supplement/ca_state_supplement.yaml 19, gov/states/ca/cdss/state_supplement/ca_state_supplement_person.yaml 37, gov/states/ca/chhs/wdp/eligibility/ca_wdp_income_eligible.yaml 3, gov/states/co/ssa/oap/co_oap.yaml 37, gov/states/ct/dss/ssp/eligibility/ct_ssp_resource_eligible.yaml 11, gov/states/dc/dhcf/ossp/dc_ossp_payment_amount.yaml 1, gov/states/de/dhss/ssp/de_ssp.yaml 9, gov/states/fl/dcf/oss/fl_oss_provider_rate.yaml 6, gov/states/hi/dhs/oss/hi_oss_payment_amount.yaml 1, gov/states/ia/hhs/ssa/ia_ssa.yaml 121, gov/states/in/fssa/ssp/in_ssp_sapn.yaml 5, gov/states/ky/dcbs/ssp/ky_ssp.yaml 13, gov/states/ky/dcbs/ssp/ky_ssp_claim_type.yaml 7, gov/states/ma/dta/ssp/ma_state_supplement.yaml 3, gov/states/me/dhhs/ssp/me_ssp.yaml 21, gov/states/mi/mdhhs/ssp/mi_ssp.yaml 5, gov/states/mn/dhs/msa/eligibility/mn_msa_resource_eligible.yaml 12, gov/states/mn/dhs/msa/mn_msa_person.yaml 17, gov/states/mn/dhs/msa/payment/mn_msa_guardian_fee.yaml 10, gov/states/mn/dhs/msa/payment/mn_msa_payment_category.yaml 11, gov/states/mo/dss/ssp/mo_ssp.yaml 35, gov/states/mo/dss/ssp/mo_ssp_resource_eligible.yaml 16, gov/states/ms/dom/hmw/eligibility/ms_hmw_eligible.yaml 4, gov/states/ms/dom/hmw/eligibility/ms_hmw_income_eligible.yaml 8, gov/states/mt/dphhs/liheap/eligibility/mt_liheap_categorical_eligibility.yaml 13, gov/states/mt/dphhs/liheap/mt_liheap.yaml 16, gov/states/mt/dphhs/liheap/payment/mt_liheap_benefit_income.yaml 8, gov/states/mt/dphhs/ssp/mt_ssp.yaml 17, gov/states/mt/tax/income/credits/elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_pre_one_claimant.yaml 3, gov/states/ne/dhhs/aabd/ne_aabd_standard_of_need.yaml 1, gov/states/nj/njdhs/ssp/nj_ssp.yaml 18, gov/states/nj/njdhs/ssp/nj_ssp_living_arrangement.yaml 12, gov/states/nv/dwss/oss/nv_oss.yaml 59, gov/states/ny/otda/ssp/ny_ssp.yaml 49, gov/states/oh/dbh/rss/oh_rss.yaml 23, gov/states/ok/dhs/ssp/ok_ssp.yaml 22, gov/states/or/odhs/ssp/or_ssp.yaml 39, gov/states/pa/dhs/ssp/pa_ssp.yaml 21, gov/states/ri/dhs/ssp/ri_ssp.yaml 23, gov/states/sd/dss/ssp/sd_ssp.yaml 41, gov/states/ut/dws/ssp/ut_dws_ssp.yaml 11, gov/states/va/dars/ag/va_ag.yaml 33, gov/states/vt/dcf/aabd/vt_aabd.yaml 18, gov/states/vt/dcf/aabd/vt_aabd_ep.yaml 27, gov/states/wa/dshs/ssp/wa_ssp.yaml 12, gov/states/wi/dhs/ssp/wi_ssp.yaml 20

Partner files: partners/amplifi/2025.yaml 1, partners/amplifi/2026.yaml 3, partners/analytics_coverage/edge_cases/federal/cash/ssi/federal.yaml 14, partners/analytics_coverage/edge_cases/federal/cash/tanf/federal.yaml 14, partners/analytics_coverage/edge_cases/federal/composition/cash_health/federal.yaml 8, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/ca.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/co.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/federal.yaml 11, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/il.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/ma.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/nc.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/aca_ptc/wa.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/chip/ca.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/chip/co.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/chip/federal.yaml 8, partners/analytics_coverage/edge_cases/federal/healthcare/chip/il.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/chip/ks.yaml 3, partners/analytics_coverage/edge_cases/federal/healthcare/chip/ma.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/chip/nc.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/chip/wa.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/ca.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/co.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/federal.yaml 9, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/il.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/ks.yaml 2, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/ma.yaml 3, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/nc.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/medicaid/wa.yaml 1, partners/analytics_coverage/edge_cases/federal/healthcare/msp/federal.yaml 11, partners/analytics_coverage/edge_cases/federal/healthcare/msp/ma.yaml 1, partners/analytics_coverage/edge_cases/state/ca/cdcc.yaml 3, partners/analytics_coverage/edge_cases/state/ca/eitc.yaml 6, partners/analytics_coverage/edge_cases/state/ca/foster_youth_tax_credit.yaml 3, partners/analytics_coverage/edge_cases/state/ca/renter_credit.yaml 5, partners/analytics_coverage/edge_cases/state/ca/state_supplement.yaml 4, partners/analytics_coverage/edge_cases/state/ca/tax_credits_composition.yaml 2, partners/analytics_coverage/edge_cases/state/ca/yctc.yaml 3, partners/analytics_coverage/edge_cases/state/co/care_worker_credit.yaml 4, partners/analytics_coverage/edge_cases/state/co/eitc_ctc.yaml 2, partners/analytics_coverage/edge_cases/state/co/family_affordability_credit.yaml 4, partners/analytics_coverage/edge_cases/state/co/oap.yaml 3, partners/analytics_coverage/edge_cases/state/co/state_supplement.yaml 3, partners/analytics_coverage/edge_cases/state/co/tax_credits_composition.yaml 2, partners/analytics_coverage/edge_cases/state/ma/eaedc.yaml 3, partners/analytics_coverage/edge_cases/state/ma/state_supplement.yaml 4, partners/analytics_coverage/edge_cases/state/wa/tax_credits_composition.yaml 2, partners/analytics_coverage/edge_cases/state/wa/working_families_tax_credit.yaml 7, partners/analytics_coverage/signatures/ca.yaml 29, partners/analytics_coverage/signatures/co.yaml 6, partners/analytics_coverage/signatures/federal.yaml 18, partners/analytics_coverage/signatures/il.yaml 7, partners/analytics_coverage/signatures/ma.yaml 5, partners/analytics_coverage/signatures/nc.yaml 5, partners/analytics_coverage/signatures/tx.yaml 7, partners/analytics_coverage/signatures/wa.yaml 4, partners/my_friend_ben/2025.yaml 1

CI

The Pull request run at this head is https://github.com/PolicyEngine/policyengine-us/actions/runs/42650265254. It was still running when I posted this. The run at the previous head, 8eca1fcf85 (https://github.com/PolicyEngine/policyengine-us/actions/runs/42274602235), passed all 36 jobs, including Household API Partners.

Judgment calls

  • AND-CS spouse helper. I used state_couple_total and state_is_married rather than the co-resident _ssi_couple_total, for the reasons under item 2. It's the reading this branch already takes for OAP.
  • The AND-CS Medicaid gate stays. co_state_supplement_deeming_exempt's near_client doesn't identify a spouse; it skips a Medicaid enrollment check for people who can't be a client's spouse or parent.
  • LTSS stays on the marital unit. It landed after the brief's main, so the hub hasn't seen it. My reasons are under item 2.
  • Case 42's home. I put it in co_state_supplement.yaml beside main's couple cases. It asserts the two intermediate variables and the grant in one household, as this branch's OAP counterpart does in co_oap.yaml. That's one simulation rather than one per variable file.
  • Changelog. The fragment's sentence now reads "Colorado OAP and AND-CS retain declared spouses living apart"; nothing else changed.
  • NJ comment. The merged case's comment names both payers of the Part B premium, MSP and Charge the Medicare Part B premium only to people who pay it #10634's SSI buy-in, since the merged tree computes 2,220 for each.
  • Wider test set. The item asked for conflict-touched files, the SSI and supplement files the merge brought in, test_ssi_spousal_deeming_invariants.py and test_source_conflicts.py. I also ran every test file this branch changes and 56 partner files, because I hand off before CI finishes.
    • I picked the partner files by searching for SSI, SSP, supplement, OAP, CAPI, MSP, Medicaid, Part B and NJ medical-expense variables.
    • I didn't run the other 89 partner files.
  • Process. I installed hypothesis with uv sync --extra dev, as CI does. I restarted the partner run once with more processes. The file I stopped mid-run, partners/.../medicaid/il.yaml, reran and passed.
  • Body. When I saved the paragraph, the server replaced the body's closing "Generated with" line with its standard attribution footer. The impact: and axiom: lines are byte-for-byte unchanged. The older "- CI:" bullet predates the passing run at 8eca1fcf85; I left it for the hub.

Proposed follow-up

Medicaid LTSS: rule out parent and child pairs as community spouses.
medicaid_ltss_community_spouse_countable_resources takes the other member of any two-person marital unit. With the community-spouse flag set, a default unit of a parent and a claimed child would count the child's resources; this branch's pair test rules that pair out.


Generated by Claude Code

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.

SSI: deemed benefit is capped at the individual rate instead of the no-deeming amount, and couples are identified by tax roles

3 participants