Skip to content

Add South Dakota state supplementation for 2026 - #10169

Open
hua7450 wants to merge 7 commits into
mainfrom
sd-ssp-2026
Open

hua7450 wants to merge 7 commits into
mainfrom
sd-ssp-2026

Conversation

@hua7450

@hua7450 hua7450 commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

South Dakota's two state-administered optional supplements were missing from PolicyEngine-US. Add sd_ssp for 2026 (SDCL 28-5A-1).

Independent living (SB 32; ARSD 67:12:14; POMS SI DEN01415.011 B.5.b). $15 a month for an individual, or $8 and $7 for an eligible couple.

  • The person must:
    • receive SSI;
    • be 18 or older and not in school;
    • live in their own household or another's (A or B);
    • have no other income apart from SSI and in-kind support. A couple's other income is pooled.
  • Residents of assisted living, adult foster care or a medical facility are excluded (67:12:14:02(4)). sd_ssp_care_setting records assisted living and adult foster care, including waiver and private-pay residents.

Assisted living and adult foster care (SB 201; ARSD 67:46). The approved monthly charge less the resident's gross income, including SSI, after state-approved deductions (67:46:06:10).

  • Assisted living is capped at the LTSS fee schedule's daily rate: $77.16 in 2026, uprated by the SSI cost-of-living adjustment until the next schedule. Adult foster care is uncapped.
  • Partial months: income isn't applied unless the resident died or transferred (06:09). A resident whose net income exceeds the monthly rate is still ineligible (04:15).
  • Resources: $2,000. When both spouses enter care in the same month, the limit is $3,000 on pooled resources (05:30), and the couple's income is shared equally that month (04:10(6)).
  • SSI: a resident whose income would qualify for SSI must take it up (04:13(2), 01:14).
  • Inputs: sd_ssp_care_certified, sd_ssp_approved_care_cost, sd_ssp_care_income_deductions, sd_ssp_care_partial_month_days, sd_ssp_care_died_or_transferred and sd_ssp_care_joint_entry. All default to 0 or false, so entering one for one person leaves other household members unchanged.

The payment is added to the household and SPM benefit totals and to SNAP and HUD unearned income.

References

Additional section and page citations are included alongside the parameters and formulas.

Coverage limits

The registry marks South Dakota as partial, verified for 2026.

Not modeled:

  • the month only one spouse of a couple living together enters care (04:10(7), 05:42(7));
  • out-of-state placements;
  • historical mandatory supplements and claim administration.

Earlier years use parameter backfilling and are unverified historical estimates; no historical coverage is claimed.

Related: #10226 tracks the reform copies of spm_unit_benefits (such as the Texas rebate's), which this PR leaves unchanged.

Test plan

  • 49 South Dakota SSP YAML cases (36 in sd_ssp.yaml, 13 in integration.yaml). They cover:
    • the independent payment and its care, facility, age, school and income exclusions;
    • the care payment's cap, deductions, partial months and the 04:15 test;
    • resources, joint-entry sharing and pooling, and SSI pursuit;
    • two residents with an input on one only;
    • SNAP, HUD and household benefit wiring.
  • After merging main: all 265 South Dakota cases (SSP and tax) pass locally, and the aggregate list integrity, program registry and uprating placement tests pass (678 passed, 10 skipped).
  • ruff format and git diff --check.
  • Changelog fragment in changelog.d/.

axiom: ARSD 67:12:14:02; ARSD 67:46:04:10, 67:46:06:10; POMS SI DEN01415.011 B TheAxiomFoundation/rulespec-us#1784 queued

hua7450 and others added 4 commits October 12, 2026 14:31
- Build per-source income arrays before summing (code-health builtin-sum rule)
- Gate the independent supplement on (ssi > 0) | receives_ssi
- Pay the $8/$7 couple split only when both spouses qualify; a lone qualifier gets $15
- Default the partial-month no-income rule from care days (ARSD 67:46:06:09)
- Drop the SSI take-up gate from the care payment
- Registry ordering, parameter periods, shorter programs.yaml note
- Realistic Case 2 income; add death/transfer, receives_ssi, couple, joint-entry, care-days and earned-income cases

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

- Correct the shared SNAP reference title to 7 CFR 273.9(b)(2) and (c)(1)
- Document the SSI-receipt assumption in the care contribution and the joint-claim couple condition
- Add joint-entry pooled-limit, immigration, incarceration and student-exclusion cases

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review October 13, 2026 08:14
@hua7450
hua7450 requested a review from DTrim99 October 13, 2026 20:37
@DTrim99

DTrim99 commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add South Dakota state supplementation for 2026

PR #10169 (author: hua7450, head ee7bc4be06, merge-base 2e9eea7e6e). 25 files (+959/−4), 4 commits.

Summary

