Skip to content

Add South Dakota state supplementation for 2026 - #10169

Merged
hua7450 merged 8 commits into
mainfrom
sd-ssp-2026
Oct 20, 2026
Merged

hua7450 merged 8 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.

@DTrim99

DTrim99 commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Add South Dakota state supplementation for 2026

PR #10169 (author: hua7450). This round reviews head b416b58e8e; round 1 reviewed ee7bc4be06. Three commits landed in between:

  • 66976a3167, "Address review: South Dakota SSP state gate, joint entry income and inputs" (21 files, +414/−83). It does the following:
    • gates sd_ssp on StateCode.SD;
    • shares a joint-entry couple's income equally in the entry month, and requires both spouses to be flagged;
    • replaces the care-days and income-not-applied formula inputs with zero-default sd_ssp_care_partial_month_days and sd_ssp_care_died_or_transferred, and applies 67:46:04:15 in partial months;
    • adds sd_ssp_care_setting and an SSI-pursuit gate, uprates the daily rate, and rewrites the descriptions and references;
    • adds sd_ssp.yaml Cases 32–36 and integration.yaml Cases 9–13.
  • 0db6e819aa, a merge of main at 9560e7f183 with no hand-resolved conflicts. Among other things, it brought in:
  • b416b58e8e, "Stop South Dakota SSP one-month care inputs from carrying into later months" (5 files, +80). It gives the three one-month inputs formulas that return their defaults, and adds sd_ssp.yaml Case 37 and integration.yaml Case 14.

Summary

Ziming fixed both round-1 critical items and both should items:

He also fixed Suggestions 3 and 4(a)–(c), most of 5 and 6, and the forward half of 1. He gave reasons for the rest, which I accept. He went further in three places:

  • The input-reset trap. In round 1, setting the care days for one resident gave everyone else 0 days.
  • Carry-over. One-month inputs leaked into later months.
  • 67:46:04:15 in partial months. A resident with income above the monthly care rate was still paid in a partial month.