The PR adds South Dakota's two state-administered optional supplements.

  • Independent living (SB 32, ARSD 67:12:14). sd_ssp_independent (Person, MONTH, defined_for = StateCode.SD) pays an adult SSI recipient who lives in federal living arrangement A or B and has no other income:
    • individual_amount is $15, and an eligible couple gets couple_first_amount $8 and couple_second_amount $7. All three are dated 1978-07-01.
    • minimum_age is 18, dated 1978-08-06. Students and care-certified residents are excluded.
  • Care (SB 201, ARSD 67:46). sd_ssp_care (Person, MONTH, SD-gated) pays the approved monthly charge, less the person's gross income (including SSI) after input deductions:
    • Nonwaiver assisted living is capped at assisted_living_daily_rate × covered days ($77.16 from 2026-01-01). Adult foster care is uncapped.
    • Income is not applied in a partial month, unless the person died or transferred (ARSD 67:46:06:09).
    • Resources must be within $2,000, or within $3,000 pooled when both spouses begin care in the same month (resource_limit, joint_entry_resource_limit, both from 2026-01-01).
    • Seven inputs or defaults: sd_ssp_care_certified, sd_ssp_approved_care_cost, sd_ssp_care_days (defaults to the days in the month), sd_ssp_care_income_deductions, sd_ssp_care_income_not_applied (defaults to "partial month"), sd_ssp_care_joint_entry and sd_ssp_is_adult_foster_care.
  • Benefit. sd_ssp adds the two components.
  • Aggregates and registry. household_state_benefits (all four year blocks), spm_unit_benefits, SNAP unearned income, HUD unearned income, and a programs.yaml entry (partial, verified_years: '2026').
  • Tests: 39 YAML cases, 31 in sd_ssp.yaml and 8 in integration.yaml.

Most of the 2026 policy content is right:

  • Independent supplement. The $15 / $8 / $7 amounts and the eligibility rules match POMS SI DEN01415.011 B and ARSD 67:12:14:01–:03. The rules are: receiving SSI, age 18 or over and not in school, an independent or household-of-another arrangement, and no "other income" apart from SSI and in-kind support.
  • Care rate. The $77.16 daily cap matches the January and July 2026 LTSS fee schedules for service 08054.
  • Care budgeting. Gross income with no SSI exclusions, no personal needs allowance outside nursing facilities, partial-month treatment, the resource limits and the payment basis all match ARSD 67:46.
  • Administration. South Dakota administers both programs itself. It is not on SSA's January 2026 list of federally administered supplements.
  • Tests. All 39 cases pass at the head, and 38 of the 39 check by hand against the rules.

Two critical items:

  1. The one failing CI check is caused by this PR, not by infrastructure. sd_ssp has no StateCode gate, so the aggregate-list guard fails. Re-running the job won't fix it; the fix is one line.
  2. Joint-entry couples' income isn't shared equally in the entry month, as ARSD 67:46:04:10(6) requires through 67:46:06:03. Integration Case 7 pins the wrong per-person split, and a probe shows a $300 couple overpayment.

Two should-fix items, the same two as in the sibling reviews (#10174, #10167, #10172):

  • The axiom: needed line isn't valid for a staff PR.
  • All seven parameter descriptions are off the house template.

Critical (must fix)

1. The failing CI check is caused by this PR: sd_ssp has no StateCode gate.

  • Where: policyengine_us/variables/gov/states/sd/dss/ssp/sd_ssp.py:4-11. The variable is adds = ["sd_ssp_independent", "sd_ssp_care"] with no defined_for.
  • The failure. Job 115197637107, "Python tests / Full Suite - Rest (Python + variables)":
    • Step "Run remaining Python tests" ended with exit code 2 after 86 minutes. The last group reported "1 failed, 2169 passed, 11 skipped".
    • The failing test is policyengine_us/tests/test_aggregate_list_integrity.py::test_members_are_state_gated[gov/household/household_state_benefits.yaml].
    • The message reads: "AssertionError: gov/household/household_state_benefits.yaml members without a StateCode gate in their defined_for chain (dc_ctc-style leak risk): ['sd_ssp']".
  • Why. The guard walks each list member's defined_for chain looking for a StateCode, and sd_ssp has none.
  • Reproduced at the head with policyengine-core 3.33.3: the guard file gives 1 failed, 21 passed. With defined_for = StateCode.SD added to a scratch copy of sd_ssp.py, it gives 22 passed.
  • Not infrastructure:
  • Fix: add defined_for = StateCode.SD to sd_ssp.py, after unit = USD. Behavior doesn't change, because both components are already SD-gated.

2. Joint-entry couples' income isn't shared equally in the entry month.

  • Where:
    • sd_ssp_care.py:15-18: sd_ssp_care_joint_entry pools only resources.
    • sd_ssp_care.py:39-47: the income applied is always the person's own.
    • tests/.../sd/dss/ssp/integration.yaml:151-189 (Case 7) pins sd_ssp_care: [1_000, 1_400] at line 187.
  • The law:
    • ARSD 67:46:06:03: "For purposes of determining the amount of long-term care assistance, income is considered according to subdivisions 67:46:04:08 to 67:46:04:10, inclusive."
    • ARSD 67:46:04:10(6): "For a married couple, if both spouses enter a long-term care facility during the same calendar month, the department considers the couple's combined income available to each other and shared equally in the calendar month of entry regardless of room occupancy. After the calendar month of entry, the department treats the couple as separate individuals and considers only that income which is actually available to the individual".
    • This covers assisted living and adult foster care.
      • ARSD 67:46:02:01(1) defines "Long-term care" as "continuing 24-hour service in a medical, nursing, or intermediate care facility, assisted living facility, or adult foster home".
      • 67:46:03:03 lists both as qualifying facilities.
      • The PR itself applies the same-month trigger to these settings for resources (67:46:05:30).
  • When it matters. It matters in an entry month in which income is applied: a full month (both spouses entered on the 1st), or the death/transfer override. Otherwise 67:46:06:09 sets income aside anyway. Case 7 is exactly the full-month case: January, 31 default days, joint entry.
  • Case 7.
    • Under the rule, combined income is 1,200 + 800 = 2,000, shared equally as 1,000 each, so each spouse gets 2,200 − 1,000 = 1,200.
    • The PR pins [1,000, 1,400]. The couple total (2,400) happens to match.
  • Probe at the head (2026-01) where the total differs.
    • The household: Social Security of $30,000 and $6,000 a year ($2,500 and $500 a month), resources of $1,000 each, an approved charge of $2,200 each, joint entry, a full month.
    • The model gives sd_ssp_care [0, 1,700], a total of 1,700.
    • Under the rule, each spouse's income is (2,500 + 500) ÷ 2 = 1,500, so each gets [700, 700], a total of 1,400.
    • So the model overpays the couple by $300. It also wrongly treats spouse A as over the 67:46:04:15 limit ("ineligible ... when the individual's net income exceeds the monthly care rates").
  • Fix:
    • In sd_ssp_care.py, when joint_entry is true, apply person.marital_unit.sum(income) / person.marital_unit.nb_persons() before the deductions.
    • Change Case 7 to [1_200, 1_200] and add the probe above as a case expecting [700, 700].
    • In the sd_ssp_care_joint_entry documentation, say that the flag drives both the 67:46:05:30 resource pooling and the 67:46:04:10(6) income sharing.

Should address

1. axiom: needed isn't valid here, and this PR adds a program.

  • Where: the last line of the PR body, axiom: needed.
  • The rule. main's CLAUDE.md, "Axiom Parity (required for policy changes)":
    • "Put one line in the PR body: axiom: <legal id> encoded-correct | <rulespec PR> encoded | <rulespec issue> queued | n/a: <reason>."
    • needed is reserved for external contributors who can't complete the Axiom work, and the author is PolicyEngine staff.
    • CONTRIBUTING.md#axiom-parity covers "a new program", and none of the n/a grounds applies.
  • Nothing to point to yet.
    • On rulespec-us main, us-sd/ holds only policies/cms/ (CHIP and Medicaid levels) and policies/income_tax/.
    • Issue searches for "South Dakota supplement", "South Dakota SSP", "South Dakota optional", "67:12:14", "67:46", "28-5A", "DEN01415" and "us-sd" find no SSP issue. The only South Dakota pe-parity issue is Presence of a complex reform causes US income tax inaccuracies #1539 (LIEAP).
  • Precedent.
  • Fix: file a dispatch-ready pe-parity issue on the Add AMT parameters for 2020-2023 #1467 / DC CDCC #1708 model, then add a line such as axiom: ARSD 67:12:14:02; ARSD 67:46:06:10; POMS SI DEN01415.011 B TheAxiomFoundation/rulespec-us#NNNN queued. The issue needs:
    • Module paths, for example:
      • us-sd/regulations/arsd/67-12-14/ for :01(2) "other income", :02 eligibility and :03 termination
      • us-sd/regulations/arsd/67-46/ for 04:10(6), 04:15, 05:30, 06:05, 06:09 and 06:10
      • us-sd/policies/ssa/poms/si-den01415-011/sd-optional-supplement-amounts.yaml for $15 / $8 / $7
      • us-sd/policies/dhs/ltss-fee-schedule/2026/state-optional-supplemental-assisted-living.yaml for $77.16 a day
    • Corpus citations, plus the blocker if a source isn't in the corpus yet.
    • Verbatim law: the passages quoted under Verified correct and in Critical 2.
    • Required outputs (Person, monthly):
      • The Money parameters ($15, $8, $7, $77.16 a day, $2,000, $3,000).
      • A Judgment for the independent payment: receives SSI AND age 18 or over AND not in school AND living arrangement A or B AND no other income AND not in assisted living or adult foster care.
      • A Money output for care: min(approved charge, days × rate) − max(0, income − deductions), floored at 0. Income is set to 0 in a partial month unless the person died or transferred, and is shared equally for a joint-entry couple in the entry month.
    • Companion tests from the same sources, not from PolicyEngine output:
      • SSI-only adult at home: $15 (POMS B.5.b).
      • Eligible couple: $8 and $7.
      • SSI recipient with any Social Security: $0 (67:12:14:03(2)).
      • Household of another with in-kind support: $15 (67:12:14:02(1)).
      • Age 17, or in school: $0 (67:12:14:02(2)).
      • Assisted living resident, full January, $3,000 charge, SSI $994: 31 × 77.16 − 994 = 1,397.96.
      • 10 days in January, $1,500 charge: income not applied, so min(1,500, 771.60) = 771.60 (67:46:06:09).
      • Joint-entry couple with resources of $3,200: $0 (67:46:05:30).
      • Joint-entry couple, full month, incomes of $1,200 and $800, $2,200 charges: 1,200 each (67:46:04:10(6)).

2. All seven parameter descriptions are off the house template.

  • Where (line 1 of each file under parameters/gov/states/sd/dss/ssp/):
    • individual_amount.yaml: "South Dakota pays this amount as its individual independent supplement."
    • couple_first_amount.yaml / couple_second_amount.yaml: "... as its first [second] member independent supplement."
    • minimum_age.yaml: "South Dakota provides its independent state supplement to adults at or above this age."
    • assisted_living_daily_rate.yaml: "South Dakota caps its state optional assisted living reimbursement at this daily amount, including room and board."
    • resource_limit.yaml / joint_entry_resource_limit.yaml: "South Dakota applies this [joint entry] individual resource limit to long-term care assistance."
  • The template: "one sentence, spell the program out fully (no acronyms), [State] [verb] ... under the [Full Program Name] program."
    • None of the seven has the "under the ... program" clause.
    • The siblings on main do. For example, ga/dhs/ssp/amount.yaml: "Georgia provides this amount as the monthly supplement under the State Supplementary Payment program." And ks/kdhe/sspp/payment/amount.yaml: "Kansas provides this amount as the monthly cash payment under the State Supplemental Payment Program."
  • Fix. ARSD 67:12:14 names the program "Optional State Supplemental Program".
    • individual_amount.yaml: "South Dakota provides this amount as the monthly payment to an individual in an independent living arrangement under the Optional State Supplemental program."
    • couple_first_amount.yaml / couple_second_amount.yaml: "South Dakota provides this amount as the monthly payment to the first [second] member of an eligible couple under the Optional State Supplemental program."
    • minimum_age.yaml: "South Dakota sets this age as the minimum for the independent living payment under the Optional State Supplemental program."
    • assisted_living_daily_rate.yaml: "South Dakota limits the assisted living payment to this daily rate, including room and board, under the Optional State Supplemental program."
    • resource_limit.yaml: "South Dakota limits an individual's countable resources to this amount for the assisted living and adult foster care payment under the Optional State Supplemental program."
    • joint_entry_resource_limit.yaml: "South Dakota limits a couple's combined resources to this amount, when both spouses begin long-term care in the same month, under the Optional State Supplemental program."

Suggestions

  1. The daily rate before and after 2026 (the historical deferral is documented in the PR body).
    • The leak. assisted_living_daily_rate.yaml:3 has only 2026-01-01: 77.16, so the rate carries back to earlier years.
    • The 2025 rate. The July 2025 fee schedule shows "State Optional Supplemental Assisted Living 2. n/a 08054 $75.05 per day". Its note 2 says "rate is effective 01/01/2025".
    • Probe at the head, 2025-01, an SSI-only certified resident with a $3,000 charge:
      • The model gives 1,424.96 (31 × 77.16 − 967).
      • The law gives 31 × 75.05 − 967 = 1,359.55.
      • The model is $65.41 a month too high. Earlier years leak too; 2015-01 gives 1,658.96.
    • Going forward. Note 2 also says the rate "is adjusted annually in January based on the Social Security Administration (SSA) Cost of Living Adjustment (COLA)".
      • 77.16 ÷ 75.05 = 1.0281, which is the 2026 COLA of 2.8%.
      • With the rate frozen, the 2027 probe gives 1,370.13 (31 × 77.16 − 1,021.83). Uprating by the same COLA would give about 1,437 (31 × 79.32 − 1,021.83).
    • Fix:
      • Add 2025-01-01: 75.05 with the July 2025 schedule as its reference.
      • Consider uprating: gov.ssa.uprating (the FBR's index) rounded to the cent, or a comment explaining why the rate is frozen.
    • The other parameters are fine. The independent amounts' 1978 dates are consistent with SSA's January 2011 table ($15 individual, $15 couple). The resource limits are unchanged since at least 2014.
  2. The Texas rebate reform's copy of spm_unit_benefits now omits sd_ssp.
    • Where: policyengine_us/reforms/states/tx/rebate/tx_rebate.py:45-127 redefines spm_unit_benefits nationwide with a hard-coded list. It already lacks ca_state_supplement, and after this PR it also lacks sd_ssp.
    • Probe at the head: an SD aged SSI-only single in 2027 has spm_unit_benefits of 13,166.26 at baseline and 12,986.26 under the reform, a drop of exactly the $180 sd_ssp.
    • Fix: insert "sd_ssp", # South Dakota benefits after "fl_oss", at line 65, as spm_unit_benefits.py does. Add ca_state_supplement while there, consistent with Add Wyoming state supplementation for 2026 #10174.
  3. The month one spouse enters care isn't modeled.
    • The rules.
      • ARSD 67:46:04:10(7): "For a married couple living together during the calendar month that one spouse enters assisted living or adult foster care, the department considers the couple's combined income available to each other in the calendar month of entry."
      • 67:46:05:42(7) says the same of resources, and 67:46:05:30 gives the $3,000 limit only "if both spouses begin receiving long-term care services in the same month".
    • The gap. The model always uses the applicant's own income and resources.
      • The income side is mostly moot, because an entry month is usually partial (67:46:06:09).
      • The resource side is not. Resource eligibility is set "as of 12:01 a.m. on the first day of the month of application" (67:46:05:43), against $2,000.
    • Fix: list this as a limitation in the programs.yaml notes and the sd_ssp_care_joint_entry documentation, or widen the flag into an entry-type input.
  4. Write down and pin three readings.
    • (a) Pooling a qualifying couple's other income (integration Case 3).
      • The rule reads person by person. ARSD 67:12:14:03(2): "The person is receiving other income in addition to SSI benefits".
      • The PR pools the income, which POMS DEN01415.011 B.5.b supports: "Only those individuals and couples on the SDX who are in LA A or B and in current pay status who have no other income."
      • Fix: add a NOTE at sd_ssp_independent.py:43-46 citing B.5.b, as the code already does for the one-qualifier case.
    • (b) The care payment replaces SSI when SSI isn't taken up.
      • Probe: an aged certified resident with no income and a $2,200 charge gets sd_ssp 2,200 with takes_up_ssi_if_eligible: false, and 1,206 with take-up.
      • The NOTE at sd_ssp_care.py:36-38 documents the assumption, citing 67:46:04:13(2). That is consistent with Virginia (Add Virginia state supplementation for 2026 #10172, Suggestion 3(b)).
      • Fix: gate the care path on SSI take-up for SSI-eligible residents, or pin the non-take-up case.
    • (c) Waiver or private-pay assisted living and adult foster care residents.
      • ARSD 67:12:14:02(4): "Institutionalized individuals or couples living in adult foster care or assisted living arrangements are not eligible."
      • POMS B.7 adds that these residents are "in noninstitutional care facilities and LA A applies".
      • The formula excludes only sd_ssp_care_certified residents. So an HCBS-waiver assisted living resident whose only income is SSI gets $15.
      • Fix: add a care-setting residence input that both formulas use, or document the gap.
  5. Assert the aggregate wiring, and add boundary cases.
    • Aggregates. Integration Case 2 checks SNAP and HUD unearned income but not household_state_benefits or spm_unit_benefits. Probe at the head (2026): household_state_benefits 180 and spm_unit_benefits 12,834.
      • Fix: add household_state_benefits: 180 to Case 2.
    • Boundary cases (optional):
      • care resources of exactly $2,000 (eligible)
      • joint-entry resources of exactly $3,000 (eligible)
      • age exactly 18 for the independent payment
  6. Polish.
    • Blank lines. The PR adds a stray blank line before metadata: in household_state_benefits.yaml (line 412) and in hud/annual_income/sources/unearned.yaml (line 33). Neither relates to South Dakota.
    • Case count. The PR body says "24 source-based YAML cases". The PR has 39 (31 + 8).
    • References:
    • Metadata. resource_limit.yaml and joint_entry_resource_limit.yaml carry period: month, but a resource limit is a stock. The SSI limits carry no period.

Verified correct

  • Statute. SDCL 28-5A-1 authorizes the department "to provide, under such rules as may be promulgated by the department, for supplementary payments to qualified residents of South Dakota who are receiving or would be eligible to receive supplemental security income".
  • Administration and amounts, POMS SI DEN01415.011 (effective 12/12/2001 to present):
    • "The State administers its optional supplementary payments - South Dakota Senate Bill 201, effective 2/75 and South Dakota Senate Bill 32, effective 7/78."
    • B.5.b, SB 32: "Only those individuals and couples on the SDX who are in LA A or B and in current pay status who have no other income. An individual will receive $15 per month and each member of a couple will receive $8 and $7 respectively."
    • SI 01415.058 A (January 2026) lists California, Delaware, DC, Hawaii, Iowa, Michigan, Montana, Nevada, New Jersey, Pennsylvania, Rhode Island and Vermont. South Dakota is absent, so no SSA rounding and no $1 minimum apply.
    • SSA's January 2011 South Dakota page shows "Living independently | 689.00 | 1,026.00 | 15.00 | 15.00". That confirms the $15 couple total and state administration.
  • Independent-payment eligibility.
    • ARSD 67:12:14:02:
      • "(1) The person is receiving SSI and may be receiving in-kind income"
      • "(2) The person is an adult of at least 18 years of age and not in school"
      • "(3) ... a resident of South Dakota"
      • "(4) ... independent living arrangements ... or resides in the household of another who provides in-kind support or maintenance"
    • 67:12:14:01(2): "'Other income,' any earned or unearned income except SSI and in-kind support and maintenance".
    • The code matches:
      • The ssi > 0 | receives_ssi gate.
      • Living arrangements A and B (Cases 1, 3 and 6).
      • minimum_age 18 and the student exclusion (Cases 4, 5 and 30).
      • Every SSI earned and unearned source floored at 0 and summed, with in-kind support outside the lists (Cases 2 and 3).
    • The 1978-08-06 date on minimum_age is the rule's original effective date ("5 SDR 6, effective August 6, 1978").
  • The care payment's basis.
    • ARSD 67:46:06:10: "Payment for program benefits shall be based on the difference between the individual's available income and the monthly rate for the provider of the individual's choice". That is max_(cost − contribution, 0).
    • 67:46:04:02(2): "'Income,' unless specifically excluded in § 67:46:04:03, any money or in-kind payments before deductions". POMS B.7, SB 201: "There are no income or resource exclusions." That supports gross income including SSI, with no $20/$65 exclusions (Cases 14 and 27).
    • 67:46:04:13(2) (amended effective July 1, 2026) uses the $994 SSI standard for assisted living and adult foster care residents.
  • No personal needs allowance in assisted living.
    • 67:46:06:05: "An applicant or recipient residing in a nursing or intermediate care facility is allowed: (1) A deduction of one hundred dollars per month to cover personal needs".
    • SSA's 2011 note, "State excludes $60 of the federal SSI payment to recipients in assisted living facilities or foster care homes", reflects the pre-2015 rule. The 2014 text read "A long-term care assistance applicant or recipient is allowed a deduction of $60". 41 SDR 93 (effective December 3, 2014) narrowed it to nursing and intermediate care facilities.
    • So the PR's documentation, "Nursing-facility-only personal needs allowances do not automatically apply", is right for 2026.
  • Partial months. 67:46:06:09: "Whenever the residence period in a long-term care facility is less than a full month, the recipient's income shall not be applied toward the cost of care unless the recipient died or transferred to another long-term care facility." That matches the sd_ssp_care_income_not_applied default and override (Cases 16 and 22).
  • Resources. 67:46:05:30: "$2,000 for an individual and $3,000 for an individual with a spouse if both spouses begin receiving long-term care services in the same month" (Cases 15 and integration 5–8).
  • The daily cap.
    • The January 2026 LTSS fee schedule shows "State Optional Supplemental Assisted Living 2. n/a 08054 n/a $77.16 per day". Note 2: "This rate includes Room and Board ... also known as the Regular Assisted Living Program, rate is effective 01/01/2026."
    • The July 2026 schedule repeats $77.16 (Cases 11 and 21). Both PDFs are one page, so no anchor is needed.
  • Kept out of SSI income. 20 CFR 416.1124(c)(2) excludes "Assistance based on need which is wholly funded by a State". sd_ssp isn't in the SSI income sources, so there is no circularity. It holds only state amounts, so summing it with ssi doesn't double-count.
  • Counted for SNAP and HUD. 7 CFR 273.9(b)(2)(i) counts "other assistance programs based on need". The SNAP reference consolidation and the HUD addition match the batch.
  • Code:
    • Vectorized throughout (where, min_, max_, clip, marital-unit sums).
    • Periods are correct. YEAR flows (ssi_earned_income, ssi_unearned_income, the SSI sources) use period. YEAR stocks and flags (age, resources, student status, ssi_claim_is_joint, immigration) use period.this_year. period.days gives the month length (Cases 12 and 25).
    • No hard-coded policy values.
    • Every input and formula branch has a case: foster care, days, income not applied, deductions, joint entry, citizenship, incarceration, receives_ssi, both student flags, and the couple position.
    • Callers. sd_ssp is referenced only in the lists this PR edits, including all four household_state_benefits blocks.
    • Microsimulation. sd_ssp_care_certified defaults to false, so microsimulation models only the independent payment. That is consistent with the partial status.
  • Hand checks: all 39 expectations. The 2026 FBR is $994 for an individual and $1,491 for a couple. The cap is 31 × 77.16 = 2,391.96 for January and 28 × 77.16 = 2,160.48 for February.
    • sd_ssp.yaml:
      • Case 1: 15.
      • Case 2: 500 a month; SSI = 994 − 480 = 514; other income is positive, so 0.
      • Case 3: household of another; SSI is positive, so 15.
      • Cases 4–8: age 17, a student, a medical facility, ssi: 0 and Wyoming each give 0.
      • Case 9: 2,200 − 994 = 1,206.
      • Cases 10 and 11: 2,391.96 − 994 = 1,397.96.
      • Case 12: 2,160.48 − 994 = 1,166.48.
      • Case 13: 2,200 − (994 − 100) = 1,306.
      • Case 14: 2,200 − 2,000 = 200.
      • Case 15: resources of 2,001 give 0.
      • Case 16: min(1,500, 771.60) with no income = 771.60.
      • Case 17: foster care, uncapped: 1,500 − 994 = 506.
      • Cases 18–20: no charge, age 40 and no take-up each give 0.
      • Case 21: June 30 × 77.16 − 994 = 1,320.80; July 1,397.96.
      • Case 22: min(1,800, 1,543.20) − 994 = 549.20.
      • Case 23: 2,200 − 1,500 = 700.
      • Case 24: reported receipt gives 15.
      • Case 25: days clip to 28, so income applies: 1,166.48.
      • Case 26: 0 days gives 0.
      • Case 27: SSI = 994 − (300 − 85) ÷ 2 = 886.50; 2,200 − 1,186.50 = 1,013.50.
      • Cases 28 and 29: a noncitizen without qualifying quarters and an incarcerated person each give 0.
      • Case 30: SSI 994 and sd_ssp 0 (part-time college).
      • Case 31: 15.
    • integration.yaml:
      • Case 1: [8, 7].
      • Case 2: 12 × 15 = 180; SNAP and HUD unearned income are 11,928 + 180 = 12,108.
      • Case 3: (500 − 20) ÷ 2 = 240 each; SSI 505.50; pooled, so [0, 0] (Suggestion 4(a)).
      • Case 4: one qualifier, so [0, 15].
      • Case 5: 3,200 > 3,000, so 0.
      • Case 6: [1,000, 1,400]. Correct here, because without joint entry each spouse is a separate individual.
      • Case 7: pins [1,000, 1,400]; the rule gives [1,200, 1,200] (Critical 2).
      • Case 8: 2,100 > 2,000, so [0, 1,400].
  • Changelog: changelog.d/sd-ssp-2026.added.md is top-level, typed added, and accurate.
  • Registry:
    • The programs.yaml entry sits under ssi_state_supplement.state_implementations, after WA, with the batch's fields.
    • partial fits the unmodeled mandatory supplement and out-of-state placements.
    • parameter_prefix resolves.

Batch checks

  1. Axiom line: SHOULD (Should 1). axiom: needed is invalid for a staff PR. rulespec-us has no South Dakota SSP module or issue, so a dispatch-ready pe-parity issue and a queued line are needed.
  2. Parameter descriptions: SHOULD (Should 2). All seven new files lack the "under the ... program" clause.
  3. Backdating: SUGGESTION (Suggestion 1). assisted_living_daily_rate leaks $77.16 into 2025 and earlier; the 2025 rate was $75.05, effective 01/01/2025. The rate is also frozen after 2026 although it is COLA-indexed. The PR claims only 2026 and documents the deferral. The $15 / $8 / $7 amounts are consistent with SSA's 2011 table.
  4. tx_rebate.py copy: SUGGESTION (Suggestion 2). sd_ssp is missing, which costs $180 of SPM benefits in 2027. ca_state_supplement is still missing too.
  5. Aggregate wiring: complete, with no double count. sd_ssp is in spm_unit_benefits, all four household_state_benefits blocks, HUD unearned, SNAP unearned and programs.yaml, and it holds only state amounts. The missing StateCode gate on sd_ssp breaks the list guard (Critical 1).
  6. Income attribution: one contradiction and three readings to pin.
    • The contradiction: joint-entry income isn't shared equally (Critical 2).
    • Not modeled: the month one spouse enters care (Suggestion 3).
    • Still to document and pin: couple pooling, SSI non-take-up and uncertified care residents (Suggestion 4).
    • Correctly pinned: the one-qualifier spouse, an ineligible spouse (person-level; probe gives SSI [994, 0] and sd_ssp [15, 0]) and separate post-entry budgeting.
  7. Sibling conflicts (branch note): trial merges with Add Wyoming state supplementation for 2026 #10174, Add Virginia state supplementation for 2026 #10172 and Add Pennsylvania state supplementation for 2026 #10167 each conflict in three files: HUD unearned, programs.yaml and spm_unit_benefits.py. This PR doesn't use the shared _ssi_state_supplement helper.
  8. CI: one failing check, and this PR causes it (CRITICAL, Critical 1).
    • The Rest job's Python step failed on test_members_are_state_gated for sd_ssp.
    • The job wasn't cancelled; it ran 87 minutes, inside the 90-minute limit.
    • Re-running won't help. The fix is defined_for = StateCode.SD on sd_ssp.
  9. State law first: done.
    • Read: SDCL 28-5A-1; ARSD 67:12:14:01–:11; ARSD 67:46:02:01, 03:02–:03, 04:01–:16, 05:30, 05:42–:43 and 06:01–:12 (plus the 2014 and 2018 texts of 06:05 and 06:08); POMS SI DEN01415.011 and SI 01415.058 A; SSA's January 2011 South Dakota page; and the January 2026, July 2026 and July 2025 LTSS fee schedules.
    • Every 2026 amount, the state administration and the effective dates check out.
    • 38 of 39 expectations check by hand; Case 7 contradicts 67:46:04:10(6).

Axiom line assessment

Invalid (Should 1).

CI status

25 of 26 checks pass. The one failure is caused by this PR (gh pr checks 10169). Three "skipping" entries belong to the superseded run 38356952235.

  • The run. Run 38367932932 on head ee7bc4be06.
  • The failure. Job 115197637107, "Python tests / Full Suite - Rest (Python + variables)", conclusion failure after 1h27m28s.
    • Step 8, "Run remaining Python tests" (08:19:46–09:45:46), failed with exit code 2.
    • The earlier Rest groups all passed: 17, 52, 54, 29, 1,362, 52, 424 and 106.
    • The last group reported "1 failed, 2169 passed, 11 skipped". The failure is test_aggregate_list_integrity.py::test_members_are_state_gated[gov/household/household_state_benefits.yaml], with the message "members without a StateCode gate ... ['sd_ssp']".
    • Step 9, "Run tests/variables YAML tests", then passed (69 passed).
  • Classification. This is caused by the PR. It isn't a runner shutdown (exit 143) or a timeout cancellation like the Add Vermont state supplementation for 2026 #10171 / Add Virginia state supplementation for 2026 #10172 Rest jobs, so a re-run will fail again. The fix is Critical 1.
  • Local run with policyengine-core 3.33.3:
    • the two new YAML files: 39 passed
    • the guard file: 1 failed, 21 passed; with the one-line defined_for fix, 22 passed
  • Partner tests. The PR touches none, and the Household API Partners job passes.

Branch status

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.

The amounts ($15/$8/$7 and the $77.16 daily rate) match POMS DEN01415.011 B, ARSD 67:12:14 and the 2026 LTSS fee schedules, and the income, NF-only PNA and partial-month rules match ARSD 67:46. Two critical (details in the comment above): (1) the CI failure is caused by this PR, not infrastructure, so re-running won't fix it. test_members_are_state_gated fails because sd_ssp has no defined_for; add defined_for = StateCode.SD. (2) Joint-entry couples' income isn't shared equally in the entry month (ARSD 67:46:04:10(6) via 67:46:06:03): integration Case 7 pins [1,000, 1,400] where the rule gives [1,200, 1,200]. Two should items: the axiom line ('needed' isn't valid for staff) and the descriptions.

hua7450 and others added 2 commits October 15, 2026 18:02
…nputs

- Gate sd_ssp on StateCode.SD (aggregate list guard).
- Share a joint-entry couple's income equally in the entry month
  (ARSD 67:46:04:10(6)), and require both spouses to be flagged.
- Replace the care days and income-not-applied formula inputs with
  zero-default partial-month days and died/transferred inputs, and apply
  the 67:46:04:15 income test in partial months.
- Add a care setting input so waiver and private-pay assisted living and
  adult foster care residents get no independent payment; require SSI
  pursuit for residents who would qualify.
- Uprate the assisted living daily rate by the SSI COLA, update
  descriptions and references, and add tests.

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

hua7450 commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, David. The fixes are in 66976a3, followed by a merge of main (0db6e81).

Critical 1 (state gate): sd_ssp now has defined_for = StateCode.SD, and the guard passes locally. You were right that this was the PR's failure, not infrastructure.

Critical 2 (joint-entry income): fixed. When both spouses enter care in the same month, sd_ssp_care shares the couple's gross income equally, before each spouse's own deductions (67:46:04:10(6), applied through 06:03).

  • Integration Case 7 is now [1,200, 1,200].
  • New Case 9 is your household ($2,500 and $500 a month): [700, 700].
  • Case 12 pins the order with a $100 deduction for one spouse: [800, 700].
  • The flag now needs both spouses flagged; Case 13 has one flag and budgets each spouse alone.
  • The documentation says the flag is for the entry month only and drives both the pooling and the sharing.
  • A NOTE records that the month only one spouse enters care (04:10(7), 05:42(7)) isn't modeled (Suggestion 3).

Should 1 (Axiom line): filed TheAxiomFoundation/rulespec-us#1784 (pe-parity). It has:

  • your module paths;
  • the verbatim SDCL, ARSD, POMS and fee schedule text;
  • the required outputs;
  • your nine companion cases mapped to the PE cases.

The source conflicts are in a separate comment on that issue.

Should 2 (descriptions): the four independent-payment files use "under the Optional State Supplemental program" (the ARSD 67:12:14 title). The care files use the names their sources use:

  • the daily rate: "the State Optional Supplemental Assisted Living program" (the fee schedule);
  • the two resource limits: "the long-term care assistance program". Article 67:46 never says "optional", and 05:30's limit is "for long-term care assistance".

Suggestion 1 (daily rate): history stays out of scope, so I didn't add the $75.05. Going forward, the rate now uprates with gov.ssa.uprating to the cent, citing note 2's annual COLA adjustment, so 2027 projects to $79.32 instead of staying frozen.

Suggestion 2 (Texas rebate copy): #10226 tracks one spm_unit_benefits list that reforms append to, so this PR leaves tx_rebate.py unchanged.

Suggestion 4 (readings):

  • (a) Couple pooling: a NOTE in sd_ssp_independent cites B.5.b and 67:12:14:03(2). It's also in the conflict comment.
  • (b) Take-up: gated. A resident whose income would qualify for SSI must take it up (04:13(2) referral; 67:46:01:14 disqualifies one who fails "to take advantage of" it); one over income for SSI needn't apply.
    • sd_ssp.yaml Case 32: no income, no take-up → 0.
    • Case 23 is renamed: over income for SSI → 700.
  • (c) Waiver and private-pay residents: a new sd_ssp_care_setting input (assisted living or adult foster care) replaces sd_ssp_is_adult_foster_care. Anyone with a setting, certified or not, gets no $15 (67:12:14:02(4)).
    • Cases 33 and 34 pin that.
    • Case 36 pins a private-pay medical facility resident.

Suggestion 5 (wiring):

  • Integration Case 2 asserts household_state_benefits: 180.
  • I didn't pin spm_unit_benefits, since its October–December SNAP uses projected FY2027 values.
  • I didn't add the at-limit boundary cases.

Suggestion 6 (polish):

  • I kept the blank lines before metadata:; they come from our formatting pass and are harmless.
  • References:
    • The amount files cite B.5.b, 67:12:14:04 and SSA's 2011 page.
    • sd_ssp.py cites SDCL 28-5A-1.
    • sd_ssp_care.py cites 04:10, 04:13, 04:15, 05:30, 06:03, 06:09, 06:10 and 01:14.
  • I kept period: month on the resource limits, because 67:46:05:43 redetermines resources monthly.
  • The body's case count is updated.

Other fixes I found:

  • Input-reset trap. sd_ssp_care_days (formula: days in the month) and sd_ssp_care_income_not_applied (formula: partial month) were both formulas and inputs. Entering days for one resident set everyone else to 0 days and $0: a second resident got 0 instead of 1,206.
    • They are replaced by zero-default inputs, sd_ssp_care_partial_month_days (0 = full month) and sd_ssp_care_died_or_transferred.
    • Integration Cases 10 and 11 pin two residents with an input on one only.
  • 04:15 in partial months. 06:09 set income aside in a partial month, so a resident with $2,500 a month of income still got the covered-days charge. 06:01 applies the chapter 04 tests first, so sd_ssp_care now applies 04:15 against the monthly rate, which is the covered-days charge scaled to the month. Case 35 pins that household at 0.

On the merged head, these pass locally: all 265 South Dakota cases, and the aggregate list integrity, program registry and uprating placement tests (678 passed, 10 skipped).

@hua7450
hua7450 requested a review from DTrim99 October 15, 2026 22:05
…months

A formula-less monthly input carries forward to later months, so partial
days, a death or transfer, or a joint entry entered for one month also
applied to every month after it. Formulas that return the default keep
these entries to their month.

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

hua7450 commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

One follow-up to my reply, in b416b58. In policyengine-core, a monthly input with no formula carries forward to later months. So sd_ssp_care_partial_month_days, sd_ssp_care_died_or_transferred and the entry-month sd_ssp_care_joint_entry, entered for January, also applied to every later month. Each now has a formula that returns its default. A one-month entry stays in its month, and entering one for one person still leaves other household members unchanged.

  • sd_ssp.yaml Case 37 pins a January partial month followed by a full February.
  • Integration Case 14 pins a joint entry flagged for January: [1,200, 1,200] in January, then separate budgets of [0, 1,360.48] in February.

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.

2 participants