The batch lessons don't catch anything here.

  • NH Add New Hampshire state supplementation for 2026 #10156 (state-gated keys). The only new gated enum, sd_ssp_care_setting, is compared in formulas but never used as a parameter key. A two-state probe gives a Massachusetts resident NONE and 0, with no error.
  • The wage count (NH r3, OK Add Oklahoma state supplementation for 2026 #10165, OR Add Oregon state supplementation for 2026 #10166 r2).
    • Both modeled payments are state-administered (POMS DEN01415.011 B.1). The one part SSA administers is the mandatory supplement (A), which the PR doesn't model.
    • The care payment reads ssi_earned_income, which nets out pre-tax premiums. 67:46:04:02(2) counts income "before deductions".
    • The payment still comes out right. 67:46:06:06 then deducts health premiums paid from income, so a probe gives the same 1,038.50 either way.
    • The one trap is double entry. A user who also enters the premium as a care deduction, as that input's documentation invites, gets 1,088.50 (Suggestion 1).

Three optional suggestions: document the premium overlap, bring #1784 in line with the head, and update the PR body's case count.

No critical or should items remain. CI is 27/27 green on the head. The branch is 56 commits behind main and conflicts with it in three shared lists, where sibling SSP lines landed at the same spots (branch note).

Prior findings status

Finding Status Evidence
Critical 1: sd_ssp had no StateCode gate, so the aggregate-list guard failed Fixed The code. sd_ssp.py:10 reads defined_for = StateCode.SD. CI. In run 38818844797, job 116711585427 ("Rest (Python, shard-1 + variables)") reports test_members_are_state_gated[gov/household/household_state_benefits.yaml] PASSED. Both Rest jobs pass, including the one that failed in round 1 (1h6m32s, conclusion success). No change in behavior. Both components were already SD-gated.
Critical 2: joint-entry couples' income wasn't shared equally in the entry month Fixed The rule. 67:46:04:10(6): "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". 67:46:06:03 applies 04:08 to 04:10 to the payment amount. The code (sd_ssp_care.py): joint entry needs both spouses flagged (lines 25–28). Then shared_income = marital_unit.sum(own_income) / nb_persons() replaces own income (lines 76–79) before each spouse's deductions (line 80). The 04:15 test uses the shared figure too, so 04:10's lead-in "For purposes of determining eligibility" holds. Tests. Case 7 is [1,200, 1,200]. Case 9 is my round-1 probe ($2,500 and $500 a month): [700, 700], against the old [0, 1,700]. Case 12 pins the order (a $100 deduction for one spouse: [800, 700]). Case 13 pins a one-sided flag. Case 14 pins the entry month only. Documentation. sd_ssp_care_joint_entry now says the flag is for the entry month only and drives both the 05:30 pooling and the 04:10(6) sharing.
Should 1: axiom: needed isn't valid for a staff PR that adds a program Fixed The line now reads 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. #1784 is open and labelled pe-parity; Ziming filed it on 2026-10-15. It has the four module paths I proposed, with corpus status. It quotes SDCL 28-5A-1, ARSD 67:12:14:01(2) to :04, the 67:46 rules and POMS B.5.b, plus the fee schedule row and note 2. It has seven required outputs and companion tests A–I, mapped to PE cases. A source-conflicts comment covers four conflicts.
Should 2: all seven parameter descriptions were off template Fixed (the care files' program names accepted) The independent-payment files follow my wording, "... under the Optional State Supplemental program", which is ARSD 67:12:14's name. The daily rate reads "... under the State Optional Supplemental Assisted Living program", the fee schedule's name. The two resource limits read "... under the long-term care assistance program". That fits: 67:46 never says "optional", and 67:46:05:30 sets the limit "for long-term care assistance". All seven now have the "under the ... program" clause.
Suggestion 1: the daily rate before and after 2026 Forward half fixed; history declined (accepted) Going forward. assisted_living_daily_rate.yaml now uprates with gov.ssa.uprating, rounded to the cent, and cites note 2's annual COLA adjustment. My probe gives $79.32 for 2027 at the head (77.16 × 326.149 / 317.265). History. The body says "no historical coverage is claimed", the registry claims only "2026", and #1784 records the $75.05 rate from 01/01/2025. That makes it a documented deferral.
Suggestion 2: the Texas rebate's copy of spm_unit_benefits omits sd_ssp Deferred to #10226 (accepted) #10226 is open. tx_rebate.py on main still goes straight from "fl_oss" to "ks_sspp" (lines 65–66), as in the sibling PRs.
Suggestion 3: the month only one spouse enters care isn't modeled Fixed (documented) Where it is recorded: a NOTE at sd_ssp_care.py:72-75 citing 04:10(7) and 05:42(7), the sd_ssp_care_joint_entry documentation ("A month in which only one spouse enters care is not covered by this flag"), the body's "Coverage limits" and #1784. My fix allowed either the registry notes or the documentation.
Suggestion 4(a): couple pooling of other income Fixed A NOTE at sd_ssp_independent.py:51-54 quotes B.5.b and 67:12:14:03(2). Conflict 1 in #1784 records both readings.
Suggestion 4(b): the care payment replaced SSI when SSI wasn't taken up Fixed (gated) The code. pursues_ssi = takes_up_ssi_if_eligible | (uncapped_ssi <= 0) (lines 38–40). The rules. 67:46:04:13(2) says the department "must refer an individual to the Social Security Administration to apply for supplemental security income if the individual's income, minus twenty dollars, is less than ... nine hundred ninety-four dollars". 67:46:01:14: an individual "must take advantage of all income and resources to which the individual is entitled, including items such as social security, SSI, ... Failure or refusal ... makes the individual ineligible". Tests. Case 32 (no income, no take-up) gives 0. Case 23 (over income for SSI) gives 700. No period mismatch. takes_up_ssi_if_eligible is a YEAR input read with period.this_year, and uncapped_ssi is MONTH.
Suggestion 4(c): waiver and private-pay care residents got $15 Fixed The input. sd_ssp_care_setting (ASSISTED_LIVING / ADULT_FOSTER_CARE / NONE) replaces sd_ssp_is_adult_foster_care. The exclusion. in_care = a setting, certification, or ssi_lives_in_medical_treatment_facility (lines 30–35). 67:12:14:02(4): "Institutionalized individuals or couples living in adult foster care or assisted living arrangements are not eligible." Tests. Cases 33, 34 and 36 each give 0, with SSI 994.
Suggestion 5: aggregate assertion and boundary cases Fixed in part; the rest declined (accepted) Added. Integration Case 2 asserts household_state_benefits: 180. Declined. There is no spm_unit_benefits assertion; the reason, that it would tie the case to projected FY2027 SNAP values, holds as it did for Rhode Island. The at-limit cases were optional in round 1.
Suggestion 6: polish Mostly fixed; the rest declined (accepted) References. The amount files cite B.5.b, 67:12:14:04 and SSA's 2011 page. sd_ssp.py adds SDCL 28-5A-1, and sd_ssp_care.py cites the eight rules it applies. Declined. The blank lines before metadata: stay (harmless). period: month stays on the resource limits, and the reason holds: 67:46:05:43 says "A recipient's resource eligibility is redetermined as of 12:01 a.m. on the first day of each month." The case count was updated, but b416b58 then added two cases (Suggestion 3).

New findings

Critical (must fix)

None.

Should address

None.

Suggestions

1. Say in sd_ssp_care_income_deductions that pre-tax payroll health premiums are already excluded.

  • Where: sd_ssp_care_income_deductions.py:11, which reads "Verified statutory exclusions, eligible insurance premiums and dependent allocations."
  • Why it matters.
    • The care payment counts ssi_earned_income, which since Document disjoint health premium inputs and correct consumer treatment #10046 subtracts pre_tax_health_insurance_premiums from wages.
    • 67:46:06:06 allows "a deduction from the income for the amount paid" when income pays health premiums.
    • So the netting already gives the premium deduction (see Verified). A user who follows the documentation and enters the same premium again deducts it twice.
  • Probe at the head (2026-01). A disabled worker in certified assisted living: Case 27 plus $600 a year of pre-tax premiums.
    • Without the deduction input, the payment is 1,038.50, which is correct.
    • With $50 entered as a deduction, it is 1,088.50, which is $50 a month too high.
  • A joint-entry detail. In a joint-entry month, the netting also splits the premium between the spouses, whereas 06:06 deducts it from the spouse who pays. The couple's total is the same unless a floor or the 04:15 test binds.
  • Fix: add "Leave out health premiums paid through pre-tax payroll salary reduction, which ssi_earned_income already excludes."

2. Bring #1784 in line with the head.

  • The case counts. The summary says sd_ssp.yaml has 34 cases and integration.yaml 11. The head has 37 and 14.
  • The carry-over change. MO minimum taxable income is inflation indexed #1784 predates b416b58e8e. The "Joint entry is the entry month only" interpretation cites Cases 6, 8 and 13. Add Integration Case 14, which pins [1,200, 1,200] in January and [0, 1,360.48] in February, and sd_ssp.yaml Case 37.
  • The 04:15 condition. The "Care eligible" judgment and the "Care payment" formula leave out the net-income test. It appears only under interpretations (Case 35). Add "AND net income (shared in a joint-entry month, after deductions) ≤ the monthly care rate" to the judgment.
  • The earned-income reading.
    • The issue defines income as "gross earned and unearned income plus SSI (04:02(2))". PE counts wages net of salary-reduction premiums.
    • Record that this matches gross income less the 06:06 premium deduction, so an encoder using gross wages applies 06:06 to those premiums.

3. Polish.

  • The PR body's case count. The Test plan says "49 South Dakota SSP YAML cases (36 in sd_ssp.yaml, 13 in integration.yaml)". The head has 51 (37 + 14), and CI ran 37 and 14.
  • joint_entry_resource_limit.yaml (optional). The description limits "the resources of an individual whose spouse begins long-term care services in the same month". The formula tests the couple's pooled resources (Integration Case 5), so "a couple's combined resources ... when both spouses begin long-term care in the same month" would match the code.

Verified

  • Administration (POMS split). POMS SI DEN01415.011 (effective 12/12/2001 to present):
    • A: "SSA administers the State's mandatory supplementary payments." The PR doesn't model these; the body lists "historical mandatory supplements" under coverage limits.
    • B: "The State administers its optional supplementary payments - South Dakota Senate Bill 201 ... and South Dakota Senate Bill 32".
    • B.1: "The Department of Social Services administers both State optional Supplementation programs."
    • So the wage lesson applies to both modeled payments, and no SSA rounding or $1 minimum applies.
  • The wage count for the state-administered parts (NH Add New Hampshire state supplementation for 2026 #10156 r3, OK Add Oklahoma state supplementation for 2026 #10165, OR Add Oregon state supplementation for 2026 #10166 r2).
    • The independent payment sums the raw SSI source lists, floored at 0 (sd_ssp_independent.py:15-20), so wages count gross. Any amount disqualifies anyway (67:12:14:01(2)).

    • The care payment's rule.

      • 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 for SB 201: "There are no income or resource exclusions."
      • 67:46:04:03 excludes no payroll amount. Its cross-reference for earned income is 20 CFR 416.1110.
      • 67:46:06:06: "If an applicant's or recipient's income is used to pay the applicant's or recipient's health insurance premiums, the department allows a deduction from the income for the amount paid."
      • 67:46:04:15 measures net income "after application of the budgeting regulations contained in §§ 67:46:06:03 to 67:46:06:07", so the premium deduction counts there too.
    • The code. own_income adds max_(ssi_earned_income, 0), ssi_unearned_income and ssi (lines 63–67). ssi_earned_income subtracts min_(wages, pre_tax_health_insurance_premiums).

    • Why the result is right. Two readings of the rule both give the model's figure:

      • Read 04:02(2) literally: gross wages, less the 06:06 deduction for the premium those wages paid.
      • Follow the 416.1110 cross-reference with POMS SI 00820.102 C.2: "Amounts used to purchase qualified benefits under a cafeteria-plan with a salary-reduction agreement are not the employee's wages and are not considered income for SSI purposes."

      This differs from New Hampshire (Add New Hampshire state supplementation for 2026 #10156), where He-W 601.04(m) counts wages "prior to payroll deductions" and the netting changed the grant.

    • Probe at the head (2026-01). A disabled worker with $3,600 a year of wages, $600 a year of pre-tax premiums, certification and a $2,200 charge:

      • ssi_earned_income is 3,000 a year (250 a month), so SSI is 994 − (250 − 20 − 65) / 2 = 911.50;
      • sd_ssp is 2,200 − (250 + 911.50) = 1,038.50;
      • under the rule, (300 + 911.50 − 50) gives the same 1,161.50 contribution, so the payment is the same 1,038.50;
      • with the $50 also entered as a deduction, the payment is 1,088.50 (Suggestion 1);
      • without premiums (Case 27), it is 1,013.50.
  • State-gated keys (NH Add New Hampshire state supplementation for 2026 #10156 check).
    • No gated variable is a parameter key. sd_ssp_care_setting (defined_for = StateCode.SD, default NONE) is compared with possible_values only. The South Dakota parameters are all scalars.
    • The other gated inputs are arithmetic or boolean: sd_ssp_care_partial_month_days and sd_ssp_care_died_or_transferred.
    • A two-state probe ran without error. One simulation held the three South Dakota workers above, a South Dakota aged SSI-only resident at home, and the same person in Massachusetts:
      • sd_ssp_care_setting was NONE for all five;
      • sd_ssp (2026-01) was [1,038.50, 1,088.50, 1,013.50, 15, 0];
      • household_state_benefits (2026) was [12,422.48, 13,022.48, 12,122.48, 180, 1,545.84];
      • SNAP unearned income was [23,360.48, 23,960.48, 22,760.48, 12,108, 13,473.84];
      • HUD unearned income was [23,360.48, 23,960.48, 22,760.48, 12,108, 11,928].
    • The persistent inputs carry forward. The worker's certification and charge were entered for January only. 12,422.48 is 11 × 1,038.50 plus February's 2,160.48 − 1,161.50 = 998.98. That is how a formula-free input should behave, and certification and charges do persist.
  • The three new fixes.
    • The input-reset trap. The old sd_ssp_care_days had a formula but was set in tests. In a multi-person YAML, a value for one person gives the others the default, not the formula, so they got 0 days. Both replacements default to 0 (a full month) or false, so the default matches the formula. Integration Cases 10 and 11 pin two residents with an input on one.
    • Carry-over. A formula-free MONTH input carries forward to later months. The one-month inputs now have formulas that return their defaults (b416b58e8e), so a January entry stays in January. Case 37 asserts sd_ssp_care_partial_month_days of [10, 0] for January and February. Integration Case 14 pins the joint-entry flag.
    • 67:46:04:15 in partial months.
      • 04:15's net income runs through 06:03–06:07, and 06:09's partial-month rule is outside that range. 67:46:06:01 adds that chapter 06 applies "after financial eligibility is established according to chapter 67:46:04".
      • So net_income <= monthly_rate before the 06:09 contribution is the right order.
      • Scaling the covered-days charge to the month fits sd_ssp_approved_care_cost's definition, "Actual approved charge for covered days".
      • days is at least 1 whenever it differs from the month length, so the scaling can't divide by zero.
  • The daily rate going forward.
    • Note 2 of the January 2026 fee schedule: "The State Optional Supplemental Assisted Living service rate is adjusted annually in January based on the Social Security Administration (SSA) Cost of Living Adjustment (COLA)."
    • gov.ssa.uprating is the January-dated CPI-W index behind the COLA, so the projection steps each January.
    • After a rebase onto main, Update Social Security and SSI parameters for the 2027 COLA #10225's 2027 index (328.746) gives 77.16 × 328.746 / 317.265 = $79.95, within a cent of 77.16 × 1.036. The published schedule replaces the projection.
  • What the main merge changes for South Dakota.
  • New and changed expectations, checked by hand. The 2026 FBRs are $994 and $1,491. The cap is 31 × 77.16 = 2,391.96 in January and 28 × 77.16 = 2,160.48 in February.
    • sd_ssp.yaml:
      • Case 16: min(1,500, 10 × 77.16) = 771.60, with no income applied. The 04:15 check: 994 ≤ 771.60 × 31 / 10 = 2,391.96.
      • Case 17: adult foster care is uncapped: 1,500 − 994 = 506.
      • Case 22: min(1,800, 1,543.20) − 994 = 549.20. The 04:15 check: 994 ≤ 1,543.20 × 31 / 20 = 2,391.96.
      • Case 23: 1,500 − 20 = 1,480 > 994, so uncapped SSI is below 0 and no take-up is needed. 2,200 − 1,500 = 700.
      • Case 25: 31 days in February is a full month: 2,160.48 − 994 = 1,166.48.
      • Case 26 (new expectation): foster care, $1,500 for 10 covered days, with no cap and no income applied, gives 1,500. The 04:15 check: 994 ≤ 1,500 × 31 / 10 = 4,650.
      • Case 32: uncapped SSI is 994 > 0 and there's no take-up, so SSI is 0 and sd_ssp 0.
      • Cases 33 and 34: SSI 994; a setting excludes the $15, and an uncertified resident gets no care payment, so 0.
      • Case 35: SSI is 0 (2,480 > 994). The cost is min(750, 771.60) = 750, and the monthly rate 750 × 31 / 10 = 2,325. 2,500 > 2,325, so 0.
      • Case 36: a private-pay medical facility gives SSI 994 (living arrangement A), and in_care excludes the $15, so 0.
      • Case 37: January 771.60. February's days are 0 by formula, so 2,160.48 − 994 = 1,166.48.
    • integration.yaml:
      • Case 2: household_state_benefits 12 × 15 = 180.
      • Case 7: pooled resources 2,900 ≤ 3,000; (1,200 + 800) / 2 = 1,000; 2,200 − 1,000 = 1,200 each.
      • Case 9: SSI is 0 (3,000 − 20 > 1,491); (2,500 + 500) / 2 = 1,500; 700 each.
      • Case 10: [771.60, 2,200 − 994 = 1,206].
      • Case 11: [1,543.20 − 994 = 549.20, 771.60].
      • Case 12: 2,200 − (1,500 − 100) = 800, and 2,200 − 1,500 = 700.
      • Case 13: one flag, so own income. Person 1's 2,500 > 2,200 gives 0; person 2 gets 2,200 − 500 = 1,700.
      • Case 14: January [1,200, 1,200]. In February the flag is false by formula: person 1's 2,100 > 2,000 gives 0, and person 2 gets 2,160.48 − 800 = 1,360.48.
  • Wiring at the head:
    • sd_ssp is in all four household_state_benefits blocks, in spm_unit_benefits.py, and on the SNAP and HUD unearned lists;
    • programs.yaml is partial, with verified_years: "2026";
    • sd_ssp is not an SSI income source.
  • Changelog. changelog.d/sd-ssp-2026.added.md is unchanged, top-level and typed added.
  • Tests.
    • In CI: Quick Feedback ran both South Dakota files at the head: 14 and 37 passed.
    • Locally: one small probe, covering the wage path, the two-state check and the 2027 rate.
  • Partner tests. The PR touches none, and Household API Partners passes.

Axiom line assessment

Valid: 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.

  • Issue state.
    • MO minimum taxable income is inflation indexed #1784 is open and labelled pe-parity. It is the only rulespec-us issue for South Dakota's supplements.
    • rulespec-us main (5796861e58, 2026-10-20) has no module for them: us-sd/ holds only policies/ (cms/, income_tax/). So queued is still the right form.
  • Dispatch-ready. It has:
    • module paths with corpus status: ARSD 67:12:14, the 67:46 chapters and POMS DEN01415.011 are in the corpus, while the fee schedule and SDCL 28-5A-1 need ingest;
    • the verbatim law;
    • seven required outputs;
    • companion tests A–I from B.5.b, 67:12:14, 06:09, 06:10, 05:30, 04:10(6) and the fee schedule, mapped to PE cases;
    • the interpretations that change a result;
    • a source-conflicts comment covering four conflicts: couple pooling, the $3,000 limit after the entry month, the pre-2014 personal needs allowance, and SSI receipt against the statute's "would be eligible".
  • Matches the head, except for the items in Suggestion 2.

CI status

27 pass, 0 pending, 0 failed (run 38818844797 on head b416b58e8e, pull_request, 2026-10-15 22:41 UTC to 2026-10-16 00:44 UTC).

  • Passed:
    • PR validation, and Package and compatibility;
    • Quick Feedback;
    • Household API Partners;
    • Microsimulation and both Rest (Python) jobs. Round 1's failing Rest job now passes, and shard-1 shows the list guard passing.
    • all 11 Baseline shards and all 9 Contrib shards.

Branch status

Verdict: APPROVE

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving round 2; details in the report above. Both round-1 critical items are fixed: sd_ssp is now SD-gated, so the state-gating test passes, and joint-entry income is shared equally in the entry month per 04:10(6) via 06:03 (Cases 7, 9 and 12–14). Both should items are fixed: rulespec-us#1784 is queued, and the descriptions follow the template. Your extra fixes (the input reset, one-month carry-over, and 04:15 in partial months) check out. The NH-sibling bug doesn't occur here. There's no wage-count gap, because 06:06 deducts health premiums, so the result is the same either way. CI is 27/27 green. Branch note: conflicts with main in three shared lists; keep both lines. Three optional suggestions are in the report (care-deduction docs, #1784 counts, PR body count).

@hua7450
hua7450 marked this pull request as draft October 20, 2026 17:16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review October 20, 2026 18:13
@hua7450
hua7450 merged commit 2f9c830 into main Oct 20, 2026
7 checks passed
@hua7450
hua7450 deleted the sd-ssp-2026 branch October 20, 2026 18:17
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